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 = " ";
114 int average_ping[NUM_TEAMS];
116 // mode 0: returns translated label
117 // mode 1: prints name and description of all the labels
118 string Label_getInfo(string label, int mode)
121 label = "bckills"; // first case in the switch
125 case "bckills": if (!mode) return CTX(_("SCO^bckills")); else LOG_HELP(strcat("^3", "bckills", " ^7", _("Number of ball carrier kills")));
126 case "bctime": if (!mode) return CTX(_("SCO^bctime")); else LOG_HELP(strcat("^3", "bctime", " ^7", _("Total amount of time holding the ball in Keepaway")));
127 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")));
128 case "captime": if (!mode) return CTX(_("SCO^captime")); else LOG_HELP(strcat("^3", "captime", " ^7", _("Time of fastest capture (CTF)")));
129 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
130 case "deaths": if (!mode) return CTX(_("SCO^deaths")); else LOG_HELP(strcat("^3", "deaths", " ^7", _("Number of deaths")));
131 case "destroyed": if (!mode) return CTX(_("SCO^destroyed")); else LOG_HELP(strcat("^3", "destroyed", " ^7", _("Number of keys destroyed by pushing them into void")));
132 case "dmg": if (!mode) return CTX(_("SCO^damage")); else LOG_HELP(strcat("^3", "dmg", " ^7", _("The total damage done")));
133 case "dmgtaken": if (!mode) return CTX(_("SCO^dmgtaken")); else LOG_HELP(strcat("^3", "dmgtaken", " ^7", _("The total damage taken")));
134 case "drops": if (!mode) return CTX(_("SCO^drops")); else LOG_HELP(strcat("^3", "drops", " ^7", _("Number of flag drops")));
135 case "elo": if (!mode) return CTX(_("SCO^elo")); else LOG_HELP(strcat("^3", "elo", " ^7", _("Player ELO")));
136 case "fastest": if (!mode) return CTX(_("SCO^fastest")); else LOG_HELP(strcat("^3", "fastest", " ^7", _("Time of fastest lap (Race/CTS)")));
137 case "faults": if (!mode) return CTX(_("SCO^faults")); else LOG_HELP(strcat("^3", "faults", " ^7", _("Number of faults committed")));
138 case "fckills": if (!mode) return CTX(_("SCO^fckills")); else LOG_HELP(strcat("^3", "fckills", " ^7", _("Number of flag carrier kills")));
139 case "fps": if (!mode) return CTX(_("SCO^fps")); else LOG_HELP(strcat("^3", "fps", " ^7", _("FPS")));
140 case "frags": if (!mode) return CTX(_("SCO^frags")); else LOG_HELP(strcat("^3", "frags", " ^7", _("Number of kills minus suicides")));
141 case "goals": if (!mode) return CTX(_("SCO^goals")); else LOG_HELP(strcat("^3", "goals", " ^7", _("Number of goals scored")));
142 case "kckills": if (!mode) return CTX(_("SCO^kckills")); else LOG_HELP(strcat("^3", "kckills", " ^7", _("Number of keys carrier kills")));
143 case "kd": if (!mode) return CTX(_("SCO^k/d")); else LOG_HELP(strcat("^3", "kd", " ^7", _("The kill-death ratio")));
144 case "kdr": if (!mode) return CTX(_("SCO^kdr")); else LOG_HELP(strcat("^3", "kdr", " ^7", _("The kill-death ratio")));
145 case "kdratio": if (!mode) return CTX(_("SCO^kdratio")); else LOG_HELP(strcat("^3", "kdratio", " ^7", _("The kill-death ratio")));
146 case "kills": if (!mode) return CTX(_("SCO^kills")); else LOG_HELP(strcat("^3", "kills", " ^7", _("Number of kills")));
147 case "laps": if (!mode) return CTX(_("SCO^laps")); else LOG_HELP(strcat("^3", "laps", " ^7", _("Number of laps finished (Race/CTS)")));
148 case "lives": if (!mode) return CTX(_("SCO^lives")); else LOG_HELP(strcat("^3", "lives", " ^7", _("Number of lives (LMS)")));
149 case "losses": if (!mode) return CTX(_("SCO^losses")); else LOG_HELP(strcat("^3", "losses", " ^7", _("Number of times a key was lost")));
150 case "name": if (!mode) return CTX(_("SCO^name")); else LOG_HELP(strcat("^3", "name", " ^7", _("Player name")));
151 case "nick": if (!mode) return CTX(_("SCO^nick")); else LOG_HELP(strcat("^3", "nick", " ^7", _("Player name")));
152 case "objectives": if (!mode) return CTX(_("SCO^objectives")); else LOG_HELP(strcat("^3", "objectives", " ^7", _("Number of objectives destroyed")));
153 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")));
154 case "ping": if (!mode) return CTX(_("SCO^ping")); else LOG_HELP(strcat("^3", "ping", " ^7", _("Ping time")));
155 case "pl": if (!mode) return CTX(_("SCO^pl")); else LOG_HELP(strcat("^3", "pl", " ^7", _("Packet loss")));
156 case "pushes": if (!mode) return CTX(_("SCO^pushes")); else LOG_HELP(strcat("^3", "pushes", " ^7", _("Number of players pushed into void")));
157 case "rank": if (!mode) return CTX(_("SCO^rank")); else LOG_HELP(strcat("^3", "rank", " ^7", _("Player rank")));
158 case "returns": if (!mode) return CTX(_("SCO^returns")); else LOG_HELP(strcat("^3", "returns", " ^7", _("Number of flag returns")));
159 case "revivals": if (!mode) return CTX(_("SCO^revivals")); else LOG_HELP(strcat("^3", "revivals", " ^7", _("Number of revivals")));
160 case "rounds": if (!mode) return CTX(_("SCO^rounds won")); else LOG_HELP(strcat("^3", "rounds", " ^7", _("Number of rounds won")));
161 case "score": if (!mode) return CTX(_("SCO^score")); else LOG_HELP(strcat("^3", "score", " ^7", _("Total score")));
162 case "suicides": if (!mode) return CTX(_("SCO^suicides")); else LOG_HELP(strcat("^3", "suicides", " ^7", _("Number of suicides")));
163 case "sum": if (!mode) return CTX(_("SCO^sum")); else LOG_HELP(strcat("^3", "sum", " ^7", _("Number of kills minus deaths")));
164 case "takes": if (!mode) return CTX(_("SCO^takes")); else LOG_HELP(strcat("^3", "takes", " ^7", _("Number of domination points taken (Domination)")));
165 case "teamkills": if (!mode) return CTX(_("SCO^teamkills")); else LOG_HELP(strcat("^3", "teamkills", " ^7", _("Number of teamkills")));
166 case "ticks": if (!mode) return CTX(_("SCO^ticks")); else LOG_HELP(strcat("^3", "ticks", " ^7", _("Number of ticks (Domination)")));
167 case "time": if (!mode) return CTX(_("SCO^time")); else LOG_HELP(strcat("^3", "time", " ^7", _("Total time raced (Race/CTS)")));
168 default: return label;
173 void PrintScoresLabels() { Label_getInfo(string_null, 1); }
174 string TranslateScoresLabel(string label) { return Label_getInfo(label, 0); }
176 #define SB_EXTRA_SORTING_FIELDS 5
177 PlayerScoreField sb_extra_sorting_field[SB_EXTRA_SORTING_FIELDS];
178 void Scoreboard_InitScores()
182 ps_primary = ps_secondary = NULL;
183 ts_primary = ts_secondary = -1;
184 FOREACH(Scores, true, {
185 f = (scores_flags(it) & SFL_SORT_PRIO_MASK);
186 if(f == SFL_SORT_PRIO_PRIMARY)
188 if(f == SFL_SORT_PRIO_SECONDARY)
190 if(ps_primary == it || ps_secondary == it)
192 if (scores_label(it) == "kills") sb_extra_sorting_field[0] = it;
193 if (scores_label(it) == "deaths") sb_extra_sorting_field[1] = it;
194 if (scores_label(it) == "suicides") sb_extra_sorting_field[2] = it;
195 if (scores_label(it) == "dmg") sb_extra_sorting_field[3] = it;
196 if (scores_label(it) == "dmgtaken") sb_extra_sorting_field[4] = it;
198 if(ps_secondary == NULL)
199 ps_secondary = ps_primary;
201 for(i = 0; i < MAX_TEAMSCORE; ++i)
203 f = (teamscores_flags(i) & SFL_SORT_PRIO_MASK);
204 if(f == SFL_SORT_PRIO_PRIMARY)
206 if(f == SFL_SORT_PRIO_SECONDARY)
209 if(ts_secondary == -1)
210 ts_secondary = ts_primary;
212 Cmd_Scoreboard_SetFields(0);
216 void Scoreboard_UpdatePlayerTeams()
220 for(pl = players.sort_next; pl; pl = pl.sort_next)
223 int Team = entcs_GetScoreTeam(pl.sv_entnum);
224 if(SetTeam(pl, Team))
227 Scoreboard_UpdatePlayerPos(pl);
231 pl = players.sort_next;
236 print(strcat("PNUM: ", ftos(num), "\n"));
241 int Scoreboard_CompareScore(int vl, int vr, int f)
243 TC(int, vl); TC(int, vr); TC(int, f);
244 if(f & SFL_ZERO_IS_WORST)
246 if(vl == 0 && vr != 0)
248 if(vl != 0 && vr == 0)
252 return IS_INCREASING(f);
254 return IS_DECREASING(f);
258 float Scoreboard_ComparePlayerScores(entity left, entity right)
260 int vl = (left.gotscores) ? entcs_GetTeam(left.sv_entnum) : NUM_SPECTATOR;
261 int vr = (right.gotscores) ? entcs_GetTeam(right.sv_entnum) : NUM_SPECTATOR;
268 if(vl == NUM_SPECTATOR)
270 // FIRST the one with scores (spectators), THEN the ones without (downloaders)
272 if(!left.gotscores && right.gotscores)
279 for (int i = -2; i < SB_EXTRA_SORTING_FIELDS; ++i)
283 if (!fld) fld = ps_primary;
284 else if (ps_secondary == ps_primary) continue;
285 else fld = ps_secondary;
289 fld = sb_extra_sorting_field[i];
290 if (fld == ps_primary || fld == ps_secondary) continue;
294 r = Scoreboard_CompareScore(left.scores(fld), right.scores(fld), scores_flags(fld));
295 if (r >= 0) return r;
298 if (left.sv_entnum < right.sv_entnum)
304 void Scoreboard_UpdatePlayerPos(entity player)
307 for(ent = player.sort_next; ent && Scoreboard_ComparePlayerScores(player, ent); ent = player.sort_next)
309 SORT_SWAP(player, ent);
311 for(ent = player.sort_prev; ent != players && Scoreboard_ComparePlayerScores(ent, player); ent = player.sort_prev)
313 SORT_SWAP(ent, player);
317 float Scoreboard_CompareTeamScores(entity left, entity right)
319 if(left.team == NUM_SPECTATOR)
321 if(right.team == NUM_SPECTATOR)
326 for(int i = -2; i < MAX_TEAMSCORE; ++i)
330 if (fld_idx == -1) fld_idx = ts_primary;
331 else if (ts_secondary == ts_primary) continue;
332 else fld_idx = ts_secondary;
337 if (fld_idx == ts_primary || fld_idx == ts_secondary) continue;
340 r = Scoreboard_CompareScore(left.teamscores(fld_idx), right.teamscores(fld_idx), teamscores_flags(fld_idx));
341 if (r >= 0) return r;
344 if (left.team < right.team)
350 void Scoreboard_UpdateTeamPos(entity Team)
353 for(ent = Team.sort_next; ent && Scoreboard_CompareTeamScores(Team, ent); ent = Team.sort_next)
355 SORT_SWAP(Team, ent);
357 for(ent = Team.sort_prev; ent != teams && Scoreboard_CompareTeamScores(ent, Team); ent = Team.sort_prev)
359 SORT_SWAP(ent, Team);
363 void Cmd_Scoreboard_Help()
365 LOG_HELP(_("You can modify the scoreboard using the ^2scoreboard_columns_set command."));
366 LOG_HELP(_("Usage:"));
367 LOG_HELP("^2scoreboard_columns_set ^3default");
368 LOG_HELP(_("^2scoreboard_columns_set ^3field1 field2 ..."));
369 LOG_HELP(_("^2scoreboard_columns_set ^7without arguments reads the arguments from the cvar scoreboard_columns"));
370 LOG_HELP(_(" ^5Note: ^7scoreboard_columns_set without arguments is executed on every map start"));
371 LOG_HELP(_("^2scoreboard_columns_set ^3expand_default ^7loads default layout and expands it into the cvar scoreboard_columns so you can edit it"));
372 LOG_HELP(_("You can use a ^3|^7 to start the right-aligned fields."));
373 LOG_HELP(_("The following field names are recognized (case insensitive):"));
379 LOG_HELP(_("Before a field you can put a + or - sign, then a comma separated list\n"
380 "of game types, then a slash, to make the field show up only in these\n"
381 "or in all but these game types. You can also specify 'all' as a\n"
382 "field to show all fields available for the current game mode."));
385 LOG_HELP(_("The special game type names 'teams' and 'noteams' can be used to\n"
386 "include/exclude ALL teams/noteams game modes."));
389 LOG_HELP(_("Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4"));
390 LOG_HELP(_("will display name, ping and pl aligned to the left, and the fields\n"
391 "right of the vertical bar aligned to the right."));
392 LOG_HELP(_("'field3' will only be shown in CTF, and 'field4' will be shown in all\n"
393 "other gamemodes except DM."));
396 // NOTE: adding a gametype with ? to not warn for an optional field
397 // make sure it's excluded in a previous exclusive rule, if any
398 // otherwise the previous exclusive rule warns anyway
399 // e.g. -teams,rc,cts,lms/kills ?+rc/kills
400 #define SCOREBOARD_DEFAULT_COLUMNS \
401 "ping pl fps cn name |" \
402 " -teams,rc,cts,inv,lms/kills +ft,tdm/kills ?+rc,inv/kills" \
403 " -teams,lms/deaths +ft,tdm/deaths" \
405 " -teams,lms,rc,cts,inv,ka/suicides +ft,tdm/suicides ?+rc,inv/suicides" \
406 " -cts,dm,tdm,ka,ft/frags" /* tdm already has this in "score" */ \
407 " +tdm,ft,dom,ons,as/teamkills"\
408 " -rc,cts,nb/dmg -rc,cts,nb/dmgtaken" \
409 " +ctf/pickups +ctf/fckills +ctf/returns +ctf/caps +ons/takes +ons/caps" \
410 " +lms/lives +lms/rank" \
411 " +kh/kckills +kh/losses +kh/caps" \
412 " ?+rc/laps ?+rc/time +rc,cts/fastest" \
413 " +as/objectives +nb/faults +nb/goals" \
414 " +ka/pickups +ka/bckills +ka/bctime +ft/revivals" \
415 " +dom/ticks +dom/takes" \
416 " -lms,rc,cts,inv,nb/score"
418 void Cmd_Scoreboard_SetFields(int argc)
423 bool have_name = false, have_primary = false, have_secondary = false, have_separator = false;
427 return; // do nothing, we don't know gametype and scores yet
429 // sbt_fields uses strunzone on the titles!
430 if(!sbt_field_title[0])
431 for(i = 0; i < MAX_SBT_FIELDS; ++i)
432 sbt_field_title[i] = strzone("(null)");
434 // TODO: re enable with gametype dependant cvars?
435 if(argc < 3) // no arguments provided
436 argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " ");
439 argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " ");
443 if(argv(2) == "default" || argv(2) == "expand_default")
445 if(argv(2) == "expand_default")
446 cvar_set("scoreboard_columns", SCOREBOARD_DEFAULT_COLUMNS);
447 argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " ");
449 else if(argv(2) == "all")
451 string s = "ping pl cn name |"; // scores without a label
452 FOREACH(Scores, true, {
454 if(it != ps_secondary)
455 if(scores_label(it) != "")
456 s = strcat(s, " ", scores_label(it));
458 if(ps_secondary != ps_primary)
459 s = strcat(s, " ", scores_label(ps_secondary));
460 s = strcat(s, " ", scores_label(ps_primary));
461 argc = tokenizebyseparator(strcat("0 1 ", s), " ");
468 hud_fontsize = HUD_GetFontsize("hud_fontsize");
470 duel_score_fontsize = hud_fontsize * 3;
471 duel_name_fontsize = hud_fontsize * 1.5;
472 duel_score_size = vec2(duel_score_fontsize.x * 1.5, duel_score_fontsize.y * 1.25);
474 team_score_fontsize = hud_fontsize * 2;
475 team_name_fontsize = hud_fontsize * 1.5;
476 team_score_size = vec2(team_score_fontsize.x * 1.5, team_score_fontsize.y * 1.25);
478 for(i = 1; i < argc - 1; ++i)
481 bool nocomplain = false;
482 if(substring(str, 0, 1) == "?")
485 str = substring(str, 1, strlen(str) - 1);
488 slash = strstrofs(str, "/", 0);
491 pattern = substring(str, 0, slash);
492 str = substring(str, slash + 1, strlen(str) - (slash + 1));
494 if (!isGametypeInFilter(gametype, teamplay, false, pattern))
498 str = strtolower(str);
499 strcpy(sbt_field_title[sbt_num_fields], TranslateScoresLabel(str));
500 sbt_field_size[sbt_num_fields] = stringwidth(sbt_field_title[sbt_num_fields], false, hud_fontsize);
505 // fields without a label (not networked via the score system)
506 case "ping": sbt_field[sbt_num_fields] = SP_PING; break;
507 case "pl": sbt_field[sbt_num_fields] = SP_PL; break;
508 case "cn": sbt_field[sbt_num_fields] = SP_COUNTRY; break; //LegendGuard adds cn label for Country column 05-04-2021
509 case "name": case "nick": sbt_field[sbt_num_fields] = SP_NAME; have_name = true; break;
510 case "|": sbt_field[sbt_num_fields] = SP_SEPARATOR; have_separator = true; break;
511 case "kd": case "kdr": case "kdratio": sbt_field[sbt_num_fields] = SP_KDRATIO; break;
512 case "sum": case "diff": case "k-d": sbt_field[sbt_num_fields] = SP_SUM; break;
513 case "cn": sbt_field[sbt_num_fields] = SP_COUNTRY; break; //LegendGuard adds cn label for Country column 05-04-2021
514 case "frags": sbt_field[sbt_num_fields] = SP_FRAGS; break;
515 default: // fields with a label
517 // map alternative labels
518 if (str == "damage") str = "dmg";
519 if (str == "damagetaken") str = "dmgtaken";
521 FOREACH(Scores, true, {
522 if (str == strtolower(scores_label(it))) {
524 goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
528 // NOTE: can't check STAT(SHOWFPS) here, if checked too early it returns false anyway
529 if(!nocomplain && str != "fps") // server can disable the fps field
530 LOG_INFOF("^1Error:^7 Unknown score field: '%s'", str);
532 strfree(sbt_field_title[sbt_num_fields]);
533 sbt_field_size[sbt_num_fields] = 0;
537 sbt_field[sbt_num_fields] = j;
540 if(j == ps_secondary)
541 have_secondary = true;
546 if(sbt_num_fields >= MAX_SBT_FIELDS)
550 if(scores_flags(ps_primary) & SFL_ALLOW_HIDE)
552 if(scores_flags(ps_secondary) & SFL_ALLOW_HIDE)
553 have_secondary = true;
554 if(ps_primary == ps_secondary)
555 have_secondary = true;
556 missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
558 if(sbt_num_fields + missing < MAX_SBT_FIELDS)
562 strfree(sbt_field_title[sbt_num_fields]);
563 for(i = sbt_num_fields; i > 0; --i)
565 sbt_field_title[i] = sbt_field_title[i-1];
566 sbt_field_size[i] = sbt_field_size[i-1];
567 sbt_field[i] = sbt_field[i-1];
569 sbt_field_title[0] = strzone(TranslateScoresLabel("name"));
570 sbt_field[0] = SP_NAME;
572 LOG_INFO("fixed missing field 'name'");
576 strfree(sbt_field_title[sbt_num_fields]);
577 for(i = sbt_num_fields; i > 1; --i)
579 sbt_field_title[i] = sbt_field_title[i-1];
580 sbt_field_size[i] = sbt_field_size[i-1];
581 sbt_field[i] = sbt_field[i-1];
583 sbt_field_title[1] = strzone("|");
584 sbt_field[1] = SP_SEPARATOR;
585 sbt_field_size[1] = stringwidth("|", false, hud_fontsize);
587 LOG_INFO("fixed missing field '|'");
590 else if(!have_separator)
592 strcpy(sbt_field_title[sbt_num_fields], "|");
593 sbt_field_size[sbt_num_fields] = stringwidth("|", false, hud_fontsize);
594 sbt_field[sbt_num_fields] = SP_SEPARATOR;
596 LOG_INFO("fixed missing field '|'");
600 strcpy(sbt_field_title[sbt_num_fields], TranslateScoresLabel(scores_label(ps_secondary)));
601 sbt_field_size[sbt_num_fields] = stringwidth(sbt_field_title[sbt_num_fields], false, hud_fontsize);
602 sbt_field[sbt_num_fields] = ps_secondary;
604 LOG_INFOF("fixed missing field '%s'", scores_label(ps_secondary));
608 strcpy(sbt_field_title[sbt_num_fields], TranslateScoresLabel(scores_label(ps_primary)));
609 sbt_field_size[sbt_num_fields] = stringwidth(sbt_field_title[sbt_num_fields], false, hud_fontsize);
610 sbt_field[sbt_num_fields] = ps_primary;
612 LOG_INFOF("fixed missing field '%s'", scores_label(ps_primary));
616 sbt_field[sbt_num_fields] = SP_END;
619 string Scoreboard_AddPlayerId(string pl_name, entity pl)
621 string pref = autocvar_hud_panel_scoreboard_playerid_prefix;
622 string suf = autocvar_hud_panel_scoreboard_playerid_suffix;
623 return strcat(pref, itos(pl.sv_entnum + 1), suf, pl_name);
627 vector sbt_field_rgb;
628 string sbt_field_icon0;
629 string sbt_field_icon1;
630 string sbt_field_icon2;
631 string sbt_field_icon3; //LegendGuard adds for Country player flags 05-04-2021
632 vector sbt_field_icon0_rgb;
633 vector sbt_field_icon1_rgb;
634 vector sbt_field_icon2_rgb;
635 string Scoreboard_GetName(entity pl)
637 if(ready_waiting && pl.ready)
639 sbt_field_icon0 = "gfx/scoreboard/player_ready";
643 int f = entcs_GetClientColors(pl.sv_entnum);
645 sbt_field_icon0 = "gfx/scoreboard/playercolor_base";
646 sbt_field_icon1 = "gfx/scoreboard/playercolor_shirt";
647 sbt_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
648 sbt_field_icon2 = "gfx/scoreboard/playercolor_pants";
649 sbt_field_icon2_rgb = colormapPaletteColor(f % 16, 1);
653 return entcs_GetName(pl.sv_entnum);
656 //LegendGuard adds GetCountrycode function 05-04-2021
657 string Scoreboard_GetCountrycode(entity pl)
659 int ccode = entcs_GetCountryCode(pl.sv_entnum);
661 sbt_field_icon3 = strcat("gfx/flags/", ftos(ccode));
663 sbt_field_icon3 = strcat("gfx/flags/", ftos(0)); //if user hasn't assigned country flag
666 //return ftos(entcs_GetCountryCode(pl.sv_entnum)); //returns a number
669 vector getPingColor(float f)
672 // 20-80 range is green
673 return '0 1 0' + '1 0 1' * max(0, min(60, f-20)) / 60;
675 // 80-300 range is red
676 return '1 1 1' - '0 1 1' * max(0, min(220, f-80)) / 220;
679 string Scoreboard_GetField(entity pl, PlayerScoreField field)
681 float tmp, num, denom;
684 sbt_field_rgb = '1 1 1';
685 sbt_field_icon0 = "";
686 sbt_field_icon1 = "";
687 sbt_field_icon2 = "";
688 sbt_field_icon3 = ""; //LegendGuard adds for Country column 05-04-2021
689 sbt_field_icon0_rgb = '1 1 1';
690 sbt_field_icon1_rgb = '1 1 1';
691 sbt_field_icon2_rgb = '1 1 1';
696 return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6 (Black Right-Pointing Triangle)
697 //str = getplayerkeyvalue(pl.sv_entnum, "ping");
701 sbt_field_rgb = getPingColor(f);
704 //return ftos(pl.team);
709 f = pl.ping_packetloss;
710 tmp = pl.ping_movementloss;
711 if(f == 0 && tmp == 0)
713 str = ftos(ceil(f * 100));
715 str = strcat(str, "~", ftos(ceil(tmp * 100)));
716 tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
717 sbt_field_rgb = '1 0.5 0.5' - '0 0.5 0.5' * tmp;
720 //LegendGuard adds Country REGISTER in the switch 05-04-2021
722 str = Scoreboard_GetCountrycode(pl);
725 //LegendGuard adds Country REGISTER in the switch 05-04-2021
727 str = Scoreboard_GetCountrycode(pl);
731 str = Scoreboard_GetName(pl);
732 if (autocvar_hud_panel_scoreboard_playerid)
733 str = Scoreboard_AddPlayerId(str, pl);
737 f = pl.(scores(SP_KILLS));
738 f -= pl.(scores(SP_SUICIDES));
742 num = pl.(scores(SP_KILLS));
743 denom = pl.(scores(SP_DEATHS));
746 sbt_field_rgb = '0 1 0';
747 str = sprintf("%d", num);
748 } else if(num <= 0) {
749 sbt_field_rgb = '1 0 0';
750 str = sprintf("%.1f", num/denom);
752 str = sprintf("%.1f", num/denom);
756 f = pl.(scores(SP_KILLS));
757 f -= pl.(scores(SP_DEATHS));
760 sbt_field_rgb = '0 1 0';
762 sbt_field_rgb = '1 1 1';
764 sbt_field_rgb = '1 0 0';
770 float elo = pl.(scores(SP_ELO));
772 case -1: return "...";
773 case -2: return _("N/A");
774 default: return ftos(elo);
780 float fps = pl.(scores(SP_FPS));
783 sbt_field_rgb = '1 1 1';
784 return ((pl.ping == 0) ? _("N/A") : "..."); // if 0 ping, either connecting or bot (either case can't show proper score)
787 //sbt_field_rgb = HUD_Get_Num_Color(fps, 200, true);
788 sbt_field_rgb = '1 0 0' + '0 1 1' * (bound(0, fps, 60) / 60);
792 case SP_DMG: case SP_DMGTAKEN:
793 return sprintf("%.1f k", pl.(scores(field)) / 1000);
795 default: case SP_SCORE:
796 tmp = pl.(scores(field));
797 f = scores_flags(field);
798 if(field == ps_primary)
799 sbt_field_rgb = '1 1 0';
800 else if(field == ps_secondary)
801 sbt_field_rgb = '0 1 1';
803 sbt_field_rgb = '1 1 1';
804 return ScoreString(f, tmp);
809 float sbt_fixcolumnwidth_len;
810 float sbt_fixcolumnwidth_iconlen;
811 float sbt_fixcolumnwidth_marginlen;
813 string Scoreboard_FixColumnWidth(int i, string str)
819 sbt_fixcolumnwidth_iconlen = 0;
821 if(sbt_field_icon0 != "")
823 sz = draw_getimagesize(sbt_field_icon0);
825 if(sbt_fixcolumnwidth_iconlen < f)
826 sbt_fixcolumnwidth_iconlen = f;
829 if(sbt_field_icon1 != "")
831 sz = draw_getimagesize(sbt_field_icon1);
833 if(sbt_fixcolumnwidth_iconlen < f)
834 sbt_fixcolumnwidth_iconlen = f;
837 if(sbt_field_icon2 != "")
839 sz = draw_getimagesize(sbt_field_icon2);
841 if(sbt_fixcolumnwidth_iconlen < f)
842 sbt_fixcolumnwidth_iconlen = f;
845 //LegendGuard adds conditional for Country column 05-04-2021
846 if(sbt_field_icon3 != "")
848 sz = draw_getimagesize(sbt_field_icon3);
850 if(sbt_fixcolumnwidth_iconlen < f)
851 sbt_fixcolumnwidth_iconlen = f;
854 if(sbt_fixcolumnwidth_iconlen != 0)
856 sbt_fixcolumnwidth_iconlen *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect
857 sbt_fixcolumnwidth_marginlen = stringwidth(" ", false, hud_fontsize);
860 sbt_fixcolumnwidth_marginlen = 0;
862 if(sbt_field[i] == SP_NAME) // name gets all remaining space
865 float remaining_space = 0;
866 for(j = 0; j < sbt_num_fields; ++j)
868 if (sbt_field[i] != SP_SEPARATOR)
869 remaining_space += sbt_field_size[j] + hud_fontsize.x;
870 sbt_field_size[i] = panel_size.x - remaining_space;
872 if (sbt_fixcolumnwidth_iconlen != 0)
873 remaining_space += sbt_fixcolumnwidth_marginlen + sbt_fixcolumnwidth_iconlen * hud_fontsize.x;
874 float namesize = panel_size.x - remaining_space;
875 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
876 sbt_fixcolumnwidth_len = stringwidth(str, true, hud_fontsize);
878 max_namesize = vid_conwidth - remaining_space;
881 sbt_fixcolumnwidth_len = stringwidth(str, false, hud_fontsize);
883 f = sbt_fixcolumnwidth_len + sbt_fixcolumnwidth_marginlen + sbt_fixcolumnwidth_iconlen * hud_fontsize.x;
884 if(sbt_field_size[i] < f)
885 sbt_field_size[i] = f;
890 void Scoreboard_initFieldSizes()
892 for(int i = 0; i < sbt_num_fields; ++i)
894 sbt_field_size[i] = stringwidth(sbt_field_title[i], false, hud_fontsize);
895 Scoreboard_FixColumnWidth(i, "");
899 vector Scoreboard_DrawHeader(vector pos, vector rgb, bool other_players, int team)
904 vector column_dim = eY * panel_size.y;
907 column_dim.y -= 1.25 * hud_fontsize.y;
908 vector text_offset = eY * (1.25 - 1) / 2 * hud_fontsize.y;
909 pos.x += hud_fontsize.x * 0.5;
910 for(i = 0; i < sbt_num_fields; ++i)
912 if(sbt_field[i] == SP_SEPARATOR)
915 vector text_offset_center = '0 0 0';
917 if(sbt_field[i] == SP_PING && teamplay) {
918 title_str = sprintf("(%d)", average_ping[Team_TeamToIndex(team) - 1]);
919 title_rgb = getPingColor(average_ping[Team_TeamToIndex(team) - 1]);
920 text_offset_center.x = sbt_field_size[i] - stringwidth(title_str, false, hud_fontsize);
922 title_str = sbt_field_title[i];
923 title_rgb = rgb * 1.5;
926 column_dim.x = sbt_field_size[i] + hud_fontsize.x;
929 drawfill(pos - eX * hud_fontsize.x * 0.5, column_dim, '0 0 0', sbt_highlight_alpha, DRAWFLAG_NORMAL);
930 drawstring(pos + text_offset + text_offset_center, title_str, hud_fontsize, title_rgb, sbt_fg_alpha, DRAWFLAG_NORMAL);
931 pos.x += column_dim.x;
933 if(sbt_field[i] == SP_SEPARATOR)
935 pos.x = panel_pos.x + panel_size.x - hud_fontsize.x * 0.5;
936 for(i = sbt_num_fields - 1; i > 0; --i)
938 if(sbt_field[i] == SP_SEPARATOR)
941 pos.x -= sbt_field_size[i];
946 column_dim.x = sbt_field_size[i] + hud_fontsize.x;
947 drawfill(pos - eX * hud_fontsize.x * 0.5, column_dim, '0 0 0', sbt_highlight_alpha, DRAWFLAG_NORMAL);
950 text_offset.x = sbt_field_size[i] - stringwidth(sbt_field_title[i], false, hud_fontsize);
951 drawstring(pos + text_offset, sbt_field_title[i], hud_fontsize, rgb * 1.5, sbt_fg_alpha, DRAWFLAG_NORMAL);
952 pos.x -= hud_fontsize.x;
957 pos.y += 1.25 * hud_fontsize.y;
961 void Scoreboard_DrawItem(vector item_pos, vector rgb, entity pl, bool is_self, int pl_number)
963 TC(bool, is_self); TC(int, pl_number);
965 bool is_spec = (entcs_GetSpecState(pl.sv_entnum) == ENTCS_SPEC_PURE);
967 vector h_pos = item_pos;
968 vector h_size = vec2(panel_size.x, hud_fontsize.y * 1.25);
969 // alternated rows highlighting
971 drawfill(h_pos, h_size, rgb, sbt_highlight_alpha_self, DRAWFLAG_NORMAL);
972 else if((sbt_highlight) && (!(pl_number % 2)))
973 drawfill(h_pos, h_size, rgb, sbt_highlight_alpha, DRAWFLAG_NORMAL);
975 float fg_alpha = (is_self ? sbt_fg_alpha_self : sbt_fg_alpha);
977 vector pos = item_pos;
978 // put a "self indicator" beside the self row, unicode U+25C0 (black left-pointing triangle)
980 drawstring(pos + eX * (panel_size.x + 0.5 * hud_fontsize.x) + eY, "\xE2\x97\x80", hud_fontsize, rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
982 pos.x += hud_fontsize.x * 0.5;
983 pos.y += (1.25 - 1) / 2 * hud_fontsize.y; // center text vertically
984 vector tmp = '0 0 0';
986 PlayerScoreField field;
987 for(i = 0; i < sbt_num_fields; ++i)
989 field = sbt_field[i];
990 if(field == SP_SEPARATOR)
993 if(is_spec && field != SP_NAME && field != SP_PING) {
994 pos.x += sbt_field_size[i] + hud_fontsize.x;
997 str = Scoreboard_GetField(pl, field);
998 str = Scoreboard_FixColumnWidth(i, str);
1000 pos.x += sbt_field_size[i] + hud_fontsize.x;
1002 if(field == SP_NAME) {
1003 tmp.x = sbt_field_size[i] - hud_fontsize.x * sbt_fixcolumnwidth_iconlen - sbt_fixcolumnwidth_marginlen + hud_fontsize.x;
1004 drawcolorcodedstring(pos - tmp, str, hud_fontsize, fg_alpha, DRAWFLAG_NORMAL);
1006 tmp.x = sbt_fixcolumnwidth_len + hud_fontsize.x;
1007 drawstring(pos - tmp, str, hud_fontsize, sbt_field_rgb, fg_alpha, DRAWFLAG_NORMAL);
1010 tmp.x = sbt_field_size[i] + hud_fontsize.x;
1011 if(sbt_field_icon0 != "")
1012 drawpic(pos - tmp, sbt_field_icon0, vec2(hud_fontsize.x * sbt_fixcolumnwidth_iconlen, hud_fontsize.y), sbt_field_icon1_rgb, fg_alpha, DRAWFLAG_NORMAL);
1013 if(sbt_field_icon1 != "")
1014 drawpic(pos - tmp, sbt_field_icon1, vec2(hud_fontsize.x * sbt_fixcolumnwidth_iconlen, hud_fontsize.y), sbt_field_icon1_rgb, fg_alpha, DRAWFLAG_NORMAL);
1015 if(sbt_field_icon2 != "")
1016 drawpic(pos - tmp, sbt_field_icon2, vec2(hud_fontsize.x * sbt_fixcolumnwidth_iconlen, hud_fontsize.y), sbt_field_icon2_rgb, fg_alpha, DRAWFLAG_NORMAL);
1017 if(sbt_field_icon3 != "") //LegendGuard adds conditional for Country column 05-04-2021
1018 drawpic(pos - tmp, sbt_field_icon3, vec2(hud_fontsize.x * sbt_fixcolumnwidth_iconlen, hud_fontsize.y), sbt_field_icon1_rgb, fg_alpha, DRAWFLAG_NORMAL);
1021 if(sbt_field[i] == SP_SEPARATOR)
1023 pos.x = item_pos.x + panel_size.x - hud_fontsize.x * 0.5;
1024 for(i = sbt_num_fields-1; i > 0; --i)
1026 field = sbt_field[i];
1027 if(field == SP_SEPARATOR)
1030 if(is_spec && field != SP_NAME && field != SP_PING) {
1031 pos.x -= sbt_field_size[i] + hud_fontsize.x;
1035 str = Scoreboard_GetField(pl, field);
1036 str = Scoreboard_FixColumnWidth(i, str);
1038 if(field == SP_NAME) {
1039 tmp.x = sbt_fixcolumnwidth_len; // left or right aligned? let's put it right...
1040 drawcolorcodedstring(pos - tmp, str, hud_fontsize, fg_alpha, DRAWFLAG_NORMAL);
1042 tmp.x = sbt_fixcolumnwidth_len;
1043 drawstring(pos - tmp, str, hud_fontsize, sbt_field_rgb, fg_alpha, DRAWFLAG_NORMAL);
1046 tmp.x = sbt_field_size[i];
1047 if(sbt_field_icon0 != "")
1048 drawpic(pos - tmp, sbt_field_icon0, vec2(hud_fontsize.x * sbt_fixcolumnwidth_iconlen, hud_fontsize.y), sbt_field_icon1_rgb, fg_alpha, DRAWFLAG_NORMAL);
1049 if(sbt_field_icon1 != "")
1050 drawpic(pos - tmp, sbt_field_icon1, vec2(hud_fontsize.x * sbt_fixcolumnwidth_iconlen, hud_fontsize.y), sbt_field_icon1_rgb, fg_alpha, DRAWFLAG_NORMAL);
1051 if(sbt_field_icon2 != "")
1052 drawpic(pos - tmp, sbt_field_icon2, vec2(hud_fontsize.x * sbt_fixcolumnwidth_iconlen, hud_fontsize.y), sbt_field_icon2_rgb, fg_alpha, DRAWFLAG_NORMAL);
1053 if(sbt_field_icon3 != "") //LegendGuard adds conditional for Country column 05-04-2021
1054 drawpic(pos - tmp, sbt_field_icon3, vec2(hud_fontsize.x * sbt_fixcolumnwidth_iconlen, hud_fontsize.y), sbt_field_icon1_rgb, fg_alpha, DRAWFLAG_NORMAL);
1055 pos.x -= sbt_field_size[i] + hud_fontsize.x;
1060 drawfill(h_pos, h_size, '0 0 0', sbt_highlight_alpha_eliminated, DRAWFLAG_NORMAL);
1063 vector Scoreboard_DrawOthers(vector item_pos, vector rgb, int this_team, entity ignored_pl, entity pl, int pl_number)
1066 vector h_pos = item_pos;
1067 vector h_size = vec2(panel_size.x, hud_fontsize.y * 1.25);
1070 bool complete = (this_team == NUM_SPECTATOR);
1073 if((sbt_highlight) && (!(pl_number % 2)))
1074 drawfill(h_pos, h_size, rgb, sbt_highlight_alpha, DRAWFLAG_NORMAL);
1076 vector pos = item_pos;
1077 pos.x += hud_fontsize.x * 0.5;
1078 pos.y += (1.25 - 1) / 2 * hud_fontsize.y; // center text vertically
1080 float width_limit = item_pos.x + panel_size.x - hud_fontsize.x;
1082 width_limit -= stringwidth("...", false, hud_fontsize);
1083 float namesize = autocvar_hud_panel_scoreboard_namesize * hud_fontsize.x;
1084 static float max_name_width = 0;
1086 float fieldsize = 0;
1087 float min_fieldsize = 0;
1088 float fieldpadding = hud_fontsize.x * 0.25;
1089 if(this_team == NUM_SPECTATOR)
1091 if(autocvar_hud_panel_scoreboard_spectators_showping)
1092 min_fieldsize = stringwidth("999", false, hud_fontsize);
1094 else if(autocvar_hud_panel_scoreboard_others_showscore)
1095 min_fieldsize = stringwidth("99", false, hud_fontsize);
1096 for(i = 0; pl; pl = pl.sort_next)
1098 if(pl.team != this_team)
1100 if(pl == ignored_pl)
1103 string flag_name = "";
1104 vector flag_size = '0 0 0';
1105 Scoreboard_GetField(pl, SP_COUNTRY);
1107 if(sbt_field_icon3 != "") {
1108 sz = draw_getimagesize(sbt_field_icon3);
1109 flag_name = sbt_field_icon3;
1110 flag_size = vec2(hud_fontsize.x * (sz.x / sz.y), hud_fontsize.y);
1114 if(this_team == NUM_SPECTATOR)
1116 if(autocvar_hud_panel_scoreboard_spectators_showping)
1117 field = Scoreboard_GetField(pl, SP_PING);
1119 else if(autocvar_hud_panel_scoreboard_others_showscore)
1120 field = Scoreboard_GetField(pl, SP_SCORE);
1122 string str = entcs_GetName(pl.sv_entnum);
1123 if (autocvar_hud_panel_scoreboard_playerid)
1124 str = Scoreboard_AddPlayerId(str, pl);
1125 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
1126 float column_width = stringwidth(str, true, hud_fontsize);
1127 if((this_team == NUM_SPECTATOR) && autocvar_hud_panel_scoreboard_spectators_aligned)
1129 if(column_width > max_name_width)
1130 max_name_width = column_width;
1131 column_width = max_name_width;
1135 fieldsize = stringwidth(field, false, hud_fontsize);
1136 column_width += hud_fontsize.x * 0.25 + max(fieldsize, min_fieldsize) + 2 * fieldpadding;
1139 if(pos.x + column_width > width_limit)
1144 drawstring(pos, "...", hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
1149 pos.x = item_pos.x + hud_fontsize.x * 0.5;
1150 pos.y += hud_fontsize.y * 1.25;
1154 if(flag_name != "") {
1155 drawpic(pos, flag_name, flag_size, sbt_field_icon1_rgb, sbt_fg_alpha, DRAWFLAG_NORMAL);
1156 pos.x += flag_size.x + hud_fontsize.x * 0.5;
1159 vector name_pos = pos;
1160 if((this_team == NUM_SPECTATOR) && autocvar_hud_panel_scoreboard_spectators_aligned)
1161 name_pos.x += max(fieldsize, min_fieldsize) + 2 * fieldpadding + hud_fontsize.x * 0.25;
1163 drawcolorcodedstring(name_pos, str, hud_fontsize, sbt_fg_alpha, DRAWFLAG_NORMAL);
1166 h_size.x = max(fieldsize, min_fieldsize) + 2 * fieldpadding;
1167 h_size.y = hud_fontsize.y;
1168 vector field_pos = pos;
1169 if(!((this_team == NUM_SPECTATOR) && autocvar_hud_panel_scoreboard_spectators_aligned))
1170 field_pos.x += column_width - h_size.x;
1172 drawfill(field_pos, h_size, '1 1 1', sbt_highlight_alpha, DRAWFLAG_NORMAL);
1173 field_pos.x += fieldpadding + (max(fieldsize, min_fieldsize) - fieldsize) * 0.5;
1174 drawstring(field_pos, field, hud_fontsize, sbt_field_rgb, sbt_fg_alpha, DRAWFLAG_NORMAL);
1178 h_size.x = column_width + hud_fontsize.x * 0.25;
1179 h_size.y = hud_fontsize.y;
1180 drawfill(pos - hud_fontsize.x * 0.25 * eX, h_size, '0 0 0', sbt_highlight_alpha_eliminated, DRAWFLAG_NORMAL);
1182 pos.x += column_width;
1183 pos.x += hud_fontsize.x;
1185 return vec2(item_pos.x, item_pos.y + i * hud_fontsize.y * 1.25);
1188 vector Scoreboard_DrawMedal(vector pos, string icon, float height, float number)
1190 if(!number) return pos;
1191 total_medals += number;
1193 vector tmp_sz, tmp_sz2;
1194 tmp_sz = draw_getimagesize(icon);
1195 tmp_sz2 = vec2(height*(tmp_sz.x/tmp_sz.y), height);
1196 string val = ftos(number);
1198 drawpic(pos, icon, tmp_sz2, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1200 pos.x += tmp_sz2.x + hud_fontsize.x * 0.25;
1201 drawstring(pos + eY * ((tmp_sz2.y - hud_fontsize.y) / 2), val, hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1203 pos.x += stringwidth(val, false, hud_fontsize) + hud_fontsize.x * 0.5;
1207 vector Scoreboard_Duel_DrawPickup(vector pos, bool skinned, string icon, vector sz, float number, bool invert)
1209 vector tmp_in = pos;
1210 vector tmp_sz, tmp_sz2;
1215 picpath = strcat(hud_skin_path, "/", icon);
1216 if(precache_pic(picpath) == "")
1217 picpath = strcat("gfx/hud/default/", icon);
1222 tmp_sz = draw_getimagesize(picpath);
1223 tmp_sz2 = vec2(sz.y*(tmp_sz.x/tmp_sz.y), sz.y);
1225 tmp_in.x = pos.x + ((sz.x - tmp_sz2.x) / 2);
1226 drawpic(tmp_in, picpath, tmp_sz2, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1230 tmp_in.x += tmp_sz2.x + hud_fontsize.x * 0.25;
1232 tmp_in.x -= hud_fontsize.x * 0.25 + hud_fontsize.x;
1234 tmp_in.y += (tmp_sz2.y - hud_fontsize.y) / 2;
1236 ((number == -1) ? "?" : ftos(number)),
1237 hud_fontsize, ((number > 0) ? '1 1 1' : '0.5 0.5 0.5'),
1241 pos.y += sz.y * 1.1;
1245 void Scoreboard_Duel_DrawTable(vector pos, bool invert, entity pl, entity tm)
1247 vector tmp, tmp_in, tmp_sz, tmp_acc;
1250 float average_acc = 0;
1256 // Stop here if there are no scores available
1257 if(pl.team != tm.team) return;
1260 tmp.x += panel_bg_padding;
1261 tmp.y += panel_bg_padding;
1262 panel_size.x -= panel_bg_padding * 2;
1265 // drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", tmp, panel_size, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL);
1268 if(invert) { tmp.x += panel_size.x; tmp.x -= duel_score_size.x; }
1269 drawfill(tmp, duel_score_size, '0 0 0', sbt_highlight_alpha, DRAWFLAG_NORMAL);
1272 tmp_str = ftos(pl.(scores(SP_SCORE)));
1274 tmp_in.x += (duel_score_size.x / 2) - (stringwidth(tmp_str, true, duel_score_fontsize) / 2);
1275 tmp_in.y += (duel_score_size.y / 2) - (duel_score_fontsize.y / 2);
1277 draw_beginBoldFont();
1278 drawstring(tmp_in, tmp_str, duel_score_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1282 tmp_str = Scoreboard_GetField(pl, SP_NAME);
1285 tmp_in.x -= stringwidth_colors(tmp_str, duel_name_fontsize) + duel_name_fontsize.x * 0.5;
1287 tmp_in.x += duel_score_size.x + duel_name_fontsize.x * 0.5;
1288 tmp_in.y += (duel_score_size.y - duel_name_fontsize.y) / 2;
1289 drawcolorcodedstring(tmp_in, tmp_str, duel_name_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
1291 //LegendGuard adds a conditional sentence for country column 05-04-2021
1292 // Player country icon/flag
1293 Scoreboard_GetField(pl, SP_COUNTRY);
1294 if(sbt_field_icon3 != "") {
1295 vector rsz = draw_getimagesize(sbt_field_icon3);
1296 sbt_fixcolumnwidth_iconlen = rsz.x / rsz.y;
1298 tmp_in.x -= hud_fontsize.x * sbt_fixcolumnwidth_iconlen + duel_name_fontsize.x * 0.5;
1300 tmp_in.x += stringwidth_colors(tmp_str, duel_name_fontsize) + duel_name_fontsize.x * 0.5;
1301 tmp_in.y += (duel_name_fontsize.y - hud_fontsize.y) / 2;
1302 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);
1306 float column_width = panel_size.x / 5;
1307 tmp.x = pos.x + panel_bg_padding;
1308 tmp.y += hud_fontsize.y * 3 + hud_fontsize.y;
1313 i = (invert ? 4 : 0);
1314 column_dim = vec2(column_width * 4, hud_fontsize.y);
1316 drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth("kills", false, hud_fontsize) / 2),
1317 "kills", hud_fontsize, '0.5 0.5 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1318 drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth("dmg", false, hud_fontsize) / 2),
1319 "dmg", hud_fontsize, '0.5 0.5 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1320 drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth("acc", false, hud_fontsize) / 2),
1321 "acc", hud_fontsize, '0.5 0.5 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1322 drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth("hits", false, hud_fontsize) / 2),
1323 "hits", hud_fontsize, '0.5 0.5 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1324 drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth("ping", false, hud_fontsize) / 2),
1325 "ping", hud_fontsize, '0.5 0.5 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1327 tmp.x = pos.x + panel_bg_padding;
1328 tmp.y += hud_fontsize.y;
1331 i = (invert ? 4 : 0);
1333 tmp_str = ftos(pl.(scores(SP_KILLS)));
1334 drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth(tmp_str, false, hud_fontsize * 1.25) / 2),
1335 tmp_str, hud_fontsize * 1.25, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1337 tmp_str = ftos(pl.(scores(SP_DMG)));
1338 drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth(tmp_str, false, hud_fontsize * 1.25) / 2),
1339 tmp_str, hud_fontsize * 1.25, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1341 tmp_acc = tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2);
1348 tmp_str = Scoreboard_GetField(pl, SP_PING);
1349 drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth(tmp_str, false, hud_fontsize * 1.25) / 2),
1350 tmp_str, hud_fontsize * 1.25, sbt_field_rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
1352 tmp.x = pos.x + panel_bg_padding;
1353 tmp.y += hud_fontsize.y * 2;
1357 int total_weapons = 0;
1360 WepSet weapons_inmap = WepSet_GetFromStat_InMap();
1361 FOREACH(Weapons, it != WEP_Null, {
1362 WepSet set = it.m_wepset;
1363 if (!(weapons_inmap & set))
1365 if (it.spawnflags & WEP_TYPE_OTHER)
1368 int weapon_cnt_fired = pl.accuracy_cnt_fired[i - WEP_FIRST];
1369 int weapon_cnt_hit = pl.accuracy_cnt_hit[i - WEP_FIRST];
1371 if(weapon_cnt_fired)
1372 weapon_acc = floor((weapon_cnt_hit / weapon_cnt_fired) * 100);
1373 average_acc += weapon_acc;
1378 int c = (invert ? 4 : 0);
1380 drawfill(tmp_in + eX * column_width * (invert ? 1 : 0), column_dim, '0 0 0', sbt_highlight_alpha, DRAWFLAG_NORMAL);
1382 draw_str = ftos(pl.accuracy_frags[i - WEP_FIRST]);
1383 drawstring(tmp_in + eX * column_width * (invert ? c-- : c++) + eX * ((column_width - stringwidth(draw_str, false, hud_fontsize)) / 2),
1384 draw_str, hud_fontsize, (weapon_cnt_fired ? '1 1 1' : '0.5 0.5 0.5'), panel_fg_alpha, DRAWFLAG_NORMAL);
1386 draw_str = ftos(pl.accuracy_hit[i - WEP_FIRST]);
1387 drawstring(tmp_in + eX * column_width * (invert ? c-- : c++) + eX * ((column_width - stringwidth(draw_str, false, hud_fontsize)) / 2),
1388 draw_str, hud_fontsize, (weapon_cnt_fired ? '1 1 1' : '0.5 0.5 0.5'), panel_fg_alpha, DRAWFLAG_NORMAL);
1390 draw_str = sprintf("%d%%", weapon_acc);
1391 drawstring(tmp_in + eX * column_width * (invert ? c-- : c++) + eX * ((column_width - stringwidth(draw_str, false, hud_fontsize)) / 2),
1392 draw_str, hud_fontsize, (weapon_cnt_fired ? '1 1 1' : '0.5 0.5 0.5'), panel_fg_alpha, DRAWFLAG_NORMAL);
1394 draw_str = strcat(ftos(weapon_cnt_hit), " / ", ftos(weapon_cnt_fired));
1395 drawstring(tmp_in + eX * column_width * (invert ? c-- : c++) + eX * (column_width / 2) - eX * stringwidth("36 /", false, hud_fontsize),
1396 draw_str,hud_fontsize, (weapon_cnt_fired ? '1 1 1' : '0.5 0.5 0.5'), panel_fg_alpha, DRAWFLAG_NORMAL);
1400 tmp_in.x = pos.x + panel_size.x - panel_bg_padding - hud_fontsize.x / 2;
1401 drawpic_aspect_skin(tmp_in, it.model2, vec2(50, hud_fontsize.y), '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1404 tmp_in.x = pos.x + panel_bg_padding;
1405 tmp_in.y += hud_fontsize.y * 1.25;
1407 if(weapon_cnt_fired)
1411 average_acc = floor((average_acc / total_weapons) + 0.5);
1413 // draw total accuracy now
1414 tmp_str = sprintf("%d%%", average_acc);
1415 drawstring(tmp_acc - eX * (stringwidth(tmp_str, false, hud_fontsize * 1.25) / 2),
1416 tmp_str, hud_fontsize * 1.25, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1419 vector icon_sz = vec2(column_width, hud_fontsize.y*1.5);
1422 tmp.x += column_width * 4;
1424 drawstring(tmp + eX * ((column_width - stringwidth("medals", false, hud_fontsize)) / 2),
1425 "medals", hud_fontsize, '0.5 0.5 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1426 tmp.y += hud_fontsize.y * 1.25;
1428 tmp = Scoreboard_Duel_DrawPickup(tmp, false, "gfx/medal/humiliation", icon_sz, pl.(scores(SP_MEDAL_HUMILIATION)), invert);
1429 tmp = Scoreboard_Duel_DrawPickup(tmp, false, "gfx/medal/impressive", icon_sz, pl.(scores(SP_MEDAL_IMPRESSIVE)), invert);
1430 tmp = Scoreboard_Duel_DrawPickup(tmp, false, "gfx/medal/excellent", icon_sz, pl.(scores(SP_MEDAL_EXCELLENT)), invert);
1433 drawstring(tmp + eX * ((column_width - stringwidth("items", false, hud_fontsize)) / 2),
1434 "items", hud_fontsize, '0.5 0.5 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1435 tmp.y += hud_fontsize.y * 1.25;
1439 it.m_id == ITEM_ArmorMega.m_id ||
1440 it.m_id == ITEM_HealthMega.m_id ||
1441 it.m_id == ITEM_ArmorBig.m_id, {
1442 // If the match isn't over, Only show pickups if we're spectating or they're our own
1443 if(intermission || warmup_stage || spectatee_status || pl.sv_entnum == current_player)
1444 inv_num = inventoryslots[pl.sv_entnum].inv_items[it.m_id];
1445 tmp = Scoreboard_Duel_DrawPickup(tmp, true, it.m_icon, icon_sz, inv_num, invert);
1447 if(it.m_id == REGISTRY_MAX(Items))
1451 vector Scoreboard_MakeDuelTable(vector pos, entity tm, vector rgb, vector bg_size)
1453 vector end_pos = pos;
1454 float screen_half = panel_size.x / 2;
1455 float weapon_margin = hud_fontsize.x;
1457 panel_size.x = screen_half - weapon_margin;
1458 panel_size.y = (duel_score_size.y * 5.5);
1460 entity pl_left = players.sort_next;
1461 entity pl_right = pl_left.sort_next;
1463 Scoreboard_Duel_DrawTable(pos, true, pl_left, tm);
1464 Scoreboard_Duel_DrawTable(pos + eX * screen_half + eX * weapon_margin, false, pl_right, tm);
1466 end_pos.y += panel_size.y + (panel_bg_padding * 2);
1467 panel_size.x = screen_half * 2;
1471 vector Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
1473 int max_players = 999;
1474 if(autocvar_hud_panel_scoreboard_maxheight > 0)
1476 float height = autocvar_hud_panel_scoreboard_maxheight * vid_conheight;
1479 height -= (panel_bg_padding * 2 + hud_fontsize.y * 1.25) * team_count; // - padding and header
1480 height -= hud_fontsize.y * (team_count - 1); // - spacing between tables
1481 height /= team_count;
1484 height -= panel_bg_padding * 2; // - padding
1485 max_players = floor(height / (hud_fontsize.y * 1.25));
1486 if(max_players <= 1)
1488 if(max_players == tm.team_size)
1493 entity me = playerslots[current_player];
1495 panel_size.y = 1.25 * hud_fontsize.y * (1 + bound(1, tm.team_size, max_players));
1496 panel_size.y += panel_bg_padding * 2;
1499 vector end_pos = panel_pos + eY * (panel_size.y + 0.5 * hud_fontsize.y);
1500 if(panel.current_panel_bg != "0")
1501 end_pos.y += panel_bg_border * 2;
1503 if(panel_bg_padding)
1505 panel_pos += '1 1 0' * panel_bg_padding;
1506 panel_size -= '2 2 0' * panel_bg_padding;
1510 vector tmp = vec2(panel_size.x, 1.25 * hud_fontsize.y);
1514 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', sbt_bg_alpha, DRAWFLAG_NORMAL);
1516 pos.y += 1.25 * hud_fontsize.y;
1519 tmp.y = panel_size.y - 1.25 * hud_fontsize.y;
1521 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL);
1524 // print header row and highlight columns
1525 pos = Scoreboard_DrawHeader(panel_pos, rgb, (max_players < tm.team_size), tm.team);
1527 // fill the table and draw the rows
1528 bool is_self = false;
1529 bool self_shown = false;
1532 if(Team_IsValidTeam(tm.team)) average_ping[Team_TeamToIndex(tm.team) - 1] = 0;
1533 for(pl = players.sort_next; pl; pl = pl.sort_next)
1535 if(pl.team != tm.team)
1537 if(i == max_players - 2 && pl != me)
1539 if(!self_shown && me.team == tm.team)
1541 Scoreboard_DrawItem(pos, rgb, me, true, i);
1543 pos.y += 1.25 * hud_fontsize.y;
1547 if(i >= max_players - 1)
1549 pos = Scoreboard_DrawOthers(pos, rgb, tm.team, (self_shown ? me : NULL), pl, i);
1552 is_self = (pl.sv_entnum == current_player);
1553 Scoreboard_DrawItem(pos, rgb, pl, is_self, i);
1555 if(Team_IsValidTeam(tm.team) && pl.ping) {
1556 average_ping[Team_TeamToIndex(tm.team) - 1] += pl.ping;
1561 pos.y += 1.25 * hud_fontsize.y;
1564 if(with_ping) average_ping[Team_TeamToIndex(tm.team) - 1] /= with_ping;
1566 panel_size.x += panel_bg_padding * 2; // restore initial width
1570 bool Scoreboard_WouldDraw()
1572 if (MUTATOR_CALLHOOK(DrawScoreboard))
1574 else if (QuickMenu_IsOpened())
1576 else if (HUD_Radar_Clickable())
1578 else if (scoreboard_showscores)
1580 else if (intermission == 1)
1582 else if (intermission == 2)
1584 else if (spectatee_status != -1 && STAT(HEALTH) <= 0 && autocvar_cl_deathscoreboard && !MUTATOR_CALLHOOK(DrawDeathScoreboard)
1585 && (!HUD_MinigameMenu_IsOpened() || !active_minigame))
1589 else if (scoreboard_showscores_force || MUTATOR_CALLHOOK(DrawScoreboard_Force))
1594 vector Scoreboard_MedalStats_Draw(vector pos)
1597 float height = hud_fontsize.y * 2;
1599 entity pl = playerslots[current_player];
1601 vector title_pos = pos;
1602 pos.x += 0.5 * hud_fontsize.x + panel_bg_padding;
1603 pos.y += 1.25 * hud_fontsize.y;
1607 pos = Scoreboard_DrawMedal(pos, "gfx/medal/airshot", height, pl.(scores(SP_MEDAL_AIRSHOT)));
1608 pos = Scoreboard_DrawMedal(pos, "gfx/medal/damage", height, pl.(scores(SP_MEDAL_DAMAGE)));
1609 pos = Scoreboard_DrawMedal(pos, "gfx/medal/electrobitch", height, pl.(scores(SP_MEDAL_ELECTROBITCH)));
1610 pos = Scoreboard_DrawMedal(pos, "gfx/medal/excellent", height, pl.(scores(SP_MEDAL_EXCELLENT)));
1611 pos = Scoreboard_DrawMedal(pos, "gfx/medal/firstblood", height, pl.(scores(SP_MEDAL_FIRSTBLOOD)));
1612 pos = Scoreboard_DrawMedal(pos, "gfx/medal/headshot", height, pl.(scores(SP_MEDAL_HEADSHOT)));
1613 pos = Scoreboard_DrawMedal(pos, "gfx/medal/humiliation", height, pl.(scores(SP_MEDAL_HUMILIATION)));
1614 pos = Scoreboard_DrawMedal(pos, "gfx/medal/impressive", height, pl.(scores(SP_MEDAL_IMPRESSIVE)));
1615 pos = Scoreboard_DrawMedal(pos, "gfx/medal/yoda", height, pl.(scores(SP_MEDAL_YODA)));
1616 pos = Scoreboard_DrawMedal(pos, "gfx/medal/telefrag", height, pl.(scores(SP_MEDAL_TELEFRAG)));
1619 pos.x += hud_fontsize.x;
1621 pos = Scoreboard_DrawMedal(pos, "gfx/medal/accuracy", height, pl.(scores(SP_MEDAL_ACCURACY)));
1622 pos = Scoreboard_DrawMedal(pos, "gfx/medal/assist", height, pl.(scores(SP_MEDAL_ASSIST)));
1623 pos = Scoreboard_DrawMedal(pos, "gfx/medal/capture", height, pl.(scores(SP_MEDAL_CAPTURE)));
1624 pos = Scoreboard_DrawMedal(pos, "gfx/medal/defense", height, pl.(scores(SP_MEDAL_DEFENSE)));
1625 pos = Scoreboard_DrawMedal(pos, "gfx/medal/perfect", height, pl.(scores(SP_MEDAL_PERFECT)));
1627 if(!total_medals) return orig;
1629 drawstring(title_pos, sprintf(_("Medal stats (total %d)"), total_medals),
1630 hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1633 pos.y += height + hud_fontsize.y * 0.5;
1637 float average_accuracy;
1638 vector Scoreboard_AccuracyStats_Draw(vector pos, vector rgb, vector bg_size)
1640 scoreboard_acc_fade_alpha = min(scoreboard_fade_alpha, scoreboard_acc_fade_alpha + frametime * 10);
1642 WepSet weapons_stat = WepSet_GetFromStat();
1643 WepSet weapons_inmap = WepSet_GetFromStat_InMap();
1644 int disownedcnt = 0;
1646 FOREACH(Weapons, it != WEP_Null, {
1647 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1649 WepSet set = it.m_wepset;
1650 if(it.spawnflags & WEP_TYPE_OTHER)
1655 if (weapon_stats < 0 && !((weapons_stat & set) || (weapons_inmap & set)))
1657 if (it.spawnflags & (WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_SPECIALATTACK))
1664 int weapon_cnt = (REGISTRY_COUNT(Weapons) - 1) - disownedcnt - nHidden;
1665 if (weapon_cnt <= 0) return pos;
1668 if (autocvar_hud_panel_scoreboard_accuracy_doublerows && weapon_cnt >= floor((REGISTRY_COUNT(Weapons) - nHidden - 1) * 0.5))
1670 int columnns = ceil(weapon_cnt / rows);
1672 float weapon_height = 29;
1673 float height = hud_fontsize.y + weapon_height;
1675 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);
1676 pos.y += 1.25 * hud_fontsize.y;
1677 if(panel.current_panel_bg != "0")
1678 pos.y += panel_bg_border;
1681 panel_size.y = height * rows;
1682 panel_size.y += panel_bg_padding * 2;
1684 float panel_bg_alpha_save = panel_bg_alpha;
1685 panel_bg_alpha *= scoreboard_acc_fade_alpha;
1687 panel_bg_alpha = panel_bg_alpha_save;
1689 vector end_pos = panel_pos + eY * (panel_size.y + 0.5 * hud_fontsize.y);
1690 if(panel.current_panel_bg != "0")
1691 end_pos.y += panel_bg_border * 2;
1693 if(panel_bg_padding)
1695 panel_pos += '1 1 0' * panel_bg_padding;
1696 panel_size -= '2 2 0' * panel_bg_padding;
1700 vector tmp = panel_size;
1702 float weapon_width = tmp.x / columnns / rows;
1705 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, sbt_bg_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL);
1709 // column highlighting
1710 for (int i = 0; i < columnns; ++i)
1712 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);
1715 for (int i = 0; i < rows; ++i)
1716 drawfill(pos + eY * (weapon_height + height * i), vec2(tmp.x, hud_fontsize.y), rgb, sbt_highlight_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL);
1719 average_accuracy = 0;
1720 int weapons_with_stats = 0;
1722 pos.x += weapon_width / 2;
1724 if (autocvar_hud_panel_scoreboard_accuracy_nocolors)
1727 Accuracy_LoadColors();
1729 float oldposx = pos.x;
1733 FOREACH(Weapons, it != WEP_Null, {
1734 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1736 WepSet set = it.m_wepset;
1737 if (weapon_stats < 0 && !((weapons_stat & set) || (weapons_inmap & set)))
1739 if (it.spawnflags & WEP_TYPE_OTHER)
1743 if (weapon_stats >= 0)
1744 weapon_alpha = sbt_fg_alpha;
1746 weapon_alpha = 0.2 * sbt_fg_alpha;
1749 drawpic_aspect_skin(tmpos, it.model2, vec2(weapon_width, weapon_height), '1 1 1', weapon_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL);
1751 if (weapon_stats >= 0) {
1752 weapons_with_stats += 1;
1753 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1756 s = sprintf("%d%%", weapon_stats * 100);
1759 padding = (weapon_width - stringwidth(s, false, hud_fontsize)) / 2; // center the accuracy value
1761 if(!autocvar_hud_panel_scoreboard_accuracy_nocolors)
1762 rgb = Accuracy_GetColor(weapon_stats);
1764 drawstring(tmpos + vec2(padding, weapon_height), s, hud_fontsize, rgb, sbt_fg_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL);
1766 tmpos.x += weapon_width * rows;
1767 pos.x += weapon_width * rows;
1768 if (rows == 2 && column == columnns - 1) {
1776 if (weapons_with_stats)
1777 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1779 panel_size.x += panel_bg_padding * 2; // restore initial width
1784 .bool uninteresting;
1785 STATIC_INIT(default_order_items_label)
1787 IL_EACH(default_order_items, true, {
1788 if(!(it.instanceOfPowerup
1789 || it == ITEM_HealthMega || it == ITEM_HealthBig
1790 || it == ITEM_ArmorMega || it == ITEM_ArmorBig
1793 it.uninteresting = true;
1798 vector Scoreboard_ItemStats_Draw(vector pos, vector rgb, vector bg_size)
1800 Inventory g_inventory = inventoryslots[current_player];
1801 scoreboard_itemstats_fade_alpha = min(scoreboard_fade_alpha, scoreboard_itemstats_fade_alpha + frametime * 10);
1803 int disowned_cnt = 0;
1804 int uninteresting_cnt = 0;
1805 IL_EACH(default_order_items, true, {
1806 int q = g_inventory.inv_items[it.m_id];
1807 //q = 1; // debug: display all items
1808 if (autocvar_hud_panel_scoreboard_itemstats_filter && it.uninteresting)
1809 ++uninteresting_cnt;
1813 int items_cnt = REGISTRY_COUNT(Items) - uninteresting_cnt;
1814 int n = items_cnt - disowned_cnt;
1815 if (n <= 0) return pos;
1817 int rows = (autocvar_hud_panel_scoreboard_itemstats_doublerows && n >= floor(REGISTRY_COUNT(Items) / 2)) ? 2 : 1;
1818 int columnns = max(6, ceil(n / rows));
1821 float fontsize = height * 1/3;
1822 float item_height = height * 2/3;
1824 drawstring(pos + eX * panel_bg_padding, _("Item stats"), hud_fontsize, '1 1 1', panel_fg_alpha * scoreboard_itemstats_fade_alpha, DRAWFLAG_NORMAL);
1825 pos.y += 1.25 * hud_fontsize.y;
1826 if(panel.current_panel_bg != "0")
1827 pos.y += panel_bg_border;
1830 panel_size.y = height * rows;
1831 panel_size.y += panel_bg_padding * 2;
1833 float panel_bg_alpha_save = panel_bg_alpha;
1834 panel_bg_alpha *= scoreboard_itemstats_fade_alpha;
1836 panel_bg_alpha = panel_bg_alpha_save;
1838 vector end_pos = panel_pos + eY * (panel_size.y + 0.5 * hud_fontsize.y);
1839 if(panel.current_panel_bg != "0")
1840 end_pos.y += panel_bg_border * 2;
1842 if(panel_bg_padding)
1844 panel_pos += '1 1 0' * panel_bg_padding;
1845 panel_size -= '2 2 0' * panel_bg_padding;
1849 vector tmp = panel_size;
1851 float item_width = tmp.x / columnns / rows;
1854 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, sbt_bg_alpha * scoreboard_itemstats_fade_alpha, DRAWFLAG_NORMAL);
1858 // column highlighting
1859 for (int i = 0; i < columnns; ++i)
1861 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);
1864 for (int i = 0; i < rows; ++i)
1865 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);
1869 pos.x += item_width / 2;
1871 float oldposx = pos.x;
1875 IL_EACH(default_order_items, !(autocvar_hud_panel_scoreboard_itemstats_filter && it.uninteresting), {
1876 int n = g_inventory.inv_items[it.m_id];
1877 //n = 1 + floor(i * 3 + 4.8) % 7; // debug: display a value for each item
1878 if (n <= 0) continue;
1879 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);
1881 float padding = (item_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center
1882 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);
1883 tmpos.x += item_width * rows;
1884 pos.x += item_width * rows;
1885 if (rows == 2 && column == columnns - 1) {
1893 panel_size.x += panel_bg_padding * 2; // restore initial width
1898 vector MapStats_DrawKeyValue(vector pos, string key, string value) {
1900 pos.x += hud_fontsize.x * 0.25;
1901 drawstring(pos, key, hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
1902 pos.x = panel_pos.x + panel_size.x - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1903 drawstring(pos, value, hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
1905 pos.y += hud_fontsize.y;
1911 vector Scoreboard_MapStats_Draw(vector pos, vector rgb, vector bg_size) {
1912 float stat_secrets_found, stat_secrets_total;
1913 float stat_monsters_killed, stat_monsters_total;
1917 // get monster stats
1918 stat_monsters_killed = STAT(MONSTERS_KILLED);
1919 stat_monsters_total = STAT(MONSTERS_TOTAL);
1921 // get secrets stats
1922 stat_secrets_found = STAT(SECRETS_FOUND);
1923 stat_secrets_total = STAT(SECRETS_TOTAL);
1925 // get number of rows
1926 if(stat_secrets_total)
1928 if(stat_monsters_total)
1931 // if no rows, return
1935 // draw table header
1936 drawstring(pos + eX * panel_bg_padding, _("Map stats:"), hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1937 pos.y += 1.25 * hud_fontsize.y;
1938 if(panel.current_panel_bg != "0")
1939 pos.y += panel_bg_border;
1942 panel_size.y = hud_fontsize.y * rows;
1943 panel_size.y += panel_bg_padding * 2;
1946 vector end_pos = panel_pos + eY * (panel_size.y + 0.5 * hud_fontsize.y);
1947 if(panel.current_panel_bg != "0")
1948 end_pos.y += panel_bg_border * 2;
1950 if(panel_bg_padding)
1952 panel_pos += '1 1 0' * panel_bg_padding;
1953 panel_size -= '2 2 0' * panel_bg_padding;
1957 vector tmp = panel_size;
1960 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL);
1963 if(stat_monsters_total)
1965 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1966 pos = MapStats_DrawKeyValue(pos, _("Monsters killed:"), val);
1970 if(stat_secrets_total)
1972 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1973 pos = MapStats_DrawKeyValue(pos, _("Secrets found:"), val);
1976 panel_size.x += panel_bg_padding * 2; // restore initial width
1981 vector Scoreboard_Rankings_Draw(vector pos, string ranktitle, entity pl, vector rgb, vector bg_size)
1984 RANKINGS_RECEIVED_CNT = 0;
1985 for (i=RANKINGS_CNT-1; i>=0; --i)
1987 ++RANKINGS_RECEIVED_CNT;
1989 if (RANKINGS_RECEIVED_CNT == 0)
1992 vector hl_rgb = rgb + '0.5 0.5 0.5';
1994 pos.y += hud_fontsize.y;
1995 drawstring(pos + eX * panel_bg_padding, ranktitle, hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1996 pos.y += 1.25 * hud_fontsize.y;
1997 if(panel.current_panel_bg != "0")
1998 pos.y += panel_bg_border;
2003 for(i = 0; i < RANKINGS_RECEIVED_CNT; ++i)
2005 float f = stringwidth(ColorTranslateRGB(grecordholder[i]), true, hud_fontsize);
2010 if(namesize > autocvar_hud_panel_scoreboard_namesize * hud_fontsize.x)
2012 namesize = autocvar_hud_panel_scoreboard_namesize * hud_fontsize.x;
2016 float ranksize = 3 * hud_fontsize.x;
2017 float timesize = 5 * hud_fontsize.x;
2018 vector columnsize = vec2(ranksize + timesize + namesize + hud_fontsize.x, 1.25 * hud_fontsize.y);
2019 int columns = max(1, floor((panel_size.x - 2 * panel_bg_padding) / columnsize.x));
2020 columns = min(columns, RANKINGS_RECEIVED_CNT);
2022 // expand name column to fill the entire row
2023 float available_space = (panel_size.x - 2 * panel_bg_padding - columnsize.x * columns) / columns;
2024 namesize += available_space;
2025 columnsize.x += available_space;
2027 panel_size.y = ceil(RANKINGS_RECEIVED_CNT / columns) * 1.25 * hud_fontsize.y;
2028 panel_size.y += panel_bg_padding * 2;
2032 vector end_pos = panel_pos + eY * (panel_size.y + 0.5 * hud_fontsize.y);
2033 if(panel.current_panel_bg != "0")
2034 end_pos.y += panel_bg_border * 2;
2036 if(panel_bg_padding)
2038 panel_pos += '1 1 0' * panel_bg_padding;
2039 panel_size -= '2 2 0' * panel_bg_padding;
2045 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, panel_size, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL);
2047 vector text_ofs = vec2(0.5 * hud_fontsize.x, (1.25 - 1) / 2 * hud_fontsize.y); // center text vertically
2049 int column = 0, j = 0;
2050 string zoned_name_self = strzone(strdecolorize(entcs_GetName(player_localnum)));
2051 for(i = 0; i < RANKINGS_RECEIVED_CNT; ++i)
2058 if(strdecolorize(grecordholder[i]) == zoned_name_self)
2059 drawfill(pos, columnsize, hl_rgb, sbt_highlight_alpha_self, DRAWFLAG_NORMAL);
2060 else if(!((j + column) & 1) && sbt_highlight)
2061 drawfill(pos, columnsize, hl_rgb, sbt_highlight_alpha, DRAWFLAG_NORMAL);
2063 str = count_ordinal(i+1);
2064 drawstring(pos + text_ofs, str, hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
2065 drawstring(pos + text_ofs + eX * ranksize, TIME_ENCODED_TOSTRING(t), hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
2066 str = ColorTranslateRGB(grecordholder[i]);
2068 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
2069 drawcolorcodedstring(pos + text_ofs + eX * (ranksize + timesize), str, hud_fontsize, sbt_fg_alpha, DRAWFLAG_NORMAL);
2071 pos.y += 1.25 * hud_fontsize.y;
2073 if(j >= ceil(RANKINGS_RECEIVED_CNT / columns))
2077 pos.x += panel_size.x / columns;
2078 pos.y = panel_pos.y;
2081 strfree(zoned_name_self);
2083 panel_size.x += panel_bg_padding * 2; // restore initial width
2087 float scoreboard_time;
2088 bool have_weapon_stats;
2089 bool Scoreboard_AccuracyStats_WouldDraw(float ypos)
2091 if (MUTATOR_CALLHOOK(DrawScoreboardAccuracy))
2093 if (!autocvar_hud_panel_scoreboard_accuracy || warmup_stage || ypos > 0.91 * vid_conheight)
2096 if (time < scoreboard_time + autocvar_hud_panel_scoreboard_accuracy_showdelay
2097 && ypos > autocvar_hud_panel_scoreboard_accuracy_showdelay_minpos * vid_conheight
2103 if (!have_weapon_stats)
2105 FOREACH(Weapons, it != WEP_Null, {
2106 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
2107 if (weapon_stats >= 0)
2109 have_weapon_stats = true;
2113 if (!have_weapon_stats)
2120 bool have_item_stats;
2121 bool Scoreboard_ItemStats_WouldDraw(float ypos)
2123 Inventory g_inventory = inventoryslots[current_player];
2125 if (MUTATOR_CALLHOOK(DrawScoreboardItemStats))
2127 if (!autocvar_hud_panel_scoreboard_itemstats || !g_inventory || warmup_stage || ypos > 0.91 * vid_conheight)
2130 if (time < scoreboard_time + autocvar_hud_panel_scoreboard_itemstats_showdelay
2131 && ypos > autocvar_hud_panel_scoreboard_itemstats_showdelay_minpos * vid_conheight
2137 if (!have_item_stats)
2139 IL_EACH(default_order_items, true, {
2140 if (!(autocvar_hud_panel_scoreboard_itemstats_filter && it.uninteresting))
2142 int q = g_inventory.inv_items[it.m_id];
2143 //q = 1; // debug: display all items
2146 have_item_stats = true;
2151 if (!have_item_stats)
2158 void Scoreboard_Draw()
2160 if(!autocvar__hud_configure)
2162 if(!hud_draw_maximized) return;
2164 // frametime checks allow to toggle the scoreboard even when the game is paused
2165 if(scoreboard_active) {
2166 if (scoreboard_fade_alpha == 0)
2167 scoreboard_time = time;
2168 if(hud_configure_menu_open == 1)
2169 scoreboard_fade_alpha = 1;
2170 float scoreboard_fadeinspeed = autocvar_hud_panel_scoreboard_fadeinspeed;
2171 if (scoreboard_fadeinspeed && frametime)
2172 scoreboard_fade_alpha = min(1, scoreboard_fade_alpha + frametime * scoreboard_fadeinspeed);
2174 scoreboard_fade_alpha = 1;
2175 if(hud_fontsize_str != autocvar_hud_fontsize)
2177 hud_fontsize = HUD_GetFontsize("hud_fontsize");
2178 Scoreboard_initFieldSizes();
2179 strcpy(hud_fontsize_str, autocvar_hud_fontsize);
2183 float scoreboard_fadeoutspeed = autocvar_hud_panel_scoreboard_fadeoutspeed;
2184 if (scoreboard_fadeoutspeed && frametime)
2185 scoreboard_fade_alpha = max(0, scoreboard_fade_alpha - frametime * scoreboard_fadeoutspeed);
2187 scoreboard_fade_alpha = 0;
2190 if (!scoreboard_fade_alpha)
2192 scoreboard_acc_fade_alpha = 0;
2193 scoreboard_itemstats_fade_alpha = 0;
2198 scoreboard_fade_alpha = 0;
2200 if (autocvar_hud_panel_scoreboard_dynamichud)
2203 HUD_Scale_Disable();
2205 if(scoreboard_fade_alpha <= 0)
2207 panel_fade_alpha *= scoreboard_fade_alpha;
2208 HUD_Panel_LoadCvars();
2210 sbt_bg_alpha = autocvar_hud_panel_scoreboard_table_bg_alpha * panel_fg_alpha;
2211 sbt_highlight = autocvar_hud_panel_scoreboard_table_highlight;
2212 sbt_highlight_alpha = autocvar_hud_panel_scoreboard_table_highlight_alpha * panel_fg_alpha;
2213 sbt_highlight_alpha_self = autocvar_hud_panel_scoreboard_table_highlight_alpha_self * panel_fg_alpha;
2214 sbt_highlight_alpha_eliminated = autocvar_hud_panel_scoreboard_table_highlight_alpha_eliminated * panel_fg_alpha;
2215 sbt_fg_alpha = autocvar_hud_panel_scoreboard_table_fg_alpha * panel_fg_alpha;
2216 sbt_fg_alpha_self = autocvar_hud_panel_scoreboard_table_fg_alpha_self * panel_fg_alpha;
2218 // don't overlap with con_notify
2219 if(!autocvar__hud_configure)
2220 panel_pos.y = max((autocvar_con_notify * autocvar_con_notifysize), panel_pos.y);
2222 float excess = max(0, max_namesize - autocvar_hud_panel_scoreboard_namesize * hud_fontsize.x);
2223 float fixed_scoreboard_width = bound(vid_conwidth * autocvar_hud_panel_scoreboard_minwidth, vid_conwidth - excess, vid_conwidth * 0.93);
2224 panel_pos.x = 0.5 * (vid_conwidth - fixed_scoreboard_width);
2225 panel_size.x = fixed_scoreboard_width;
2227 Scoreboard_UpdatePlayerTeams();
2229 float initial_pos_y = panel_pos.y;
2230 vector pos = panel_pos;
2235 vector sb_gameinfo_type_fontsize, sb_gameinfo_detail_fontsize;
2237 // Begin of Game Info Section
2238 sb_gameinfo_type_fontsize = hud_fontsize * 2.5;
2239 sb_gameinfo_detail_fontsize = hud_fontsize * 1.3;
2242 //drawcolorcodedstring(pos, "bienvenidoainternet.org", sb_gameinfo_type_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
2243 //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);
2244 //pos.y += sb_gameinfo_type_fontsize.y;
2246 // Game Info: Game Type
2247 str = MapInfo_Type_ToText(gametype);
2249 draw_beginBoldFont();
2250 //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);
2251 drawcolorcodedstring(pos, str, sb_gameinfo_type_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
2254 vector tmp_old_sz = draw_getimagesize("gfx/bai_logo");
2255 float tmp_aspect = tmp_old_sz.x/tmp_old_sz.y;
2256 vector tmp_new_sz = vec2(sb_gameinfo_type_fontsize.y * tmp_aspect, sb_gameinfo_type_fontsize.y);
2258 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);
2260 pos.y += sb_gameinfo_type_fontsize.y;
2263 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);
2265 pos.y += sb_gameinfo_detail_fontsize.y;
2267 // Game Info: Game Detail
2268 float tl = STAT(TIMELIMIT);
2269 float fl = STAT(FRAGLIMIT);
2270 float ll = STAT(LEADLIMIT);
2271 float ll_and_fl = STAT(LEADLIMIT_AND_FRAGLIMIT);
2274 str = strcat(str, sprintf(_("^3%1.0f minutes"), tl));
2275 if(!gametype.m_hidelimits)
2280 str = strcat(str, "^7 / "); // delimiter
2283 str = strcat(str, sprintf(_("^5%s %s"), ScoreString(teamscores_flags(ts_primary), fl),
2284 (teamscores_label(ts_primary) == "score") ? CTX(_("SCO^points")) :
2285 (teamscores_label(ts_primary) == "fastest") ? "" :
2286 TranslateScoresLabel(teamscores_label(ts_primary))));
2290 str = strcat(str, sprintf(_("^5%s %s"), ScoreString(scores_flags(ps_primary), fl),
2291 (scores_label(ps_primary) == "score") ? CTX(_("SCO^points")) :
2292 (scores_label(ps_primary) == "fastest") ? "" :
2293 TranslateScoresLabel(scores_label(ps_primary))));
2298 if(tl > 0 || fl > 0)
2301 if (ll_and_fl && fl > 0)
2302 str = strcat(str, "^7 & ");
2304 str = strcat(str, "^7 / ");
2309 str = strcat(str, sprintf(_("^2+%s %s"), ScoreString(teamscores_flags(ts_primary), ll),
2310 (teamscores_label(ts_primary) == "score") ? CTX(_("SCO^points")) :
2311 (teamscores_label(ts_primary) == "fastest") ? "" :
2312 TranslateScoresLabel(teamscores_label(ts_primary))));
2316 str = strcat(str, sprintf(_("^2+%s %s"), ScoreString(scores_flags(ps_primary), ll),
2317 (scores_label(ps_primary) == "score") ? CTX(_("SCO^points")) :
2318 (scores_label(ps_primary) == "fastest") ? "" :
2319 TranslateScoresLabel(scores_label(ps_primary))));
2324 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
2326 str = sprintf(_("^7Map: ^2%s"), shortmapname);
2327 drawcolorcodedstring(pos, str, sb_gameinfo_detail_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL); // align left
2328 // End of Game Info Section
2330 pos.y += sb_gameinfo_detail_fontsize.y + hud_fontsize.y * 0.3; // space between Game Info Section and score table
2331 if(panel.current_panel_bg != "0")
2332 pos.y += panel_bg_border;
2334 // Draw the scoreboard
2335 float scale = autocvar_hud_panel_scoreboard_table_bg_scale;
2338 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * scale;
2342 vector panel_bg_color_save = panel_bg_color;
2343 vector team_score_baseoffset;
2344 vector team_size_baseoffset;
2345 if (autocvar_hud_panel_scoreboard_team_size_position != 1) // team size not on left
2347 // put team score to the left of scoreboard (and team size to the right)
2348 team_score_baseoffset = eY * hud_fontsize.y - eX * hud_fontsize.x * 1.5;
2349 team_size_baseoffset = eY * hud_fontsize.y + eX * hud_fontsize.x * 0.5;
2350 if(panel.current_panel_bg != "0")
2352 team_score_baseoffset.x -= panel_bg_border;
2353 team_size_baseoffset.x += panel_bg_border;
2358 // put team score to the right of scoreboard (and team size to the left)
2359 team_score_baseoffset = eY * hud_fontsize.y + eX * hud_fontsize.x * 1.5;
2360 team_size_baseoffset = eY * hud_fontsize.y - eX * hud_fontsize.x * 0.5;
2361 if(panel.current_panel_bg != "0")
2363 team_score_baseoffset.x += panel_bg_border;
2364 team_size_baseoffset.x -= panel_bg_border;
2368 int team_size_total = 0;
2369 if (autocvar_hud_panel_scoreboard_team_size_position != 0) // team size not off
2371 // calculate team size total (sum of all team sizes)
2372 for(tm = teams.sort_next; tm; tm = tm.sort_next)
2373 if(tm.team != NUM_SPECTATOR)
2374 team_size_total += tm.team_size;
2377 for(tm = teams.sort_next; tm; tm = tm.sort_next)
2379 if(tm.team == NUM_SPECTATOR)
2384 vector rgb = Team_ColorRGB(tm.team);
2385 /*draw_beginBoldFont();
2386 str = ftos(tm.(teamscores(ts_primary)));
2387 if (autocvar_hud_panel_scoreboard_team_size_position != 1) // team size not on left
2389 // team score on the left (default)
2390 str_pos = pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 3);
2394 // team score on the right
2395 str_pos = pos + team_score_baseoffset + eX * (panel_size.x + hud_fontsize.x * 3);
2397 drawstring(str_pos, str, hud_fontsize * 3, rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
2399 // team size (if set to show on the side)
2400 if (autocvar_hud_panel_scoreboard_team_size_position != 0) // team size not off
2402 // calculate the starting position for the whole team size info string
2403 str = sprintf("%d/%d", tm.team_size, team_size_total);
2404 if (autocvar_hud_panel_scoreboard_team_size_position == 1)
2406 // team size on the left
2407 str_pos = pos + team_size_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5);
2411 // team size on the right
2412 str_pos = pos + team_size_baseoffset + eX * (panel_size.x + hud_fontsize.x * 1.5);
2414 str = sprintf("%d", tm.team_size);
2415 drawstring(str_pos, str, hud_fontsize * 1.5, rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
2416 str_pos += eX * stringwidth(str, true, hud_fontsize * 1.5) + eY * hud_fontsize.y * .5;
2417 str = sprintf("/%d", team_size_total);
2418 drawstring(str_pos, str, hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
2422 // secondary score, e.g. keyhunt
2423 if(ts_primary != ts_secondary)
2425 str = ftos(tm.(teamscores(ts_secondary)));
2426 if (autocvar_hud_panel_scoreboard_team_size_position != 1) // team size not on left
2429 str_pos = pos + team_score_baseoffset - vec2(stringwidth(str, false, hud_fontsize), hud_fontsize.y * -1.5);
2434 str_pos = pos + team_score_baseoffset + vec2(panel_size.x + hud_fontsize.x * 1.5, hud_fontsize.y * 1.5);
2437 drawstring(str_pos, str, hud_fontsize, rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
2442 // z411 My team header
2444 drawfill(pos, team_score_size, rgb * 0.5, sbt_highlight_alpha, DRAWFLAG_NORMAL);
2447 str = ftos(tm.(teamscores(ts_primary)));
2449 str_pos.x += (team_score_size.x / 2) - (stringwidth(str, true, team_score_fontsize) / 2);
2450 str_pos.y += (team_score_size.y / 2) - (team_score_fontsize.y / 2);
2452 draw_beginBoldFont();
2453 drawstring(str_pos, str, team_score_fontsize, rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
2457 str = Team_CustomName(tm.team);
2459 str_pos.x += team_score_size.x + team_name_fontsize.x * 0.5;
2460 str_pos.y += (team_score_size.y / 2) - (team_name_fontsize.y / 2);
2461 drawcolorcodedstring(str_pos, str, team_name_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
2463 pos.y += team_score_size.y + (hud_fontsize.y * 0.5);
2465 if(autocvar_hud_panel_scoreboard_bg_teams_color_team > 0)
2466 panel_bg_color = rgb * autocvar_hud_panel_scoreboard_bg_teams_color_team;
2467 else if(panel_bg_color_team > 0)
2468 panel_bg_color = rgb * panel_bg_color_team;
2470 panel_bg_color = rgb;
2471 pos = Scoreboard_MakeTable(pos, tm, panel_bg_color, bg_size);
2473 panel_bg_color = panel_bg_color_save;
2475 else if(gametype == MAPINFO_TYPE_DUEL)
2477 for(tm = teams.sort_next; tm; tm = tm.sort_next)
2478 if(tm.team != NUM_SPECTATOR)
2481 // z411 make DUEL TABLE
2482 pos = Scoreboard_MakeDuelTable(pos, tm, panel_bg_color, bg_size);
2486 for(tm = teams.sort_next; tm; tm = tm.sort_next)
2487 if(tm.team != NUM_SPECTATOR)
2490 // display it anyway
2491 pos = Scoreboard_MakeTable(pos, tm, panel_bg_color, bg_size);
2494 pos = Scoreboard_MedalStats_Draw(pos);
2496 if (Scoreboard_AccuracyStats_WouldDraw(pos.y))
2497 pos = Scoreboard_AccuracyStats_Draw(pos, panel_bg_color, bg_size);
2498 if (Scoreboard_ItemStats_WouldDraw(pos.y))
2499 pos = Scoreboard_ItemStats_Draw(pos, panel_bg_color, bg_size);
2501 if(MUTATOR_CALLHOOK(ShowRankings)) {
2502 string ranktitle = M_ARGV(0, string);
2503 if(race_speedaward) {
2504 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);
2505 pos.y += 1.25 * hud_fontsize.y;
2507 if(race_speedaward_alltimebest) {
2508 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);
2509 pos.y += 1.25 * hud_fontsize.y;
2511 pos = Scoreboard_Rankings_Draw(pos, ranktitle, playerslots[player_localnum], panel_bg_color, bg_size);
2514 //pos = Scoreboard_MapStats_Draw(pos, panel_bg_color, bg_size);
2517 for(pl = players.sort_next; pl; pl = pl.sort_next)
2519 if(pl.team == NUM_SPECTATOR)
2521 for(tm = teams.sort_next; tm; tm = tm.sort_next)
2522 if(tm.team == NUM_SPECTATOR)
2524 str = sprintf("%s (%d)", _("Spectators"), tm.team_size);
2525 draw_beginBoldFont();
2526 drawstring(pos, str, hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
2528 pos.y += 1.25 * hud_fontsize.y;
2530 pos = Scoreboard_DrawOthers(pos, '0 0 0', pl.team, NULL, pl, 0);
2531 pos.y += 1.25 * hud_fontsize.y;
2538 // print information about respawn status
2539 float respawn_time = STAT(RESPAWN_TIME);
2543 if(respawn_time < 0)
2545 // a negative number means we are awaiting respawn, time value is still the same
2546 respawn_time *= -1; // remove mark now that we checked it
2548 if(respawn_time < time) // it happens for a few frames when server is respawning the player
2549 str = ""; // draw an empty string to not change suddenly scoreboard_bottom
2551 str = sprintf(_("^1Respawning in ^3%s^1..."),
2552 (autocvar_hud_panel_scoreboard_respawntime_decimals ?
2553 count_seconds_decs(respawn_time - time, autocvar_hud_panel_scoreboard_respawntime_decimals)
2555 count_seconds(ceil(respawn_time - time))
2559 else if(time < respawn_time)
2561 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
2562 (autocvar_hud_panel_scoreboard_respawntime_decimals ?
2563 count_seconds_decs(respawn_time - time, autocvar_hud_panel_scoreboard_respawntime_decimals)
2565 count_seconds(ceil(respawn_time - time))
2569 else if(time >= respawn_time)
2570 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
2572 pos.y += 1.2 * hud_fontsize.y;
2573 drawcolorcodedstring(pos + '0.5 0 0' * (panel_size.x - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
2576 pos.y += 2 * hud_fontsize.y;
2577 if (scoreboard_fade_alpha < 1)
2578 scoreboard_bottom = initial_pos_y + (pos.y - initial_pos_y) * scoreboard_fade_alpha;
2579 else if (pos.y != scoreboard_bottom)
2581 if (pos.y > scoreboard_bottom)
2582 scoreboard_bottom = min(pos.y, scoreboard_bottom + frametime * 10 * (pos.y - initial_pos_y));
2584 scoreboard_bottom = max(pos.y, scoreboard_bottom - frametime * 10 * (pos.y - initial_pos_y));