1 #include "scoreboard.qh"
3 #include <client/draw.qh>
4 #include <client/hud/panel/chat.qh>
5 #include <client/hud/panel/quickmenu.qh>
6 #include <client/hud/panel/racetimer.qh>
7 #include <client/hud/panel/weapons.qh>
8 #include <common/constants.qh>
9 #include <common/ent_cs.qh>
10 #include <common/mapinfo.qh>
11 #include <common/minigames/cl_minigames.qh>
12 #include <common/net_linked.qh>
13 #include <common/scores.qh>
14 #include <common/stats.qh>
15 #include <common/teams.qh>
16 #include <common/items/inventory.qh>
20 void Scoreboard_Draw_Export(int fh)
22 // allow saving cvars that aesthetically change the panel into hud skin files
23 HUD_Write_Cvar("hud_panel_scoreboard_fadeinspeed");
24 HUD_Write_Cvar("hud_panel_scoreboard_fadeoutspeed");
25 HUD_Write_Cvar("hud_panel_scoreboard_respawntime_decimals");
26 HUD_Write_Cvar("hud_panel_scoreboard_table_bg_alpha");
27 HUD_Write_Cvar("hud_panel_scoreboard_table_bg_scale");
28 HUD_Write_Cvar("hud_panel_scoreboard_table_fg_alpha");
29 HUD_Write_Cvar("hud_panel_scoreboard_table_fg_alpha_self");
30 HUD_Write_Cvar("hud_panel_scoreboard_table_highlight");
31 HUD_Write_Cvar("hud_panel_scoreboard_table_highlight_alpha");
32 HUD_Write_Cvar("hud_panel_scoreboard_table_highlight_alpha_self");
33 HUD_Write_Cvar("hud_panel_scoreboard_table_highlight_alpha_eliminated");
34 HUD_Write_Cvar("hud_panel_scoreboard_bg_teams_color_team");
35 HUD_Write_Cvar("hud_panel_scoreboard_accuracy_doublerows");
36 HUD_Write_Cvar("hud_panel_scoreboard_accuracy_nocolors");
39 const int MAX_SBT_FIELDS = MAX_SCORE;
41 PlayerScoreField sbt_field[MAX_SBT_FIELDS + 1];
42 float sbt_field_size[MAX_SBT_FIELDS + 1];
43 string sbt_field_title[MAX_SBT_FIELDS + 1];
46 string autocvar_hud_fontsize;
47 string hud_fontsize_str;
50 vector duel_score_fontsize;
51 vector duel_name_fontsize;
52 vector duel_score_size;
53 vector team_score_fontsize;
54 vector team_name_fontsize;
55 vector team_score_size;
60 float sbt_fg_alpha_self;
62 float sbt_highlight_alpha;
63 float sbt_highlight_alpha_self;
64 float sbt_highlight_alpha_eliminated;
66 // provide basic panel cvars to old clients
67 // TODO remove them after a future release (0.8.2+)
68 noref string autocvar_hud_panel_scoreboard_pos = "0.150000 0.150000";
69 noref string autocvar_hud_panel_scoreboard_size = "0.700000 0.700000";
70 noref string autocvar_hud_panel_scoreboard_bg = "border_default";
71 noref string autocvar_hud_panel_scoreboard_bg_color = "0 0.3 0.5";
72 noref string autocvar_hud_panel_scoreboard_bg_color_team = "";
73 noref string autocvar_hud_panel_scoreboard_bg_alpha = "0.7";
74 noref string autocvar_hud_panel_scoreboard_bg_border = "";
75 noref string autocvar_hud_panel_scoreboard_bg_padding = "";
77 float autocvar_hud_panel_scoreboard_fadeinspeed = 10;
78 float autocvar_hud_panel_scoreboard_fadeoutspeed = 5;
79 float autocvar_hud_panel_scoreboard_respawntime_decimals = 1;
80 float autocvar_hud_panel_scoreboard_table_bg_alpha = 0;
81 float autocvar_hud_panel_scoreboard_table_bg_scale = 0.25;
82 float autocvar_hud_panel_scoreboard_table_fg_alpha = 0.9;
83 float autocvar_hud_panel_scoreboard_table_fg_alpha_self = 1;
84 bool autocvar_hud_panel_scoreboard_table_highlight = true;
85 float autocvar_hud_panel_scoreboard_table_highlight_alpha = 0.2;
86 float autocvar_hud_panel_scoreboard_table_highlight_alpha_self = 0.4;
87 float autocvar_hud_panel_scoreboard_table_highlight_alpha_eliminated = 0.6;
88 float autocvar_hud_panel_scoreboard_bg_teams_color_team = 0;
89 float autocvar_hud_panel_scoreboard_namesize = 15;
90 float autocvar_hud_panel_scoreboard_team_size_position = 0;
92 bool autocvar_hud_panel_scoreboard_accuracy = true;
93 bool autocvar_hud_panel_scoreboard_accuracy_doublerows = false;
94 bool autocvar_hud_panel_scoreboard_accuracy_nocolors = false;
95 float autocvar_hud_panel_scoreboard_accuracy_showdelay = 2;
96 float autocvar_hud_panel_scoreboard_accuracy_showdelay_minpos = 0.75;
98 bool autocvar_hud_panel_scoreboard_itemstats = true;
99 bool autocvar_hud_panel_scoreboard_itemstats_doublerows = false;
100 bool autocvar_hud_panel_scoreboard_itemstats_filter = true;
101 float autocvar_hud_panel_scoreboard_itemstats_showdelay = 2.2; // slightly more delayed than accuracy
102 float autocvar_hud_panel_scoreboard_itemstats_showdelay_minpos = 0.75;
104 bool autocvar_hud_panel_scoreboard_dynamichud = false;
106 float autocvar_hud_panel_scoreboard_maxheight = 0.6;
107 bool autocvar_hud_panel_scoreboard_others_showscore = true;
108 bool autocvar_hud_panel_scoreboard_spectators_showping = true;
109 bool autocvar_hud_panel_scoreboard_spectators_aligned = false;
110 float autocvar_hud_panel_scoreboard_minwidth = 0.4;
111 bool autocvar_hud_panel_scoreboard_playerid = false;
112 string autocvar_hud_panel_scoreboard_playerid_prefix = "#";
113 string autocvar_hud_panel_scoreboard_playerid_suffix = " ";
115 int average_ping[NUM_TEAMS];
117 // mode 0: returns translated label
118 // mode 1: prints name and description of all the labels
119 string Label_getInfo(string label, int mode)
122 label = "bckills"; // first case in the switch
126 case "bckills": if (!mode) return CTX(_("SCO^bckills")); else LOG_HELP(strcat("^3", "bckills", " ^7", _("Number of ball carrier kills")));
127 case "bctime": if (!mode) return CTX(_("SCO^bctime")); else LOG_HELP(strcat("^3", "bctime", " ^7", _("Total amount of time holding the ball in Keepaway")));
128 case "caps": if (!mode) return CTX(_("SCO^caps")); else LOG_HELP(strcat("^3", "caps", " ^7", _("How often a flag (CTF) or a key (KeyHunt) was captured")));
129 case "captime": if (!mode) return CTX(_("SCO^captime")); else LOG_HELP(strcat("^3", "captime", " ^7", _("Time of fastest capture (CTF)")));
130 case "cn": if (!mode) return CTX(_("SCO^cn")); else LOG_HELP(strcat("^3", "cn", " ^7", _("Player country"))); //LegendGuard adds cn for Country column 05-04-2021
131 case "deaths": if (!mode) return CTX(_("SCO^deaths")); else LOG_HELP(strcat("^3", "deaths", " ^7", _("Number of deaths")));
132 case "destroyed": if (!mode) return CTX(_("SCO^destroyed")); else LOG_HELP(strcat("^3", "destroyed", " ^7", _("Number of keys destroyed by pushing them into void")));
133 case "dmg": if (!mode) return CTX(_("SCO^damage")); else LOG_HELP(strcat("^3", "dmg", " ^7", _("The total damage done")));
134 case "dmgtaken": if (!mode) return CTX(_("SCO^dmgtaken")); else LOG_HELP(strcat("^3", "dmgtaken", " ^7", _("The total damage taken")));
135 case "drops": if (!mode) return CTX(_("SCO^drops")); else LOG_HELP(strcat("^3", "drops", " ^7", _("Number of flag drops")));
136 case "elo": if (!mode) return CTX(_("SCO^elo")); else LOG_HELP(strcat("^3", "elo", " ^7", _("Player ELO")));
137 case "fastest": if (!mode) return CTX(_("SCO^fastest")); else LOG_HELP(strcat("^3", "fastest", " ^7", _("Time of fastest lap (Race/CTS)")));
138 case "faults": if (!mode) return CTX(_("SCO^faults")); else LOG_HELP(strcat("^3", "faults", " ^7", _("Number of faults committed")));
139 case "fckills": if (!mode) return CTX(_("SCO^fckills")); else LOG_HELP(strcat("^3", "fckills", " ^7", _("Number of flag carrier kills")));
140 case "fps": if (!mode) return CTX(_("SCO^fps")); else LOG_HELP(strcat("^3", "fps", " ^7", _("FPS")));
141 case "frags": if (!mode) return CTX(_("SCO^frags")); else LOG_HELP(strcat("^3", "frags", " ^7", _("Number of kills minus suicides")));
142 case "goals": if (!mode) return CTX(_("SCO^goals")); else LOG_HELP(strcat("^3", "goals", " ^7", _("Number of goals scored")));
143 case "kckills": if (!mode) return CTX(_("SCO^kckills")); else LOG_HELP(strcat("^3", "kckills", " ^7", _("Number of keys carrier kills")));
144 case "kd": if (!mode) return CTX(_("SCO^k/d")); else LOG_HELP(strcat("^3", "kd", " ^7", _("The kill-death ratio")));
145 case "kdr": if (!mode) return CTX(_("SCO^kdr")); else LOG_HELP(strcat("^3", "kdr", " ^7", _("The kill-death ratio")));
146 case "kdratio": if (!mode) return CTX(_("SCO^kdratio")); else LOG_HELP(strcat("^3", "kdratio", " ^7", _("The kill-death ratio")));
147 case "kills": if (!mode) return CTX(_("SCO^kills")); else LOG_HELP(strcat("^3", "kills", " ^7", _("Number of kills")));
148 case "laps": if (!mode) return CTX(_("SCO^laps")); else LOG_HELP(strcat("^3", "laps", " ^7", _("Number of laps finished (Race/CTS)")));
149 case "lives": if (!mode) return CTX(_("SCO^lives")); else LOG_HELP(strcat("^3", "lives", " ^7", _("Number of lives (LMS)")));
150 case "losses": if (!mode) return CTX(_("SCO^losses")); else LOG_HELP(strcat("^3", "losses", " ^7", _("Number of times a key was lost")));
151 case "name": if (!mode) return CTX(_("SCO^name")); else LOG_HELP(strcat("^3", "name", " ^7", _("Player name")));
152 case "nick": if (!mode) return CTX(_("SCO^nick")); else LOG_HELP(strcat("^3", "nick", " ^7", _("Player name")));
153 case "objectives": if (!mode) return CTX(_("SCO^objectives")); else LOG_HELP(strcat("^3", "objectives", " ^7", _("Number of objectives destroyed")));
154 case "pickups": if (!mode) return CTX(_("SCO^pickups")); else LOG_HELP(strcat("^3", "pickups", " ^7", _("How often a flag (CTF) or a key (KeyHunt) or a ball (Keepaway) was picked up")));
155 case "ping": if (!mode) return CTX(_("SCO^ping")); else LOG_HELP(strcat("^3", "ping", " ^7", _("Ping time")));
156 case "pl": if (!mode) return CTX(_("SCO^pl")); else LOG_HELP(strcat("^3", "pl", " ^7", _("Packet loss")));
157 case "pushes": if (!mode) return CTX(_("SCO^pushes")); else LOG_HELP(strcat("^3", "pushes", " ^7", _("Number of players pushed into void")));
158 case "rank": if (!mode) return CTX(_("SCO^rank")); else LOG_HELP(strcat("^3", "rank", " ^7", _("Player rank")));
159 case "returns": if (!mode) return CTX(_("SCO^returns")); else LOG_HELP(strcat("^3", "returns", " ^7", _("Number of flag returns")));
160 case "revivals": if (!mode) return CTX(_("SCO^revivals")); else LOG_HELP(strcat("^3", "revivals", " ^7", _("Number of revivals")));
161 case "rounds": if (!mode) return CTX(_("SCO^rounds won")); else LOG_HELP(strcat("^3", "rounds", " ^7", _("Number of rounds won")));
162 case "score": if (!mode) return CTX(_("SCO^score")); else LOG_HELP(strcat("^3", "score", " ^7", _("Total score")));
163 case "suicides": if (!mode) return CTX(_("SCO^suicides")); else LOG_HELP(strcat("^3", "suicides", " ^7", _("Number of suicides")));
164 case "sum": if (!mode) return CTX(_("SCO^sum")); else LOG_HELP(strcat("^3", "sum", " ^7", _("Number of kills minus deaths")));
165 case "takes": if (!mode) return CTX(_("SCO^takes")); else LOG_HELP(strcat("^3", "takes", " ^7", _("Number of domination points taken (Domination)")));
166 case "teamkills": if (!mode) return CTX(_("SCO^teamkills")); else LOG_HELP(strcat("^3", "teamkills", " ^7", _("Number of teamkills")));
167 case "ticks": if (!mode) return CTX(_("SCO^ticks")); else LOG_HELP(strcat("^3", "ticks", " ^7", _("Number of ticks (Domination)")));
168 case "time": if (!mode) return CTX(_("SCO^time")); else LOG_HELP(strcat("^3", "time", " ^7", _("Total time raced (Race/CTS)")));
169 default: return label;
174 void PrintScoresLabels() { Label_getInfo(string_null, 1); }
175 string TranslateScoresLabel(string label) { return Label_getInfo(label, 0); }
177 void Scoreboard_InitScores()
181 ps_primary = ps_secondary = NULL;
182 ts_primary = ts_secondary = -1;
183 FOREACH(Scores, true, {
184 f = (scores_flags(it) & SFL_SORT_PRIO_MASK);
185 if(f == SFL_SORT_PRIO_PRIMARY)
187 if(f == SFL_SORT_PRIO_SECONDARY)
190 if(ps_secondary == NULL)
191 ps_secondary = ps_primary;
193 for(i = 0; i < MAX_TEAMSCORE; ++i)
195 f = (teamscores_flags(i) & SFL_SORT_PRIO_MASK);
196 if(f == SFL_SORT_PRIO_PRIMARY)
198 if(f == SFL_SORT_PRIO_SECONDARY)
201 if(ts_secondary == -1)
202 ts_secondary = ts_primary;
204 Cmd_Scoreboard_SetFields(0);
208 void Scoreboard_UpdatePlayerTeams()
212 for(pl = players.sort_next; pl; pl = pl.sort_next)
215 int Team = entcs_GetScoreTeam(pl.sv_entnum);
216 if(SetTeam(pl, Team))
219 Scoreboard_UpdatePlayerPos(pl);
223 pl = players.sort_next;
228 print(strcat("PNUM: ", ftos(num), "\n"));
233 int Scoreboard_CompareScore(int vl, int vr, int f)
235 TC(int, vl); TC(int, vr); TC(int, f);
236 if(f & SFL_ZERO_IS_WORST)
238 if(vl == 0 && vr != 0)
240 if(vl != 0 && vr == 0)
244 return IS_INCREASING(f);
246 return IS_DECREASING(f);
250 float Scoreboard_ComparePlayerScores(entity left, entity right)
253 vl = entcs_GetTeam(left.sv_entnum);
254 vr = entcs_GetTeam(right.sv_entnum);
266 if(vl == NUM_SPECTATOR)
268 // FIRST the one with scores (spectators), THEN the ones without (downloaders)
270 if(!left.gotscores && right.gotscores)
275 r = Scoreboard_CompareScore(left.scores(ps_primary), right.scores(ps_primary), scores_flags(ps_primary));
279 r = Scoreboard_CompareScore(left.scores(ps_secondary), right.scores(ps_secondary), scores_flags(ps_secondary));
283 FOREACH(Scores, true, {
284 r = Scoreboard_CompareScore(left.scores(it), right.scores(it), scores_flags(it));
285 if (r >= 0) return r;
288 if (left.sv_entnum < right.sv_entnum)
294 void Scoreboard_UpdatePlayerPos(entity player)
297 for(ent = player.sort_next; ent && Scoreboard_ComparePlayerScores(player, ent); ent = player.sort_next)
299 SORT_SWAP(player, ent);
301 for(ent = player.sort_prev; ent != players && Scoreboard_ComparePlayerScores(ent, player); ent = player.sort_prev)
303 SORT_SWAP(ent, player);
307 float Scoreboard_CompareTeamScores(entity left, entity right)
311 if(left.team == NUM_SPECTATOR)
313 if(right.team == NUM_SPECTATOR)
316 r = Scoreboard_CompareScore(left.teamscores(ts_primary), right.teamscores(ts_primary), teamscores_flags(ts_primary));
320 r = Scoreboard_CompareScore(left.teamscores(ts_secondary), right.teamscores(ts_secondary), teamscores_flags(ts_secondary));
324 for(i = 0; i < MAX_TEAMSCORE; ++i)
326 r = Scoreboard_CompareScore(left.teamscores(i), right.teamscores(i), teamscores_flags(i));
331 if (left.team < right.team)
337 void Scoreboard_UpdateTeamPos(entity Team)
340 for(ent = Team.sort_next; ent && Scoreboard_CompareTeamScores(Team, ent); ent = Team.sort_next)
342 SORT_SWAP(Team, ent);
344 for(ent = Team.sort_prev; ent != teams && Scoreboard_CompareTeamScores(ent, Team); ent = Team.sort_prev)
346 SORT_SWAP(ent, Team);
350 void Cmd_Scoreboard_Help()
352 LOG_HELP(_("You can modify the scoreboard using the ^2scoreboard_columns_set command."));
353 LOG_HELP(_("Usage:"));
354 LOG_HELP("^2scoreboard_columns_set ^3default");
355 LOG_HELP(_("^2scoreboard_columns_set ^3field1 field2 ..."));
356 LOG_HELP(_("^2scoreboard_columns_set ^7without arguments reads the arguments from the cvar scoreboard_columns"));
357 LOG_HELP(_(" ^5Note: ^7scoreboard_columns_set without arguments is executed on every map start"));
358 LOG_HELP(_("^2scoreboard_columns_set ^3expand_default ^7loads default layout and expands it into the cvar scoreboard_columns so you can edit it"));
359 LOG_HELP(_("You can use a ^3|^7 to start the right-aligned fields."));
360 LOG_HELP(_("The following field names are recognized (case insensitive):"));
366 LOG_HELP(_("Before a field you can put a + or - sign, then a comma separated list\n"
367 "of game types, then a slash, to make the field show up only in these\n"
368 "or in all but these game types. You can also specify 'all' as a\n"
369 "field to show all fields available for the current game mode."));
372 LOG_HELP(_("The special game type names 'teams' and 'noteams' can be used to\n"
373 "include/exclude ALL teams/noteams game modes."));
376 LOG_HELP(_("Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4"));
377 LOG_HELP(_("will display name, ping and pl aligned to the left, and the fields\n"
378 "right of the vertical bar aligned to the right."));
379 LOG_HELP(_("'field3' will only be shown in CTF, and 'field4' will be shown in all\n"
380 "other gamemodes except DM."));
383 // NOTE: adding a gametype with ? to not warn for an optional field
384 // make sure it's excluded in a previous exclusive rule, if any
385 // otherwise the previous exclusive rule warns anyway
386 // e.g. -teams,rc,cts,lms/kills ?+rc/kills
387 #define SCOREBOARD_DEFAULT_COLUMNS \
388 "ping pl fps cn name |" \
389 " -teams,rc,cts,inv,lms/kills +ft,tdm/kills ?+rc,inv/kills" \
390 " -teams,lms/deaths +ft,tdm/deaths" \
392 " -teams,lms,rc,cts,inv,ka/suicides +ft,tdm/suicides ?+rc,inv/suicides" \
393 " -cts,dm,tdm,ka,ft/frags" /* tdm already has this in "score" */ \
394 " +tdm,ft,dom,ons,as/teamkills"\
395 " -rc,cts,nb/dmg -rc,cts,nb/dmgtaken" \
396 " +ctf/pickups +ctf/fckills +ctf/returns +ctf/caps +ons/takes +ons/caps" \
397 " +lms/lives +lms/rank" \
398 " +kh/kckills +kh/losses +kh/caps" \
399 " ?+rc/laps ?+rc/time +rc,cts/fastest" \
400 " +as/objectives +nb/faults +nb/goals" \
401 " +ka/pickups +ka/bckills +ka/bctime +ft/revivals" \
402 " +dom/ticks +dom/takes" \
403 " -lms,rc,cts,inv,nb/score"
405 void Cmd_Scoreboard_SetFields(int argc)
410 bool have_name = false, have_primary = false, have_secondary = false, have_separator = false;
414 return; // do nothing, we don't know gametype and scores yet
416 // sbt_fields uses strunzone on the titles!
417 if(!sbt_field_title[0])
418 for(i = 0; i < MAX_SBT_FIELDS; ++i)
419 sbt_field_title[i] = strzone("(null)");
421 // TODO: re enable with gametype dependant cvars?
422 if(argc < 3) // no arguments provided
423 argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " ");
426 argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " ");
430 if(argv(2) == "default" || argv(2) == "expand_default")
432 if(argv(2) == "expand_default")
433 cvar_set("scoreboard_columns", SCOREBOARD_DEFAULT_COLUMNS);
434 argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " ");
436 else if(argv(2) == "all")
438 string s = "ping pl cn name |"; // scores without a label
439 FOREACH(Scores, true, {
441 if(it != ps_secondary)
442 if(scores_label(it) != "")
443 s = strcat(s, " ", scores_label(it));
445 if(ps_secondary != ps_primary)
446 s = strcat(s, " ", scores_label(ps_secondary));
447 s = strcat(s, " ", scores_label(ps_primary));
448 argc = tokenizebyseparator(strcat("0 1 ", s), " ");
455 hud_fontsize = HUD_GetFontsize("hud_fontsize");
457 duel_score_fontsize = hud_fontsize * 3;
458 duel_name_fontsize = hud_fontsize * 1.5;
459 duel_score_size = vec2(duel_score_fontsize.x * 1.5, duel_score_fontsize.y * 1.25);
461 team_score_fontsize = hud_fontsize * 2;
462 team_name_fontsize = hud_fontsize * 1.5;
463 team_score_size = vec2(team_score_fontsize.x * 1.5, team_score_fontsize.y * 1.25);
465 for(i = 1; i < argc - 1; ++i)
468 bool nocomplain = false;
469 if(substring(str, 0, 1) == "?")
472 str = substring(str, 1, strlen(str) - 1);
475 slash = strstrofs(str, "/", 0);
478 pattern = substring(str, 0, slash);
479 str = substring(str, slash + 1, strlen(str) - (slash + 1));
481 if (!isGametypeInFilter(gametype, teamplay, false, pattern))
485 strcpy(sbt_field_title[sbt_num_fields], TranslateScoresLabel(str));
486 sbt_field_size[sbt_num_fields] = stringwidth(sbt_field_title[sbt_num_fields], false, hud_fontsize);
487 str = strtolower(str);
492 case "ping": sbt_field[sbt_num_fields] = SP_PING; break;
493 case "pl": sbt_field[sbt_num_fields] = SP_PL; break;
494 case "kd": case "kdr": case "kdratio": sbt_field[sbt_num_fields] = SP_KDRATIO; break;
495 case "sum": case "diff": case "k-d": sbt_field[sbt_num_fields] = SP_SUM; break;
496 case "cn": sbt_field[sbt_num_fields] = SP_COUNTRY; break; //LegendGuard adds cn label for Country column 05-04-2021
497 case "name": case "nick": sbt_field[sbt_num_fields] = SP_NAME; have_name = true; break;
498 case "|": sbt_field[sbt_num_fields] = SP_SEPARATOR; have_separator = true; break;
499 case "elo": sbt_field[sbt_num_fields] = SP_ELO; break;
500 case "dmg": case "damage": sbt_field[sbt_num_fields] = SP_DMG; break;
501 case "dmgtaken": case "damagetaken": sbt_field[sbt_num_fields] = SP_DMGTAKEN; break;
502 case "fps": sbt_field[sbt_num_fields] = SP_FPS; break;
505 FOREACH(Scores, true, {
506 if (str == strtolower(scores_label(it))) {
508 goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
518 LOG_INFOF("^1Error:^7 Unknown score field: '%s'", str);
522 sbt_field[sbt_num_fields] = j;
525 if(j == ps_secondary)
526 have_secondary = true;
531 if(sbt_num_fields >= MAX_SBT_FIELDS)
535 if(scores_flags(ps_primary) & SFL_ALLOW_HIDE)
537 if(scores_flags(ps_secondary) & SFL_ALLOW_HIDE)
538 have_secondary = true;
539 if(ps_primary == ps_secondary)
540 have_secondary = true;
541 missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
543 if(sbt_num_fields + missing < MAX_SBT_FIELDS)
547 strunzone(sbt_field_title[sbt_num_fields]);
548 for(i = sbt_num_fields; i > 0; --i)
550 sbt_field_title[i] = sbt_field_title[i-1];
551 sbt_field_size[i] = sbt_field_size[i-1];
552 sbt_field[i] = sbt_field[i-1];
554 sbt_field_title[0] = strzone(TranslateScoresLabel("name"));
555 sbt_field[0] = SP_NAME;
557 LOG_INFO("fixed missing field 'name'");
561 strunzone(sbt_field_title[sbt_num_fields]);
562 for(i = sbt_num_fields; i > 1; --i)
564 sbt_field_title[i] = sbt_field_title[i-1];
565 sbt_field_size[i] = sbt_field_size[i-1];
566 sbt_field[i] = sbt_field[i-1];
568 sbt_field_title[1] = strzone("|");
569 sbt_field[1] = SP_SEPARATOR;
570 sbt_field_size[1] = stringwidth("|", false, hud_fontsize);
572 LOG_INFO("fixed missing field '|'");
575 else if(!have_separator)
577 strcpy(sbt_field_title[sbt_num_fields], "|");
578 sbt_field_size[sbt_num_fields] = stringwidth("|", false, hud_fontsize);
579 sbt_field[sbt_num_fields] = SP_SEPARATOR;
581 LOG_INFO("fixed missing field '|'");
585 strcpy(sbt_field_title[sbt_num_fields], TranslateScoresLabel(scores_label(ps_secondary)));
586 sbt_field_size[sbt_num_fields] = stringwidth(sbt_field_title[sbt_num_fields], false, hud_fontsize);
587 sbt_field[sbt_num_fields] = ps_secondary;
589 LOG_INFOF("fixed missing field '%s'", scores_label(ps_secondary));
593 strcpy(sbt_field_title[sbt_num_fields], TranslateScoresLabel(scores_label(ps_primary)));
594 sbt_field_size[sbt_num_fields] = stringwidth(sbt_field_title[sbt_num_fields], false, hud_fontsize);
595 sbt_field[sbt_num_fields] = ps_primary;
597 LOG_INFOF("fixed missing field '%s'", scores_label(ps_primary));
601 sbt_field[sbt_num_fields] = SP_END;
604 string Scoreboard_AddPlayerId(string pl_name, entity pl)
606 string pref = autocvar_hud_panel_scoreboard_playerid_prefix;
607 string suf = autocvar_hud_panel_scoreboard_playerid_suffix;
608 return strcat(pref, itos(pl.sv_entnum + 1), suf, pl_name);
612 vector sbt_field_rgb;
613 string sbt_field_icon0;
614 string sbt_field_icon1;
615 string sbt_field_icon2;
616 string sbt_field_icon3; //LegendGuard adds for Country player flags 05-04-2021
617 vector sbt_field_icon0_rgb;
618 vector sbt_field_icon1_rgb;
619 vector sbt_field_icon2_rgb;
620 string Scoreboard_GetName(entity pl)
622 if(ready_waiting && pl.ready)
624 sbt_field_icon0 = "gfx/scoreboard/player_ready";
628 int f = entcs_GetClientColors(pl.sv_entnum);
630 sbt_field_icon0 = "gfx/scoreboard/playercolor_base";
631 sbt_field_icon1 = "gfx/scoreboard/playercolor_shirt";
632 sbt_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
633 sbt_field_icon2 = "gfx/scoreboard/playercolor_pants";
634 sbt_field_icon2_rgb = colormapPaletteColor(f % 16, 1);
638 return entcs_GetName(pl.sv_entnum);
641 //LegendGuard adds GetCountrycode function 05-04-2021
642 string Scoreboard_GetCountrycode(entity pl)
644 int ccode = entcs_GetCountryCode(pl.sv_entnum);
646 sbt_field_icon3 = strcat("gfx/flags/", ftos(ccode));
648 return ftos(entcs_GetCountryCode(pl.sv_entnum));
651 vector getPingColor(float f)
654 // 20-80 range is green
655 return '0 1 0' + '1 0 1' * max(0, min(60, f-20)) / 60;
657 // 80-300 range is red
658 return '1 1 1' - '0 1 1' * max(0, min(220, f-80)) / 220;
661 string Scoreboard_GetField(entity pl, PlayerScoreField field)
663 float tmp, num, denom;
666 sbt_field_rgb = '1 1 1';
667 sbt_field_icon0 = "";
668 sbt_field_icon1 = "";
669 sbt_field_icon2 = "";
670 sbt_field_icon3 = ""; //LegendGuard adds for Country column 05-04-2021
671 sbt_field_icon0_rgb = '1 1 1';
672 sbt_field_icon1_rgb = '1 1 1';
673 sbt_field_icon2_rgb = '1 1 1';
678 return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6 (Black Right-Pointing Triangle)
679 //str = getplayerkeyvalue(pl.sv_entnum, "ping");
683 sbt_field_rgb = getPingColor(f);
686 //return ftos(pl.team);
691 f = pl.ping_packetloss;
692 tmp = pl.ping_movementloss;
693 if(f == 0 && tmp == 0)
695 str = ftos(ceil(f * 100));
697 str = strcat(str, "~", ftos(ceil(tmp * 100)));
698 tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
699 sbt_field_rgb = '1 0.5 0.5' - '0 0.5 0.5' * tmp;
702 //LegendGuard adds Country REGISTER in the switch 05-04-2021
705 str = Scoreboard_GetCountrycode(pl);
710 str = Scoreboard_GetName(pl);
711 if (autocvar_hud_panel_scoreboard_playerid)
712 str = Scoreboard_AddPlayerId(str, pl);
716 f = pl.(scores(SP_KILLS));
717 f -= pl.(scores(SP_SUICIDES));
721 num = pl.(scores(SP_KILLS));
722 denom = pl.(scores(SP_DEATHS));
725 sbt_field_rgb = '0 1 0';
726 str = sprintf("%d", num);
727 } else if(num <= 0) {
728 sbt_field_rgb = '1 0 0';
729 str = sprintf("%.1f", num/denom);
731 str = sprintf("%.1f", num/denom);
735 f = pl.(scores(SP_KILLS));
736 f -= pl.(scores(SP_DEATHS));
739 sbt_field_rgb = '0 1 0';
741 sbt_field_rgb = '1 1 1';
743 sbt_field_rgb = '1 0 0';
749 float elo = pl.(scores(SP_ELO));
751 case -1: return "...";
752 case -2: return _("N/A");
753 default: return ftos(elo);
759 float fps = pl.(scores(SP_FPS));
762 sbt_field_rgb = '1 1 1';
763 return ((pl.ping == 0) ? _("N/A") : "..."); // if 0 ping, either connecting or bot (either case can't show proper score)
766 //sbt_field_rgb = HUD_Get_Num_Color(fps, 200, true);
767 sbt_field_rgb = '1 0 0' + '0 1 1' * (bound(0, fps, 60) / 60);
771 case SP_DMG: case SP_DMGTAKEN:
772 return sprintf("%.1f k", pl.(scores(field)) / 1000);
774 default: case SP_SCORE:
775 tmp = pl.(scores(field));
776 f = scores_flags(field);
777 if(field == ps_primary)
778 sbt_field_rgb = '1 1 0';
779 else if(field == ps_secondary)
780 sbt_field_rgb = '0 1 1';
782 sbt_field_rgb = '1 1 1';
783 return ScoreString(f, tmp);
788 float sbt_fixcolumnwidth_len;
789 float sbt_fixcolumnwidth_iconlen;
790 float sbt_fixcolumnwidth_marginlen;
792 string Scoreboard_FixColumnWidth(int i, string str)
798 sbt_fixcolumnwidth_iconlen = 0;
800 if(sbt_field_icon0 != "")
802 sz = draw_getimagesize(sbt_field_icon0);
804 if(sbt_fixcolumnwidth_iconlen < f)
805 sbt_fixcolumnwidth_iconlen = f;
808 if(sbt_field_icon1 != "")
810 sz = draw_getimagesize(sbt_field_icon1);
812 if(sbt_fixcolumnwidth_iconlen < f)
813 sbt_fixcolumnwidth_iconlen = f;
816 if(sbt_field_icon2 != "")
818 sz = draw_getimagesize(sbt_field_icon2);
820 if(sbt_fixcolumnwidth_iconlen < f)
821 sbt_fixcolumnwidth_iconlen = f;
824 //LegendGuard adds conditional for Country column 05-04-2021
825 if(sbt_field_icon3 != "")
827 sz = draw_getimagesize(sbt_field_icon3);
829 if(sbt_fixcolumnwidth_iconlen < f)
830 sbt_fixcolumnwidth_iconlen = f;
833 if(sbt_fixcolumnwidth_iconlen != 0)
835 sbt_fixcolumnwidth_iconlen *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect
836 sbt_fixcolumnwidth_marginlen = stringwidth(" ", false, hud_fontsize);
839 sbt_fixcolumnwidth_marginlen = 0;
841 if(sbt_field[i] == SP_NAME) // name gets all remaining space
844 float remaining_space = 0;
845 for(j = 0; j < sbt_num_fields; ++j)
847 if (sbt_field[i] != SP_SEPARATOR)
848 remaining_space += sbt_field_size[j] + hud_fontsize.x;
849 sbt_field_size[i] = panel_size.x - remaining_space;
851 if (sbt_fixcolumnwidth_iconlen != 0)
852 remaining_space += sbt_fixcolumnwidth_marginlen + sbt_fixcolumnwidth_iconlen * hud_fontsize.x;
853 float namesize = panel_size.x - remaining_space;
854 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
855 sbt_fixcolumnwidth_len = stringwidth(str, true, hud_fontsize);
857 max_namesize = vid_conwidth - remaining_space;
860 sbt_fixcolumnwidth_len = stringwidth(str, false, hud_fontsize);
862 f = sbt_fixcolumnwidth_len + sbt_fixcolumnwidth_marginlen + sbt_fixcolumnwidth_iconlen * hud_fontsize.x;
863 if(sbt_field_size[i] < f)
864 sbt_field_size[i] = f;
869 void Scoreboard_initFieldSizes()
871 for(int i = 0; i < sbt_num_fields; ++i)
873 sbt_field_size[i] = stringwidth(sbt_field_title[i], false, hud_fontsize);
874 Scoreboard_FixColumnWidth(i, "");
878 vector Scoreboard_DrawHeader(vector pos, vector rgb, bool other_players, int team)
883 vector column_dim = eY * panel_size.y;
886 column_dim.y -= 1.25 * hud_fontsize.y;
887 vector text_offset = eY * (1.25 - 1) / 2 * hud_fontsize.y;
888 pos.x += hud_fontsize.x * 0.5;
889 for(i = 0; i < sbt_num_fields; ++i)
891 if(sbt_field[i] == SP_SEPARATOR)
894 vector text_offset_center = '0 0 0';
896 if(sbt_field[i] == SP_PING && teamplay) {
897 title_str = sprintf("(%d)", average_ping[Team_TeamToIndex(team) - 1]);
898 title_rgb = getPingColor(average_ping[Team_TeamToIndex(team) - 1]);
899 text_offset_center.x = sbt_field_size[i] - stringwidth(title_str, false, hud_fontsize);
901 title_str = sbt_field_title[i];
902 title_rgb = rgb * 1.5;
905 column_dim.x = sbt_field_size[i] + hud_fontsize.x;
908 drawfill(pos - eX * hud_fontsize.x * 0.5, column_dim, '0 0 0', sbt_highlight_alpha, DRAWFLAG_NORMAL);
909 drawstring(pos + text_offset + text_offset_center, title_str, hud_fontsize, title_rgb, sbt_fg_alpha, DRAWFLAG_NORMAL);
910 pos.x += column_dim.x;
912 if(sbt_field[i] == SP_SEPARATOR)
914 pos.x = panel_pos.x + panel_size.x - hud_fontsize.x * 0.5;
915 for(i = sbt_num_fields - 1; i > 0; --i)
917 if(sbt_field[i] == SP_SEPARATOR)
920 pos.x -= sbt_field_size[i];
925 column_dim.x = sbt_field_size[i] + hud_fontsize.x;
926 drawfill(pos - eX * hud_fontsize.x * 0.5, column_dim, '0 0 0', sbt_highlight_alpha, DRAWFLAG_NORMAL);
929 text_offset.x = sbt_field_size[i] - stringwidth(sbt_field_title[i], false, hud_fontsize);
930 drawstring(pos + text_offset, sbt_field_title[i], hud_fontsize, rgb * 1.5, sbt_fg_alpha, DRAWFLAG_NORMAL);
931 pos.x -= hud_fontsize.x;
936 pos.y += 1.25 * hud_fontsize.y;
940 void Scoreboard_DrawItem(vector item_pos, vector rgb, entity pl, bool is_self, int pl_number)
942 TC(bool, is_self); TC(int, pl_number);
944 bool is_spec = (entcs_GetSpecState(pl.sv_entnum) == ENTCS_SPEC_PURE);
946 vector h_pos = item_pos;
947 vector h_size = vec2(panel_size.x, hud_fontsize.y * 1.25);
948 // alternated rows highlighting
950 drawfill(h_pos, h_size, rgb, sbt_highlight_alpha_self, DRAWFLAG_NORMAL);
951 else if((sbt_highlight) && (!(pl_number % 2)))
952 drawfill(h_pos, h_size, rgb, sbt_highlight_alpha, DRAWFLAG_NORMAL);
954 float fg_alpha = (is_self ? sbt_fg_alpha_self : sbt_fg_alpha);
956 vector pos = item_pos;
957 // put a "self indicator" beside the self row, unicode U+25C0 (black left-pointing triangle)
959 drawstring(pos + eX * (panel_size.x + 0.5 * hud_fontsize.x) + eY, "\xE2\x97\x80", hud_fontsize, rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
961 pos.x += hud_fontsize.x * 0.5;
962 pos.y += (1.25 - 1) / 2 * hud_fontsize.y; // center text vertically
963 vector tmp = '0 0 0';
965 PlayerScoreField field;
966 for(i = 0; i < sbt_num_fields; ++i)
968 field = sbt_field[i];
969 if(field == SP_SEPARATOR)
972 if(is_spec && field != SP_NAME && field != SP_PING) {
973 pos.x += sbt_field_size[i] + hud_fontsize.x;
976 str = Scoreboard_GetField(pl, field);
977 str = Scoreboard_FixColumnWidth(i, str);
979 pos.x += sbt_field_size[i] + hud_fontsize.x;
981 if(field == SP_NAME) {
982 tmp.x = sbt_field_size[i] - hud_fontsize.x * sbt_fixcolumnwidth_iconlen - sbt_fixcolumnwidth_marginlen + hud_fontsize.x;
983 drawcolorcodedstring(pos - tmp, str, hud_fontsize, fg_alpha, DRAWFLAG_NORMAL);
985 tmp.x = sbt_fixcolumnwidth_len + hud_fontsize.x;
986 drawstring(pos - tmp, str, hud_fontsize, sbt_field_rgb, fg_alpha, DRAWFLAG_NORMAL);
989 tmp.x = sbt_field_size[i] + hud_fontsize.x;
990 if(sbt_field_icon0 != "")
991 drawpic(pos - tmp, sbt_field_icon0, vec2(hud_fontsize.x * sbt_fixcolumnwidth_iconlen, hud_fontsize.y), sbt_field_icon1_rgb, fg_alpha, DRAWFLAG_NORMAL);
992 if(sbt_field_icon1 != "")
993 drawpic(pos - tmp, sbt_field_icon1, vec2(hud_fontsize.x * sbt_fixcolumnwidth_iconlen, hud_fontsize.y), sbt_field_icon1_rgb, fg_alpha, DRAWFLAG_NORMAL);
994 if(sbt_field_icon2 != "")
995 drawpic(pos - tmp, sbt_field_icon2, vec2(hud_fontsize.x * sbt_fixcolumnwidth_iconlen, hud_fontsize.y), sbt_field_icon2_rgb, fg_alpha, DRAWFLAG_NORMAL);
996 if(sbt_field_icon3 != "") //LegendGuard adds conditional for Country column 05-04-2021
997 drawpic(pos - tmp, sbt_field_icon3, vec2(hud_fontsize.x * sbt_fixcolumnwidth_iconlen, hud_fontsize.y), sbt_field_icon1_rgb, fg_alpha, DRAWFLAG_NORMAL);
1000 if(sbt_field[i] == SP_SEPARATOR)
1002 pos.x = item_pos.x + panel_size.x - hud_fontsize.x * 0.5;
1003 for(i = sbt_num_fields-1; i > 0; --i)
1005 field = sbt_field[i];
1006 if(field == SP_SEPARATOR)
1009 if(is_spec && field != SP_NAME && field != SP_PING) {
1010 pos.x -= sbt_field_size[i] + hud_fontsize.x;
1014 str = Scoreboard_GetField(pl, field);
1015 str = Scoreboard_FixColumnWidth(i, str);
1017 if(field == SP_NAME) {
1018 tmp.x = sbt_fixcolumnwidth_len; // left or right aligned? let's put it right...
1019 drawcolorcodedstring(pos - tmp, str, hud_fontsize, fg_alpha, DRAWFLAG_NORMAL);
1021 tmp.x = sbt_fixcolumnwidth_len;
1022 drawstring(pos - tmp, str, hud_fontsize, sbt_field_rgb, fg_alpha, DRAWFLAG_NORMAL);
1025 tmp.x = sbt_field_size[i];
1026 if(sbt_field_icon0 != "")
1027 drawpic(pos - tmp, sbt_field_icon0, vec2(hud_fontsize.x * sbt_fixcolumnwidth_iconlen, hud_fontsize.y), sbt_field_icon1_rgb, fg_alpha, DRAWFLAG_NORMAL);
1028 if(sbt_field_icon1 != "")
1029 drawpic(pos - tmp, sbt_field_icon1, vec2(hud_fontsize.x * sbt_fixcolumnwidth_iconlen, hud_fontsize.y), sbt_field_icon1_rgb, fg_alpha, DRAWFLAG_NORMAL);
1030 if(sbt_field_icon2 != "")
1031 drawpic(pos - tmp, sbt_field_icon2, vec2(hud_fontsize.x * sbt_fixcolumnwidth_iconlen, hud_fontsize.y), sbt_field_icon2_rgb, fg_alpha, DRAWFLAG_NORMAL);
1032 if(sbt_field_icon3 != "") //LegendGuard adds conditional for Country column 05-04-2021
1033 drawpic(pos - tmp, sbt_field_icon3, vec2(hud_fontsize.x * sbt_fixcolumnwidth_iconlen, hud_fontsize.y), sbt_field_icon1_rgb, fg_alpha, DRAWFLAG_NORMAL);
1034 pos.x -= sbt_field_size[i] + hud_fontsize.x;
1039 drawfill(h_pos, h_size, '0 0 0', sbt_highlight_alpha_eliminated, DRAWFLAG_NORMAL);
1042 vector Scoreboard_DrawOthers(vector item_pos, vector rgb, int this_team, entity ignored_pl, entity pl, int pl_number)
1045 vector h_pos = item_pos;
1046 vector h_size = vec2(panel_size.x, hud_fontsize.y * 1.25);
1048 bool complete = (this_team == NUM_SPECTATOR);
1051 if((sbt_highlight) && (!(pl_number % 2)))
1052 drawfill(h_pos, h_size, rgb, sbt_highlight_alpha, DRAWFLAG_NORMAL);
1054 vector pos = item_pos;
1055 pos.x += hud_fontsize.x * 0.5;
1056 pos.y += (1.25 - 1) / 2 * hud_fontsize.y; // center text vertically
1058 float width_limit = item_pos.x + panel_size.x - hud_fontsize.x;
1060 width_limit -= stringwidth("...", false, hud_fontsize);
1061 float namesize = autocvar_hud_panel_scoreboard_namesize * hud_fontsize.x;
1062 static float max_name_width = 0;
1064 float fieldsize = 0;
1065 float min_fieldsize = 0;
1066 float fieldpadding = hud_fontsize.x * 0.25;
1067 if(this_team == NUM_SPECTATOR)
1069 if(autocvar_hud_panel_scoreboard_spectators_showping)
1070 min_fieldsize = stringwidth("999", false, hud_fontsize);
1072 else if(autocvar_hud_panel_scoreboard_others_showscore)
1073 min_fieldsize = stringwidth("99", false, hud_fontsize);
1074 for(i = 0; pl; pl = pl.sort_next)
1076 if(pl.team != this_team)
1078 if(pl == ignored_pl)
1082 if(this_team == NUM_SPECTATOR)
1084 if(autocvar_hud_panel_scoreboard_spectators_showping)
1085 field = Scoreboard_GetField(pl, SP_PING);
1087 else if(autocvar_hud_panel_scoreboard_others_showscore)
1088 field = Scoreboard_GetField(pl, SP_SCORE);
1090 string str = entcs_GetName(pl.sv_entnum);
1091 if (autocvar_hud_panel_scoreboard_playerid)
1092 str = Scoreboard_AddPlayerId(str, pl);
1093 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
1094 float column_width = stringwidth(str, true, hud_fontsize);
1095 if((this_team == NUM_SPECTATOR) && autocvar_hud_panel_scoreboard_spectators_aligned)
1097 if(column_width > max_name_width)
1098 max_name_width = column_width;
1099 column_width = max_name_width;
1103 fieldsize = stringwidth(field, false, hud_fontsize);
1104 column_width += hud_fontsize.x * 0.25 + max(fieldsize, min_fieldsize) + 2 * fieldpadding;
1107 if(pos.x + column_width > width_limit)
1112 drawstring(pos, "...", hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
1117 pos.x = item_pos.x + hud_fontsize.x * 0.5;
1118 pos.y += hud_fontsize.y * 1.25;
1122 vector name_pos = pos;
1123 if((this_team == NUM_SPECTATOR) && autocvar_hud_panel_scoreboard_spectators_aligned)
1124 name_pos.x += max(fieldsize, min_fieldsize) + 2 * fieldpadding + hud_fontsize.x * 0.25;
1125 drawcolorcodedstring(name_pos, str, hud_fontsize, sbt_fg_alpha, DRAWFLAG_NORMAL);
1128 h_size.x = max(fieldsize, min_fieldsize) + 2 * fieldpadding;
1129 h_size.y = hud_fontsize.y;
1130 vector field_pos = pos;
1131 if(!((this_team == NUM_SPECTATOR) && autocvar_hud_panel_scoreboard_spectators_aligned))
1132 field_pos.x += column_width - h_size.x;
1134 drawfill(field_pos, h_size, '1 1 1', sbt_highlight_alpha, DRAWFLAG_NORMAL);
1135 field_pos.x += fieldpadding + (max(fieldsize, min_fieldsize) - fieldsize) * 0.5;
1136 drawstring(field_pos, field, hud_fontsize, sbt_field_rgb, sbt_fg_alpha, DRAWFLAG_NORMAL);
1140 h_size.x = column_width + hud_fontsize.x * 0.25;
1141 h_size.y = hud_fontsize.y;
1142 drawfill(pos - hud_fontsize.x * 0.25 * eX, h_size, '0 0 0', sbt_highlight_alpha_eliminated, DRAWFLAG_NORMAL);
1144 pos.x += column_width;
1145 pos.x += hud_fontsize.x;
1147 return vec2(item_pos.x, item_pos.y + i * hud_fontsize.y * 1.25);
1150 vector Scoreboard_DrawMedal(vector pos, string icon, float height, float number)
1152 if(!number) return pos;
1153 total_medals += number;
1155 vector tmp_sz, tmp_sz2;
1156 tmp_sz = draw_getimagesize(icon);
1157 tmp_sz2 = vec2(height*(tmp_sz.x/tmp_sz.y), height);
1158 string val = ftos(number);
1160 drawpic(pos, icon, tmp_sz2, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1162 pos.x += tmp_sz2.x + hud_fontsize.x * 0.25;
1163 drawstring(pos + eY * ((tmp_sz2.y - hud_fontsize.y) / 2), val, hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1165 pos.x += stringwidth(val, false, hud_fontsize) + hud_fontsize.x * 0.5;
1169 vector Scoreboard_Duel_DrawPickup(vector pos, bool skinned, string icon, vector sz, float number, bool invert)
1171 vector tmp_in = pos;
1172 vector tmp_sz, tmp_sz2;
1177 picpath = strcat(hud_skin_path, "/", icon);
1178 if(precache_pic(picpath) == "")
1179 picpath = strcat("gfx/hud/default/", icon);
1184 tmp_sz = draw_getimagesize(picpath);
1185 tmp_sz2 = vec2(sz.y*(tmp_sz.x/tmp_sz.y), sz.y);
1187 tmp_in.x = pos.x + ((sz.x - tmp_sz2.x) / 2);
1188 drawpic(tmp_in, picpath, tmp_sz2, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1192 tmp_in.x += tmp_sz2.x + hud_fontsize.x * 0.25;
1194 tmp_in.x -= hud_fontsize.x * 0.25 + hud_fontsize.x;
1196 tmp_in.y += (tmp_sz2.y - hud_fontsize.y) / 2;
1198 ((number == -1) ? "?" : ftos(number)),
1199 hud_fontsize, ((number > 0) ? '1 1 1' : '0.5 0.5 0.5'),
1203 pos.y += sz.y * 1.1;
1207 void Scoreboard_Duel_DrawTable(vector pos, bool invert, entity pl, entity tm)
1209 vector tmp, tmp_in, tmp_sz, tmp_acc;
1212 float average_acc = 0;
1218 // Stop here if there are no scores available
1219 if(pl.team != tm.team) return;
1222 tmp.x += panel_bg_padding;
1223 tmp.y += panel_bg_padding;
1224 panel_size.x -= panel_bg_padding * 2;
1227 // drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", tmp, panel_size, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL);
1230 if(invert) { tmp.x += panel_size.x; tmp.x -= duel_score_size.x; }
1231 drawfill(tmp, duel_score_size, '0 0 0', sbt_highlight_alpha, DRAWFLAG_NORMAL);
1234 tmp_str = ftos(pl.(scores(SP_SCORE)));
1236 tmp_in.x += (duel_score_size.x / 2) - (stringwidth(tmp_str, true, duel_score_fontsize) / 2);
1237 tmp_in.y += (duel_score_size.y / 2) - (duel_score_fontsize.y / 2);
1239 draw_beginBoldFont();
1240 drawstring(tmp_in, tmp_str, duel_score_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1244 tmp_str = Scoreboard_GetField(pl, SP_NAME);
1247 tmp_in.x -= stringwidth_colors(tmp_str, duel_name_fontsize) + duel_name_fontsize.x * 0.5;
1249 tmp_in.x += duel_score_size.x + duel_name_fontsize.x * 0.5;
1250 tmp_in.y += (duel_score_size.y - duel_name_fontsize.y) / 2;
1251 drawcolorcodedstring(tmp_in, tmp_str, duel_name_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
1254 if(sbt_field_icon0 != "") {
1255 vector rsz = draw_getimagesize(sbt_field_icon0);
1256 sbt_fixcolumnwidth_iconlen = rsz.x / rsz.y;
1258 tmp_in.x -= hud_fontsize.x * sbt_fixcolumnwidth_iconlen + duel_name_fontsize.x * 0.5;
1260 tmp_in.x += stringwidth_colors(tmp_str, duel_name_fontsize) + duel_name_fontsize.x * 0.5;
1261 tmp_in.y += (duel_name_fontsize.y - hud_fontsize.y) / 2;
1262 drawpic(tmp_in, sbt_field_icon0, vec2(hud_fontsize.x * sbt_fixcolumnwidth_iconlen, hud_fontsize.y), sbt_field_icon1_rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
1265 //LegendGuard adds a conditional sentence for country column 05-04-2021
1266 // Player country icon/flag
1267 if(sbt_field_icon3 != "") {
1268 vector rsz = draw_getimagesize(sbt_field_icon3);
1269 sbt_fixcolumnwidth_iconlen = rsz.x / rsz.y;
1271 tmp_in.x -= hud_fontsize.x * sbt_fixcolumnwidth_iconlen + duel_name_fontsize.x * 0.5;
1273 tmp_in.x += stringwidth_colors(tmp_str, duel_name_fontsize) + duel_name_fontsize.x * 0.5;
1274 tmp_in.y += (duel_name_fontsize.y - hud_fontsize.y) / 2;
1275 drawpic(tmp_in, sbt_field_icon3, vec2(hud_fontsize.x * sbt_fixcolumnwidth_iconlen, hud_fontsize.y), sbt_field_icon1_rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
1279 float column_width = panel_size.x / 5;
1280 tmp.x = pos.x + panel_bg_padding;
1281 tmp.y += hud_fontsize.y * 3 + hud_fontsize.y;
1286 i = (invert ? 4 : 0);
1287 column_dim = vec2(column_width * 4, hud_fontsize.y);
1289 drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth("kills", false, hud_fontsize) / 2),
1290 "kills", hud_fontsize, '0.5 0.5 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1291 drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth("dmg", false, hud_fontsize) / 2),
1292 "dmg", hud_fontsize, '0.5 0.5 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1293 drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth("acc", false, hud_fontsize) / 2),
1294 "acc", hud_fontsize, '0.5 0.5 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1295 drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth("hits", false, hud_fontsize) / 2),
1296 "hits", hud_fontsize, '0.5 0.5 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1297 drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth("ping", false, hud_fontsize) / 2),
1298 "ping", hud_fontsize, '0.5 0.5 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1300 tmp.x = pos.x + panel_bg_padding;
1301 tmp.y += hud_fontsize.y;
1304 i = (invert ? 4 : 0);
1306 tmp_str = ftos(pl.(scores(SP_KILLS)));
1307 drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth(tmp_str, false, hud_fontsize * 1.25) / 2),
1308 tmp_str, hud_fontsize * 1.25, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1310 tmp_str = ftos(pl.(scores(SP_DMG)));
1311 drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth(tmp_str, false, hud_fontsize * 1.25) / 2),
1312 tmp_str, hud_fontsize * 1.25, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1314 tmp_acc = tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2);
1321 tmp_str = Scoreboard_GetField(pl, SP_PING);
1322 drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth(tmp_str, false, hud_fontsize * 1.25) / 2),
1323 tmp_str, hud_fontsize * 1.25, sbt_field_rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
1325 tmp.x = pos.x + panel_bg_padding;
1326 tmp.y += hud_fontsize.y * 2;
1330 int total_weapons = 0;
1333 WepSet weapons_inmap = WepSet_GetFromStat_InMap();
1334 FOREACH(Weapons, it != WEP_Null, {
1335 WepSet set = it.m_wepset;
1336 if (!(weapons_inmap & set))
1338 if (it.spawnflags & WEP_TYPE_OTHER)
1341 int weapon_cnt_fired = pl.accuracy_cnt_fired[i - WEP_FIRST];
1342 int weapon_cnt_hit = pl.accuracy_cnt_hit[i - WEP_FIRST];
1344 if(weapon_cnt_fired)
1345 weapon_acc = floor((weapon_cnt_hit / weapon_cnt_fired) * 100);
1346 average_acc += weapon_acc;
1351 int c = (invert ? 4 : 0);
1353 drawfill(tmp_in + eX * column_width * (invert ? 1 : 0), column_dim, '0 0 0', sbt_highlight_alpha, DRAWFLAG_NORMAL);
1355 draw_str = ftos(pl.accuracy_frags[i - WEP_FIRST]);
1356 drawstring(tmp_in + eX * column_width * (invert ? c-- : c++) + eX * ((column_width - stringwidth(draw_str, false, hud_fontsize)) / 2),
1357 draw_str, hud_fontsize, (weapon_cnt_fired ? '1 1 1' : '0.5 0.5 0.5'), panel_fg_alpha, DRAWFLAG_NORMAL);
1359 draw_str = ftos(pl.accuracy_hit[i - WEP_FIRST]);
1360 drawstring(tmp_in + eX * column_width * (invert ? c-- : c++) + eX * ((column_width - stringwidth(draw_str, false, hud_fontsize)) / 2),
1361 draw_str, hud_fontsize, (weapon_cnt_fired ? '1 1 1' : '0.5 0.5 0.5'), panel_fg_alpha, DRAWFLAG_NORMAL);
1363 draw_str = sprintf("%d%%", weapon_acc);
1364 drawstring(tmp_in + eX * column_width * (invert ? c-- : c++) + eX * ((column_width - stringwidth(draw_str, false, hud_fontsize)) / 2),
1365 draw_str, hud_fontsize, (weapon_cnt_fired ? '1 1 1' : '0.5 0.5 0.5'), panel_fg_alpha, DRAWFLAG_NORMAL);
1367 draw_str = strcat(ftos(weapon_cnt_hit), " / ", ftos(weapon_cnt_fired));
1368 drawstring(tmp_in + eX * column_width * (invert ? c-- : c++) + eX * (column_width / 2) - eX * stringwidth("36 /", false, hud_fontsize),
1369 draw_str,hud_fontsize, (weapon_cnt_fired ? '1 1 1' : '0.5 0.5 0.5'), panel_fg_alpha, DRAWFLAG_NORMAL);
1373 tmp_in.x = pos.x + panel_size.x - panel_bg_padding - hud_fontsize.x / 2;
1374 drawpic_aspect_skin(tmp_in, it.model2, vec2(50, hud_fontsize.y), '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1377 tmp_in.x = pos.x + panel_bg_padding;
1378 tmp_in.y += hud_fontsize.y * 1.25;
1380 if(weapon_cnt_fired)
1384 average_acc = floor((average_acc / total_weapons) + 0.5);
1386 // draw total accuracy now
1387 tmp_str = sprintf("%d%%", average_acc);
1388 drawstring(tmp_acc - eX * (stringwidth(tmp_str, false, hud_fontsize * 1.25) / 2),
1389 tmp_str, hud_fontsize * 1.25, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1392 vector icon_sz = vec2(column_width, hud_fontsize.y*1.5);
1395 tmp.x += column_width * 4;
1397 drawstring(tmp + eX * ((column_width - stringwidth("medals", false, hud_fontsize)) / 2),
1398 "medals", hud_fontsize, '0.5 0.5 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1399 tmp.y += hud_fontsize.y * 1.25;
1401 tmp = Scoreboard_Duel_DrawPickup(tmp, false, "gfx/medal/humiliation", icon_sz, pl.(scores(SP_MEDAL_HUMILIATION)), invert);
1402 tmp = Scoreboard_Duel_DrawPickup(tmp, false, "gfx/medal/impressive", icon_sz, pl.(scores(SP_MEDAL_IMPRESSIVE)), invert);
1403 tmp = Scoreboard_Duel_DrawPickup(tmp, false, "gfx/medal/excellent", icon_sz, pl.(scores(SP_MEDAL_EXCELLENT)), invert);
1406 drawstring(tmp + eX * ((column_width - stringwidth("items", false, hud_fontsize)) / 2),
1407 "items", hud_fontsize, '0.5 0.5 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1408 tmp.y += hud_fontsize.y * 1.25;
1412 it.m_id == ITEM_ArmorMega.m_id ||
1413 it.m_id == ITEM_HealthMega.m_id ||
1414 it.m_id == ITEM_ArmorBig.m_id, {
1415 // If the match isn't over, Only show pickups if we're spectating or they're our own
1416 if(intermission || warmup_stage || spectatee_status || pl.sv_entnum == current_player)
1417 inv_num = inventoryslots[pl.sv_entnum].inv_items[it.m_id];
1418 tmp = Scoreboard_Duel_DrawPickup(tmp, true, it.m_icon, icon_sz, inv_num, invert);
1420 if(it.m_id == REGISTRY_MAX(Items))
1424 vector Scoreboard_MakeDuelTable(vector pos, entity tm, vector rgb, vector bg_size)
1426 vector end_pos = pos;
1427 float screen_half = panel_size.x / 2;
1428 float weapon_margin = hud_fontsize.x;
1430 panel_size.x = screen_half - weapon_margin;
1431 panel_size.y = (duel_score_size.y * 5.5);
1433 entity pl_left = players.sort_next;
1434 entity pl_right = pl_left.sort_next;
1436 Scoreboard_Duel_DrawTable(pos, true, pl_left, tm);
1437 Scoreboard_Duel_DrawTable(pos + eX * screen_half + eX * weapon_margin, false, pl_right, tm);
1439 end_pos.y += panel_size.y + (panel_bg_padding * 2);
1440 panel_size.x = screen_half * 2;
1444 vector Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
1446 int max_players = 999;
1447 if(autocvar_hud_panel_scoreboard_maxheight > 0)
1449 float height = autocvar_hud_panel_scoreboard_maxheight * vid_conheight;
1452 height -= (panel_bg_padding * 2 + hud_fontsize.y * 1.25) * team_count; // - padding and header
1453 height -= hud_fontsize.y * (team_count - 1); // - spacing between tables
1454 height /= team_count;
1457 height -= panel_bg_padding * 2; // - padding
1458 max_players = floor(height / (hud_fontsize.y * 1.25));
1459 if(max_players <= 1)
1461 if(max_players == tm.team_size)
1466 entity me = playerslots[current_player];
1468 panel_size.y = 1.25 * hud_fontsize.y * (1 + bound(1, tm.team_size, max_players));
1469 panel_size.y += panel_bg_padding * 2;
1472 vector end_pos = panel_pos + eY * (panel_size.y + 0.5 * hud_fontsize.y);
1473 if(panel.current_panel_bg != "0")
1474 end_pos.y += panel_bg_border * 2;
1476 if(panel_bg_padding)
1478 panel_pos += '1 1 0' * panel_bg_padding;
1479 panel_size -= '2 2 0' * panel_bg_padding;
1483 vector tmp = vec2(panel_size.x, 1.25 * hud_fontsize.y);
1487 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', sbt_bg_alpha, DRAWFLAG_NORMAL);
1489 pos.y += 1.25 * hud_fontsize.y;
1492 tmp.y = panel_size.y - 1.25 * hud_fontsize.y;
1494 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL);
1497 // print header row and highlight columns
1498 pos = Scoreboard_DrawHeader(panel_pos, rgb, (max_players < tm.team_size), tm.team);
1500 // fill the table and draw the rows
1501 bool is_self = false;
1502 bool self_shown = false;
1505 if(Team_IsValidTeam(tm.team)) average_ping[Team_TeamToIndex(tm.team) - 1] = 0;
1506 for(pl = players.sort_next; pl; pl = pl.sort_next)
1508 if(pl.team != tm.team)
1510 if(i == max_players - 2 && pl != me)
1512 if(!self_shown && me.team == tm.team)
1514 Scoreboard_DrawItem(pos, rgb, me, true, i);
1516 pos.y += 1.25 * hud_fontsize.y;
1520 if(i >= max_players - 1)
1522 pos = Scoreboard_DrawOthers(pos, rgb, tm.team, (self_shown ? me : NULL), pl, i);
1525 is_self = (pl.sv_entnum == current_player);
1526 Scoreboard_DrawItem(pos, rgb, pl, is_self, i);
1528 if(Team_IsValidTeam(tm.team) && pl.ping) {
1529 average_ping[Team_TeamToIndex(tm.team) - 1] += pl.ping;
1534 pos.y += 1.25 * hud_fontsize.y;
1537 if(with_ping) average_ping[Team_TeamToIndex(tm.team) - 1] /= with_ping;
1539 panel_size.x += panel_bg_padding * 2; // restore initial width
1543 bool Scoreboard_WouldDraw()
1545 if (MUTATOR_CALLHOOK(DrawScoreboard))
1547 else if (QuickMenu_IsOpened())
1549 else if (HUD_Radar_Clickable())
1551 else if (scoreboard_showscores)
1553 else if (intermission == 1)
1555 else if (intermission == 2)
1557 else if (spectatee_status != -1 && STAT(HEALTH) <= 0 && autocvar_cl_deathscoreboard && !MUTATOR_CALLHOOK(DrawDeathScoreboard)
1558 && (!HUD_MinigameMenu_IsOpened() || !active_minigame))
1562 else if (scoreboard_showscores_force || MUTATOR_CALLHOOK(DrawScoreboard_Force))
1567 vector Scoreboard_MedalStats_Draw(vector pos)
1570 float height = hud_fontsize.y * 2;
1572 entity pl = playerslots[current_player];
1574 vector title_pos = pos;
1575 pos.x += 0.5 * hud_fontsize.x + panel_bg_padding;
1576 pos.y += 1.25 * hud_fontsize.y;
1580 pos = Scoreboard_DrawMedal(pos, "gfx/medal/airshot", height, pl.(scores(SP_MEDAL_AIRSHOT)));
1581 pos = Scoreboard_DrawMedal(pos, "gfx/medal/damage", height, pl.(scores(SP_MEDAL_DAMAGE)));
1582 pos = Scoreboard_DrawMedal(pos, "gfx/medal/electrobitch", height, pl.(scores(SP_MEDAL_ELECTROBITCH)));
1583 pos = Scoreboard_DrawMedal(pos, "gfx/medal/excellent", height, pl.(scores(SP_MEDAL_EXCELLENT)));
1584 pos = Scoreboard_DrawMedal(pos, "gfx/medal/firstblood", height, pl.(scores(SP_MEDAL_FIRSTBLOOD)));
1585 pos = Scoreboard_DrawMedal(pos, "gfx/medal/headshot", height, pl.(scores(SP_MEDAL_HEADSHOT)));
1586 pos = Scoreboard_DrawMedal(pos, "gfx/medal/humiliation", height, pl.(scores(SP_MEDAL_HUMILIATION)));
1587 pos = Scoreboard_DrawMedal(pos, "gfx/medal/impressive", height, pl.(scores(SP_MEDAL_IMPRESSIVE)));
1588 pos = Scoreboard_DrawMedal(pos, "gfx/medal/yoda", height, pl.(scores(SP_MEDAL_YODA)));
1589 pos = Scoreboard_DrawMedal(pos, "gfx/medal/telefrag", height, pl.(scores(SP_MEDAL_TELEFRAG)));
1592 pos.x += hud_fontsize.x;
1594 pos = Scoreboard_DrawMedal(pos, "gfx/medal/accuracy", height, pl.(scores(SP_MEDAL_ACCURACY)));
1595 pos = Scoreboard_DrawMedal(pos, "gfx/medal/assist", height, pl.(scores(SP_MEDAL_ASSIST)));
1596 pos = Scoreboard_DrawMedal(pos, "gfx/medal/capture", height, pl.(scores(SP_MEDAL_CAPTURE)));
1597 pos = Scoreboard_DrawMedal(pos, "gfx/medal/defense", height, pl.(scores(SP_MEDAL_DEFENSE)));
1598 pos = Scoreboard_DrawMedal(pos, "gfx/medal/perfect", height, pl.(scores(SP_MEDAL_PERFECT)));
1600 if(!total_medals) return orig;
1602 drawstring(title_pos, sprintf(_("Medal stats (total %d)"), total_medals),
1603 hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1606 pos.y += height + hud_fontsize.y * 0.5;
1610 float average_accuracy;
1611 vector Scoreboard_AccuracyStats_Draw(vector pos, vector rgb, vector bg_size)
1613 scoreboard_acc_fade_alpha = min(scoreboard_fade_alpha, scoreboard_acc_fade_alpha + frametime * 10);
1615 WepSet weapons_stat = WepSet_GetFromStat();
1616 WepSet weapons_inmap = WepSet_GetFromStat_InMap();
1617 int disownedcnt = 0;
1619 FOREACH(Weapons, it != WEP_Null, {
1620 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1622 WepSet set = it.m_wepset;
1623 if(it.spawnflags & WEP_TYPE_OTHER)
1628 if (weapon_stats < 0 && !((weapons_stat & set) || (weapons_inmap & set)))
1630 if (it.spawnflags & (WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_SPECIALATTACK))
1637 int weapon_cnt = (REGISTRY_COUNT(Weapons) - 1) - disownedcnt - nHidden;
1638 if (weapon_cnt <= 0) return pos;
1641 if (autocvar_hud_panel_scoreboard_accuracy_doublerows && weapon_cnt >= floor((REGISTRY_COUNT(Weapons) - nHidden - 1) * 0.5))
1643 int columnns = ceil(weapon_cnt / rows);
1645 float weapon_height = 29;
1646 float height = hud_fontsize.y + weapon_height;
1648 drawstring(pos + eX * panel_bg_padding, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', panel_fg_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL);
1649 pos.y += 1.25 * hud_fontsize.y;
1650 if(panel.current_panel_bg != "0")
1651 pos.y += panel_bg_border;
1654 panel_size.y = height * rows;
1655 panel_size.y += panel_bg_padding * 2;
1657 float panel_bg_alpha_save = panel_bg_alpha;
1658 panel_bg_alpha *= scoreboard_acc_fade_alpha;
1660 panel_bg_alpha = panel_bg_alpha_save;
1662 vector end_pos = panel_pos + eY * (panel_size.y + 0.5 * hud_fontsize.y);
1663 if(panel.current_panel_bg != "0")
1664 end_pos.y += panel_bg_border * 2;
1666 if(panel_bg_padding)
1668 panel_pos += '1 1 0' * panel_bg_padding;
1669 panel_size -= '2 2 0' * panel_bg_padding;
1673 vector tmp = panel_size;
1675 float weapon_width = tmp.x / columnns / rows;
1678 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, sbt_bg_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL);
1682 // column highlighting
1683 for (int i = 0; i < columnns; ++i)
1685 drawfill(pos + eX * weapon_width * rows * i, vec2(weapon_width * rows, height * rows), '0 0 0', sbt_highlight_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL);
1688 for (int i = 0; i < rows; ++i)
1689 drawfill(pos + eY * (weapon_height + height * i), vec2(tmp.x, hud_fontsize.y), rgb, sbt_highlight_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL);
1692 average_accuracy = 0;
1693 int weapons_with_stats = 0;
1695 pos.x += weapon_width / 2;
1697 if (autocvar_hud_panel_scoreboard_accuracy_nocolors)
1700 Accuracy_LoadColors();
1702 float oldposx = pos.x;
1706 FOREACH(Weapons, it != WEP_Null, {
1707 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1709 WepSet set = it.m_wepset;
1710 if (weapon_stats < 0 && !((weapons_stat & set) || (weapons_inmap & set)))
1712 if (it.spawnflags & WEP_TYPE_OTHER)
1716 if (weapon_stats >= 0)
1717 weapon_alpha = sbt_fg_alpha;
1719 weapon_alpha = 0.2 * sbt_fg_alpha;
1722 drawpic_aspect_skin(tmpos, it.model2, vec2(weapon_width, weapon_height), '1 1 1', weapon_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL);
1724 if (weapon_stats >= 0) {
1725 weapons_with_stats += 1;
1726 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1729 s = sprintf("%d%%", weapon_stats * 100);
1732 padding = (weapon_width - stringwidth(s, false, hud_fontsize)) / 2; // center the accuracy value
1734 if(!autocvar_hud_panel_scoreboard_accuracy_nocolors)
1735 rgb = Accuracy_GetColor(weapon_stats);
1737 drawstring(tmpos + vec2(padding, weapon_height), s, hud_fontsize, rgb, sbt_fg_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL);
1739 tmpos.x += weapon_width * rows;
1740 pos.x += weapon_width * rows;
1741 if (rows == 2 && column == columnns - 1) {
1749 if (weapons_with_stats)
1750 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1752 panel_size.x += panel_bg_padding * 2; // restore initial width
1757 .bool uninteresting;
1758 STATIC_INIT(default_order_items_label)
1760 IL_EACH(default_order_items, true, {
1761 if(!(it.instanceOfPowerup
1762 || it == ITEM_HealthMega || it == ITEM_HealthBig
1763 || it == ITEM_ArmorMega || it == ITEM_ArmorBig
1766 it.uninteresting = true;
1771 vector Scoreboard_ItemStats_Draw(vector pos, vector rgb, vector bg_size)
1773 scoreboard_itemstats_fade_alpha = min(scoreboard_fade_alpha, scoreboard_itemstats_fade_alpha + frametime * 10);
1775 int disowned_cnt = 0;
1776 int uninteresting_cnt = 0;
1777 IL_EACH(default_order_items, true, {
1778 int q = g_inventory.inv_items[it.m_id];
1779 //q = 1; // debug: display all items
1780 if (autocvar_hud_panel_scoreboard_itemstats_filter && it.uninteresting)
1781 ++uninteresting_cnt;
1785 int items_cnt = REGISTRY_COUNT(Items) - uninteresting_cnt;
1786 int n = items_cnt - disowned_cnt;
1787 if (n <= 0) return pos;
1789 int rows = (autocvar_hud_panel_scoreboard_itemstats_doublerows && n >= floor(REGISTRY_COUNT(Items) / 2)) ? 2 : 1;
1790 int columnns = max(6, ceil(n / rows));
1793 float fontsize = height * 1/3;
1794 float item_height = height * 2/3;
1796 drawstring(pos + eX * panel_bg_padding, _("Item stats"), hud_fontsize, '1 1 1', panel_fg_alpha * scoreboard_itemstats_fade_alpha, DRAWFLAG_NORMAL);
1797 pos.y += 1.25 * hud_fontsize.y;
1798 if(panel.current_panel_bg != "0")
1799 pos.y += panel_bg_border;
1802 panel_size.y = height * rows;
1803 panel_size.y += panel_bg_padding * 2;
1805 float panel_bg_alpha_save = panel_bg_alpha;
1806 panel_bg_alpha *= scoreboard_itemstats_fade_alpha;
1808 panel_bg_alpha = panel_bg_alpha_save;
1810 vector end_pos = panel_pos + eY * (panel_size.y + 0.5 * hud_fontsize.y);
1811 if(panel.current_panel_bg != "0")
1812 end_pos.y += panel_bg_border * 2;
1814 if(panel_bg_padding)
1816 panel_pos += '1 1 0' * panel_bg_padding;
1817 panel_size -= '2 2 0' * panel_bg_padding;
1821 vector tmp = panel_size;
1823 float item_width = tmp.x / columnns / rows;
1826 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, sbt_bg_alpha * scoreboard_itemstats_fade_alpha, DRAWFLAG_NORMAL);
1830 // column highlighting
1831 for (int i = 0; i < columnns; ++i)
1833 drawfill(pos + '1 0 0' * item_width * rows * i, '0 1 0' * height * rows + '1 0 0' * item_width * rows, '0 0 0', sbt_highlight_alpha * scoreboard_itemstats_fade_alpha, DRAWFLAG_NORMAL);
1836 for (int i = 0; i < rows; ++i)
1837 drawfill(pos + '0 1 0' * item_height + '0 1 0' * height * i, '1 0 0' * panel_size.x + '0 1 0' * fontsize, rgb, sbt_highlight_alpha * scoreboard_itemstats_fade_alpha, DRAWFLAG_NORMAL);
1841 pos.x += item_width / 2;
1843 float oldposx = pos.x;
1847 IL_EACH(default_order_items, !(autocvar_hud_panel_scoreboard_itemstats_filter && it.uninteresting), {
1848 int n = g_inventory.inv_items[it.m_id];
1849 //n = 1 + floor(i * 3 + 4.8) % 7; // debug: display a value for each item
1850 if (n <= 0) continue;
1851 drawpic_aspect_skin(tmpos, it.m_icon, '1 0 0' * item_width + '0 1 0' * item_height, '1 1 1', panel_fg_alpha * scoreboard_itemstats_fade_alpha, DRAWFLAG_NORMAL);
1853 float padding = (item_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center
1854 drawstring(tmpos + '1 0 0' * padding + '0 1 0' * item_height, s, '1 1 0' * fontsize, '1 1 1', panel_fg_alpha * scoreboard_itemstats_fade_alpha, DRAWFLAG_NORMAL);
1855 tmpos.x += item_width * rows;
1856 pos.x += item_width * rows;
1857 if (rows == 2 && column == columnns - 1) {
1865 panel_size.x += panel_bg_padding * 2; // restore initial width
1870 vector MapStats_DrawKeyValue(vector pos, string key, string value) {
1872 pos.x += hud_fontsize.x * 0.25;
1873 drawstring(pos, key, hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
1874 pos.x = panel_pos.x + panel_size.x - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1875 drawstring(pos, value, hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
1877 pos.y += hud_fontsize.y;
1883 vector Scoreboard_MapStats_Draw(vector pos, vector rgb, vector bg_size) {
1884 float stat_secrets_found, stat_secrets_total;
1885 float stat_monsters_killed, stat_monsters_total;
1889 // get monster stats
1890 stat_monsters_killed = STAT(MONSTERS_KILLED);
1891 stat_monsters_total = STAT(MONSTERS_TOTAL);
1893 // get secrets stats
1894 stat_secrets_found = STAT(SECRETS_FOUND);
1895 stat_secrets_total = STAT(SECRETS_TOTAL);
1897 // get number of rows
1898 if(stat_secrets_total)
1900 if(stat_monsters_total)
1903 // if no rows, return
1907 // draw table header
1908 drawstring(pos + eX * panel_bg_padding, _("Map stats:"), hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1909 pos.y += 1.25 * hud_fontsize.y;
1910 if(panel.current_panel_bg != "0")
1911 pos.y += panel_bg_border;
1914 panel_size.y = hud_fontsize.y * rows;
1915 panel_size.y += panel_bg_padding * 2;
1918 vector end_pos = panel_pos + eY * (panel_size.y + 0.5 * hud_fontsize.y);
1919 if(panel.current_panel_bg != "0")
1920 end_pos.y += panel_bg_border * 2;
1922 if(panel_bg_padding)
1924 panel_pos += '1 1 0' * panel_bg_padding;
1925 panel_size -= '2 2 0' * panel_bg_padding;
1929 vector tmp = panel_size;
1932 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL);
1935 if(stat_monsters_total)
1937 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1938 pos = MapStats_DrawKeyValue(pos, _("Monsters killed:"), val);
1942 if(stat_secrets_total)
1944 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1945 pos = MapStats_DrawKeyValue(pos, _("Secrets found:"), val);
1948 panel_size.x += panel_bg_padding * 2; // restore initial width
1953 vector Scoreboard_Rankings_Draw(vector pos, string ranktitle, entity pl, vector rgb, vector bg_size)
1956 RANKINGS_RECEIVED_CNT = 0;
1957 for (i=RANKINGS_CNT-1; i>=0; --i)
1959 ++RANKINGS_RECEIVED_CNT;
1961 if (RANKINGS_RECEIVED_CNT == 0)
1964 vector hl_rgb = rgb + '0.5 0.5 0.5';
1966 pos.y += hud_fontsize.y;
1967 drawstring(pos + eX * panel_bg_padding, ranktitle, hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1968 pos.y += 1.25 * hud_fontsize.y;
1969 if(panel.current_panel_bg != "0")
1970 pos.y += panel_bg_border;
1975 for(i = 0; i < RANKINGS_RECEIVED_CNT; ++i)
1977 float f = stringwidth(ColorTranslateRGB(grecordholder[i]), true, hud_fontsize);
1982 if(namesize > autocvar_hud_panel_scoreboard_namesize * hud_fontsize.x)
1984 namesize = autocvar_hud_panel_scoreboard_namesize * hud_fontsize.x;
1988 float ranksize = 3 * hud_fontsize.x;
1989 float timesize = 5 * hud_fontsize.x;
1990 vector columnsize = vec2(ranksize + timesize + namesize + hud_fontsize.x, 1.25 * hud_fontsize.y);
1991 int columns = max(1, floor((panel_size.x - 2 * panel_bg_padding) / columnsize.x));
1992 columns = min(columns, RANKINGS_RECEIVED_CNT);
1994 // expand name column to fill the entire row
1995 float available_space = (panel_size.x - 2 * panel_bg_padding - columnsize.x * columns) / columns;
1996 namesize += available_space;
1997 columnsize.x += available_space;
1999 panel_size.y = ceil(RANKINGS_RECEIVED_CNT / columns) * 1.25 * hud_fontsize.y;
2000 panel_size.y += panel_bg_padding * 2;
2004 vector end_pos = panel_pos + eY * (panel_size.y + 0.5 * hud_fontsize.y);
2005 if(panel.current_panel_bg != "0")
2006 end_pos.y += panel_bg_border * 2;
2008 if(panel_bg_padding)
2010 panel_pos += '1 1 0' * panel_bg_padding;
2011 panel_size -= '2 2 0' * panel_bg_padding;
2017 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, panel_size, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL);
2019 vector text_ofs = vec2(0.5 * hud_fontsize.x, (1.25 - 1) / 2 * hud_fontsize.y); // center text vertically
2021 int column = 0, j = 0;
2022 string zoned_name_self = strzone(strdecolorize(entcs_GetName(player_localnum)));
2023 for(i = 0; i < RANKINGS_RECEIVED_CNT; ++i)
2030 if(strdecolorize(grecordholder[i]) == zoned_name_self)
2031 drawfill(pos, columnsize, hl_rgb, sbt_highlight_alpha_self, DRAWFLAG_NORMAL);
2032 else if(!((j + column) & 1) && sbt_highlight)
2033 drawfill(pos, columnsize, hl_rgb, sbt_highlight_alpha, DRAWFLAG_NORMAL);
2035 str = count_ordinal(i+1);
2036 drawstring(pos + text_ofs, str, hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
2037 drawstring(pos + text_ofs + eX * ranksize, TIME_ENCODED_TOSTRING(t), hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
2038 str = ColorTranslateRGB(grecordholder[i]);
2040 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
2041 drawcolorcodedstring(pos + text_ofs + eX * (ranksize + timesize), str, hud_fontsize, sbt_fg_alpha, DRAWFLAG_NORMAL);
2043 pos.y += 1.25 * hud_fontsize.y;
2045 if(j >= ceil(RANKINGS_RECEIVED_CNT / columns))
2049 pos.x += panel_size.x / columns;
2050 pos.y = panel_pos.y;
2053 strfree(zoned_name_self);
2055 panel_size.x += panel_bg_padding * 2; // restore initial width
2059 float scoreboard_time;
2060 bool have_weapon_stats;
2061 bool Scoreboard_AccuracyStats_WouldDraw(float ypos)
2063 if (MUTATOR_CALLHOOK(DrawScoreboardAccuracy))
2065 if (!autocvar_hud_panel_scoreboard_accuracy || warmup_stage || ypos > 0.91 * vid_conheight)
2068 if (time < scoreboard_time + autocvar_hud_panel_scoreboard_accuracy_showdelay
2069 && ypos > autocvar_hud_panel_scoreboard_accuracy_showdelay_minpos * vid_conheight
2075 if (!have_weapon_stats)
2077 FOREACH(Weapons, it != WEP_Null, {
2078 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
2079 if (weapon_stats >= 0)
2081 have_weapon_stats = true;
2085 if (!have_weapon_stats)
2092 bool have_item_stats;
2093 bool Scoreboard_ItemStats_WouldDraw(float ypos)
2095 if (MUTATOR_CALLHOOK(DrawScoreboardItemStats))
2097 if (!autocvar_hud_panel_scoreboard_itemstats || !g_inventory || warmup_stage || ypos > 0.91 * vid_conheight)
2100 if (time < scoreboard_time + autocvar_hud_panel_scoreboard_itemstats_showdelay
2101 && ypos > autocvar_hud_panel_scoreboard_itemstats_showdelay_minpos * vid_conheight
2107 if (!have_item_stats)
2109 IL_EACH(default_order_items, true, {
2110 if (!(autocvar_hud_panel_scoreboard_itemstats_filter && it.uninteresting))
2112 int q = g_inventory.inv_items[it.m_id];
2113 //q = 1; // debug: display all items
2116 have_item_stats = true;
2121 if (!have_item_stats)
2128 void Scoreboard_Draw()
2130 if(!autocvar__hud_configure)
2132 if(!hud_draw_maximized) return;
2134 // frametime checks allow to toggle the scoreboard even when the game is paused
2135 if(scoreboard_active) {
2136 if (scoreboard_fade_alpha == 0)
2137 scoreboard_time = time;
2138 if(hud_configure_menu_open == 1)
2139 scoreboard_fade_alpha = 1;
2140 float scoreboard_fadeinspeed = autocvar_hud_panel_scoreboard_fadeinspeed;
2141 if (scoreboard_fadeinspeed && frametime)
2142 scoreboard_fade_alpha = min(1, scoreboard_fade_alpha + frametime * scoreboard_fadeinspeed);
2144 scoreboard_fade_alpha = 1;
2145 if(hud_fontsize_str != autocvar_hud_fontsize)
2147 hud_fontsize = HUD_GetFontsize("hud_fontsize");
2148 Scoreboard_initFieldSizes();
2149 strcpy(hud_fontsize_str, autocvar_hud_fontsize);
2153 float scoreboard_fadeoutspeed = autocvar_hud_panel_scoreboard_fadeoutspeed;
2154 if (scoreboard_fadeoutspeed && frametime)
2155 scoreboard_fade_alpha = max(0, scoreboard_fade_alpha - frametime * scoreboard_fadeoutspeed);
2157 scoreboard_fade_alpha = 0;
2160 if (!scoreboard_fade_alpha)
2162 scoreboard_acc_fade_alpha = 0;
2163 scoreboard_itemstats_fade_alpha = 0;
2168 scoreboard_fade_alpha = 0;
2170 if (autocvar_hud_panel_scoreboard_dynamichud)
2173 HUD_Scale_Disable();
2175 if(scoreboard_fade_alpha <= 0)
2177 panel_fade_alpha *= scoreboard_fade_alpha;
2178 HUD_Panel_LoadCvars();
2180 sbt_bg_alpha = autocvar_hud_panel_scoreboard_table_bg_alpha * panel_fg_alpha;
2181 sbt_highlight = autocvar_hud_panel_scoreboard_table_highlight;
2182 sbt_highlight_alpha = autocvar_hud_panel_scoreboard_table_highlight_alpha * panel_fg_alpha;
2183 sbt_highlight_alpha_self = autocvar_hud_panel_scoreboard_table_highlight_alpha_self * panel_fg_alpha;
2184 sbt_highlight_alpha_eliminated = autocvar_hud_panel_scoreboard_table_highlight_alpha_eliminated * panel_fg_alpha;
2185 sbt_fg_alpha = autocvar_hud_panel_scoreboard_table_fg_alpha * panel_fg_alpha;
2186 sbt_fg_alpha_self = autocvar_hud_panel_scoreboard_table_fg_alpha_self * panel_fg_alpha;
2188 // don't overlap with con_notify
2189 if(!autocvar__hud_configure)
2190 panel_pos.y = max((autocvar_con_notify * autocvar_con_notifysize), panel_pos.y);
2192 float excess = max(0, max_namesize - autocvar_hud_panel_scoreboard_namesize * hud_fontsize.x);
2193 float fixed_scoreboard_width = bound(vid_conwidth * autocvar_hud_panel_scoreboard_minwidth, vid_conwidth - excess, vid_conwidth * 0.93);
2194 panel_pos.x = 0.5 * (vid_conwidth - fixed_scoreboard_width);
2195 panel_size.x = fixed_scoreboard_width;
2197 Scoreboard_UpdatePlayerTeams();
2199 float initial_pos_y = panel_pos.y;
2200 vector pos = panel_pos;
2205 vector sb_gameinfo_type_fontsize, sb_gameinfo_detail_fontsize;
2207 // Begin of Game Info Section
2208 sb_gameinfo_type_fontsize = hud_fontsize * 2.5;
2209 sb_gameinfo_detail_fontsize = hud_fontsize * 1.3;
2212 //drawcolorcodedstring(pos, "bienvenidoainternet.org", sb_gameinfo_type_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
2213 //drawpic_aspect(pos + '1 0 0' * (panel_size.x - 150), "gfx/bai_logo", vec2(150, sb_gameinfo_type_fontsize.y), '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
2214 //pos.y += sb_gameinfo_type_fontsize.y;
2216 // Game Info: Game Type
2217 str = MapInfo_Type_ToText(gametype);
2219 draw_beginBoldFont();
2220 //drawcolorcodedstring(pos + '0.5 0 0' * (panel_size.x - stringwidth(str, true, sb_gameinfo_type_fontsize)), str, sb_gameinfo_type_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
2221 drawcolorcodedstring(pos, str, sb_gameinfo_type_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
2224 vector tmp_old_sz = draw_getimagesize("gfx/bai_logo");
2225 float tmp_aspect = tmp_old_sz.x/tmp_old_sz.y;
2226 vector tmp_new_sz = vec2(sb_gameinfo_type_fontsize.y * tmp_aspect, sb_gameinfo_type_fontsize.y);
2228 drawpic(pos + '1 0 0' * (panel_size.x - tmp_new_sz.x), "gfx/bai_logo", tmp_new_sz, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
2230 pos.y += sb_gameinfo_type_fontsize.y;
2233 drawcolorcodedstring(pos + '0.5 0 0' * (panel_size.x - stringwidth_colors(hostname_full, sb_gameinfo_detail_fontsize)), hostname_full, sb_gameinfo_detail_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
2235 pos.y += sb_gameinfo_detail_fontsize.y;
2237 // Game Info: Game Detail
2238 float tl = STAT(TIMELIMIT);
2239 float fl = STAT(FRAGLIMIT);
2240 float ll = STAT(LEADLIMIT);
2241 float ll_and_fl = STAT(LEADLIMIT_AND_FRAGLIMIT);
2244 str = strcat(str, sprintf(_("^3%1.0f minutes"), tl));
2245 if(!gametype.m_hidelimits)
2250 str = strcat(str, "^7 / "); // delimiter
2253 str = strcat(str, sprintf(_("^5%s %s"), ScoreString(teamscores_flags(ts_primary), fl),
2254 (teamscores_label(ts_primary) == "score") ? CTX(_("SCO^points")) :
2255 (teamscores_label(ts_primary) == "fastest") ? "" :
2256 TranslateScoresLabel(teamscores_label(ts_primary))));
2260 str = strcat(str, sprintf(_("^5%s %s"), ScoreString(scores_flags(ps_primary), fl),
2261 (scores_label(ps_primary) == "score") ? CTX(_("SCO^points")) :
2262 (scores_label(ps_primary) == "fastest") ? "" :
2263 TranslateScoresLabel(scores_label(ps_primary))));
2268 if(tl > 0 || fl > 0)
2271 if (ll_and_fl && fl > 0)
2272 str = strcat(str, "^7 & ");
2274 str = strcat(str, "^7 / ");
2279 str = strcat(str, sprintf(_("^2+%s %s"), ScoreString(teamscores_flags(ts_primary), ll),
2280 (teamscores_label(ts_primary) == "score") ? CTX(_("SCO^points")) :
2281 (teamscores_label(ts_primary) == "fastest") ? "" :
2282 TranslateScoresLabel(teamscores_label(ts_primary))));
2286 str = strcat(str, sprintf(_("^2+%s %s"), ScoreString(scores_flags(ps_primary), ll),
2287 (scores_label(ps_primary) == "score") ? CTX(_("SCO^points")) :
2288 (scores_label(ps_primary) == "fastest") ? "" :
2289 TranslateScoresLabel(scores_label(ps_primary))));
2294 drawcolorcodedstring(pos + '1 0 0' * (panel_size.x - stringwidth(str, true, sb_gameinfo_detail_fontsize)), str, sb_gameinfo_detail_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL); // align right
2296 str = sprintf(_("^7Map: ^2%s"), shortmapname);
2297 drawcolorcodedstring(pos, str, sb_gameinfo_detail_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL); // align left
2298 // End of Game Info Section
2300 pos.y += sb_gameinfo_detail_fontsize.y + hud_fontsize.y * 0.3; // space between Game Info Section and score table
2301 if(panel.current_panel_bg != "0")
2302 pos.y += panel_bg_border;
2304 // Draw the scoreboard
2305 float scale = autocvar_hud_panel_scoreboard_table_bg_scale;
2308 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * scale;
2312 vector panel_bg_color_save = panel_bg_color;
2313 vector team_score_baseoffset;
2314 vector team_size_baseoffset;
2315 if (autocvar_hud_panel_scoreboard_team_size_position != 1) // team size not on left
2317 // put team score to the left of scoreboard (and team size to the right)
2318 team_score_baseoffset = eY * hud_fontsize.y - eX * hud_fontsize.x * 1.5;
2319 team_size_baseoffset = eY * hud_fontsize.y + eX * hud_fontsize.x * 0.5;
2320 if(panel.current_panel_bg != "0")
2322 team_score_baseoffset.x -= panel_bg_border;
2323 team_size_baseoffset.x += panel_bg_border;
2328 // put team score to the right of scoreboard (and team size to the left)
2329 team_score_baseoffset = eY * hud_fontsize.y + eX * hud_fontsize.x * 1.5;
2330 team_size_baseoffset = eY * hud_fontsize.y - eX * hud_fontsize.x * 0.5;
2331 if(panel.current_panel_bg != "0")
2333 team_score_baseoffset.x += panel_bg_border;
2334 team_size_baseoffset.x -= panel_bg_border;
2338 int team_size_total = 0;
2339 if (autocvar_hud_panel_scoreboard_team_size_position != 0) // team size not off
2341 // calculate team size total (sum of all team sizes)
2342 for(tm = teams.sort_next; tm; tm = tm.sort_next)
2343 if(tm.team != NUM_SPECTATOR)
2344 team_size_total += tm.team_size;
2347 for(tm = teams.sort_next; tm; tm = tm.sort_next)
2349 if(tm.team == NUM_SPECTATOR)
2354 vector rgb = Team_ColorRGB(tm.team);
2355 /*draw_beginBoldFont();
2356 str = ftos(tm.(teamscores(ts_primary)));
2357 if (autocvar_hud_panel_scoreboard_team_size_position != 1) // team size not on left
2359 // team score on the left (default)
2360 str_pos = pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 3);
2364 // team score on the right
2365 str_pos = pos + team_score_baseoffset + eX * (panel_size.x + hud_fontsize.x * 3);
2367 drawstring(str_pos, str, hud_fontsize * 3, rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
2369 // team size (if set to show on the side)
2370 if (autocvar_hud_panel_scoreboard_team_size_position != 0) // team size not off
2372 // calculate the starting position for the whole team size info string
2373 str = sprintf("%d/%d", tm.team_size, team_size_total);
2374 if (autocvar_hud_panel_scoreboard_team_size_position == 1)
2376 // team size on the left
2377 str_pos = pos + team_size_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5);
2381 // team size on the right
2382 str_pos = pos + team_size_baseoffset + eX * (panel_size.x + hud_fontsize.x * 1.5);
2384 str = sprintf("%d", tm.team_size);
2385 drawstring(str_pos, str, hud_fontsize * 1.5, rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
2386 str_pos += eX * stringwidth(str, true, hud_fontsize * 1.5) + eY * hud_fontsize.y * .5;
2387 str = sprintf("/%d", team_size_total);
2388 drawstring(str_pos, str, hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
2392 // secondary score, e.g. keyhunt
2393 if(ts_primary != ts_secondary)
2395 str = ftos(tm.(teamscores(ts_secondary)));
2396 if (autocvar_hud_panel_scoreboard_team_size_position != 1) // team size not on left
2399 str_pos = pos + team_score_baseoffset - vec2(stringwidth(str, false, hud_fontsize), hud_fontsize.y * -1.5);
2404 str_pos = pos + team_score_baseoffset + vec2(panel_size.x + hud_fontsize.x * 1.5, hud_fontsize.y * 1.5);
2407 drawstring(str_pos, str, hud_fontsize, rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
2412 // z411 My team header
2414 drawfill(pos, team_score_size, rgb * 0.5, sbt_highlight_alpha, DRAWFLAG_NORMAL);
2417 str = ftos(tm.(teamscores(ts_primary)));
2419 str_pos.x += (team_score_size.x / 2) - (stringwidth(str, true, team_score_fontsize) / 2);
2420 str_pos.y += (team_score_size.y / 2) - (team_score_fontsize.y / 2);
2422 draw_beginBoldFont();
2423 drawstring(str_pos, str, team_score_fontsize, rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
2427 str = Team_CustomName(tm.team);
2429 str_pos.x += team_score_size.x + team_name_fontsize.x * 0.5;
2430 str_pos.y += (team_score_size.y / 2) - (team_name_fontsize.y / 2);
2431 drawcolorcodedstring(str_pos, str, team_name_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
2433 pos.y += team_score_size.y + (hud_fontsize.y * 0.5);
2435 if(autocvar_hud_panel_scoreboard_bg_teams_color_team > 0)
2436 panel_bg_color = rgb * autocvar_hud_panel_scoreboard_bg_teams_color_team;
2437 else if(panel_bg_color_team > 0)
2438 panel_bg_color = rgb * panel_bg_color_team;
2440 panel_bg_color = rgb;
2441 pos = Scoreboard_MakeTable(pos, tm, panel_bg_color, bg_size);
2443 panel_bg_color = panel_bg_color_save;
2445 else if(gametype == MAPINFO_TYPE_DUEL)
2447 for(tm = teams.sort_next; tm; tm = tm.sort_next)
2448 if(tm.team != NUM_SPECTATOR)
2451 // z411 make DUEL TABLE
2452 pos = Scoreboard_MakeDuelTable(pos, tm, panel_bg_color, bg_size);
2456 for(tm = teams.sort_next; tm; tm = tm.sort_next)
2457 if(tm.team != NUM_SPECTATOR)
2460 // display it anyway
2461 pos = Scoreboard_MakeTable(pos, tm, panel_bg_color, bg_size);
2464 pos = Scoreboard_MedalStats_Draw(pos);
2466 if (Scoreboard_AccuracyStats_WouldDraw(pos.y))
2467 pos = Scoreboard_AccuracyStats_Draw(pos, panel_bg_color, bg_size);
2468 if (Scoreboard_ItemStats_WouldDraw(pos.y))
2469 pos = Scoreboard_ItemStats_Draw(pos, panel_bg_color, bg_size);
2471 if(MUTATOR_CALLHOOK(ShowRankings)) {
2472 string ranktitle = M_ARGV(0, string);
2473 if(race_speedaward) {
2474 drawcolorcodedstring(pos, sprintf(_("Speed award: %d%s ^7(%s^7)"), race_speedaward, race_speedaward_unit, ColorTranslateRGB(race_speedaward_holder)), hud_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
2475 pos.y += 1.25 * hud_fontsize.y;
2477 if(race_speedaward_alltimebest) {
2478 drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d%s ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_unit, ColorTranslateRGB(race_speedaward_alltimebest_holder)), hud_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
2479 pos.y += 1.25 * hud_fontsize.y;
2481 pos = Scoreboard_Rankings_Draw(pos, ranktitle, playerslots[player_localnum], panel_bg_color, bg_size);
2484 //pos = Scoreboard_MapStats_Draw(pos, panel_bg_color, bg_size);
2487 for(pl = players.sort_next; pl; pl = pl.sort_next)
2489 if(pl.team == NUM_SPECTATOR)
2491 for(tm = teams.sort_next; tm; tm = tm.sort_next)
2492 if(tm.team == NUM_SPECTATOR)
2494 str = sprintf("%s (%d)", _("Spectators"), tm.team_size);
2495 draw_beginBoldFont();
2496 drawstring(pos, str, hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
2498 pos.y += 1.25 * hud_fontsize.y;
2500 pos = Scoreboard_DrawOthers(pos, '0 0 0', pl.team, NULL, pl, 0);
2501 pos.y += 1.25 * hud_fontsize.y;
2508 // print information about respawn status
2509 float respawn_time = STAT(RESPAWN_TIME);
2513 if(respawn_time < 0)
2515 // a negative number means we are awaiting respawn, time value is still the same
2516 respawn_time *= -1; // remove mark now that we checked it
2518 if(respawn_time < time) // it happens for a few frames when server is respawning the player
2519 str = ""; // draw an empty string to not change suddenly scoreboard_bottom
2521 str = sprintf(_("^1Respawning in ^3%s^1..."),
2522 (autocvar_hud_panel_scoreboard_respawntime_decimals ?
2523 count_seconds_decs(respawn_time - time, autocvar_hud_panel_scoreboard_respawntime_decimals)
2525 count_seconds(ceil(respawn_time - time))
2529 else if(time < respawn_time)
2531 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
2532 (autocvar_hud_panel_scoreboard_respawntime_decimals ?
2533 count_seconds_decs(respawn_time - time, autocvar_hud_panel_scoreboard_respawntime_decimals)
2535 count_seconds(ceil(respawn_time - time))
2539 else if(time >= respawn_time)
2540 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
2542 pos.y += 1.2 * hud_fontsize.y;
2543 drawcolorcodedstring(pos + '0.5 0 0' * (panel_size.x - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
2546 pos.y += 2 * hud_fontsize.y;
2547 if (scoreboard_fade_alpha < 1)
2548 scoreboard_bottom = initial_pos_y + (pos.y - initial_pos_y) * scoreboard_fade_alpha;
2549 else if (pos.y != scoreboard_bottom)
2551 if (pos.y > scoreboard_bottom)
2552 scoreboard_bottom = min(pos.y, scoreboard_bottom + frametime * 10 * (pos.y - initial_pos_y));
2554 scoreboard_bottom = max(pos.y, scoreboard_bottom - frametime * 10 * (pos.y - initial_pos_y));