]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/client/hud/panel/scoreboard.qc
Added new column of country flags in the scoreboard (for the sake of z411's request)
[xonotic/xonotic-data.pk3dir.git] / qcsrc / client / hud / panel / scoreboard.qc
1 #include "scoreboard.qh"
2
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>
17
18 // Scoreboard (#24)
19
20 void Scoreboard_Draw_Export(int fh)
21 {
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");
37 }
38
39 const int MAX_SBT_FIELDS = MAX_SCORE;
40
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];
44 int sbt_num_fields;
45
46 string autocvar_hud_fontsize;
47 string hud_fontsize_str;
48 float max_namesize;
49
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;
56 int total_medals;
57
58 float sbt_bg_alpha;
59 float sbt_fg_alpha;
60 float sbt_fg_alpha_self;
61 bool sbt_highlight;
62 float sbt_highlight_alpha;
63 float sbt_highlight_alpha_self;
64 float sbt_highlight_alpha_eliminated;
65
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 = "";
76
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;
91
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;
97
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;
103
104 bool autocvar_hud_panel_scoreboard_dynamichud = false;
105
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
115 int average_ping[NUM_TEAMS];
116
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)
120 {
121         if (mode == 1)
122                 label = "bckills"; // first case in the switch
123
124         switch(label)
125         {
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;
170         }
171         return label;
172 }
173
174 void PrintScoresLabels() { Label_getInfo(string_null, 1); }
175 string TranslateScoresLabel(string label) { return Label_getInfo(label, 0); }
176
177 void Scoreboard_InitScores()
178 {
179         int i, f;
180
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)
186                         ps_primary = it;
187                 if(f == SFL_SORT_PRIO_SECONDARY)
188                         ps_secondary = it;
189         });
190         if(ps_secondary == NULL)
191                 ps_secondary = ps_primary;
192
193         for(i = 0; i < MAX_TEAMSCORE; ++i)
194         {
195                 f = (teamscores_flags(i) & SFL_SORT_PRIO_MASK);
196                 if(f == SFL_SORT_PRIO_PRIMARY)
197                         ts_primary = i;
198                 if(f == SFL_SORT_PRIO_SECONDARY)
199                         ts_secondary = i;
200         }
201         if(ts_secondary == -1)
202                 ts_secondary = ts_primary;
203
204         Cmd_Scoreboard_SetFields(0);
205 }
206
207 //float lastpnum;
208 void Scoreboard_UpdatePlayerTeams()
209 {
210         entity pl, tmp;
211         //int num = 0;
212         for(pl = players.sort_next; pl; pl = pl.sort_next)
213         {
214                 //num += 1;
215                 int Team = entcs_GetScoreTeam(pl.sv_entnum);
216                 if(SetTeam(pl, Team))
217                 {
218                         tmp = pl.sort_prev;
219                         Scoreboard_UpdatePlayerPos(pl);
220                         if(tmp)
221                                 pl = tmp;
222                         else
223                                 pl = players.sort_next;
224                 }
225         }
226         /*
227         if(num != lastpnum)
228                 print(strcat("PNUM: ", ftos(num), "\n"));
229         lastpnum = num;
230         */
231 }
232
233 int Scoreboard_CompareScore(int vl, int vr, int f)
234 {
235         TC(int, vl); TC(int, vr); TC(int, f);
236         if(f & SFL_ZERO_IS_WORST)
237         {
238                 if(vl == 0 && vr != 0)
239                         return 1;
240                 if(vl != 0 && vr == 0)
241                         return 0;
242         }
243         if(vl > vr)
244                 return IS_INCREASING(f);
245         if(vl < vr)
246                 return IS_DECREASING(f);
247         return -1;
248 }
249
250 float Scoreboard_ComparePlayerScores(entity left, entity right)
251 {
252         float vl, vr, r;
253         vl = entcs_GetTeam(left.sv_entnum);
254         vr = entcs_GetTeam(right.sv_entnum);
255
256         if(!left.gotscores)
257                 vl = NUM_SPECTATOR;
258         if(!right.gotscores)
259                 vr = NUM_SPECTATOR;
260
261         if(vl > vr)
262                 return true;
263         if(vl < vr)
264                 return false;
265
266         if(vl == NUM_SPECTATOR)
267         {
268                 // FIRST the one with scores (spectators), THEN the ones without (downloaders)
269                 // no other sorting
270                 if(!left.gotscores && right.gotscores)
271                         return true;
272                 return false;
273         }
274
275         r = Scoreboard_CompareScore(left.scores(ps_primary), right.scores(ps_primary), scores_flags(ps_primary));
276         if (r >= 0)
277                 return r;
278
279         r = Scoreboard_CompareScore(left.scores(ps_secondary), right.scores(ps_secondary), scores_flags(ps_secondary));
280         if (r >= 0)
281                 return r;
282
283         FOREACH(Scores, true, {
284                 r = Scoreboard_CompareScore(left.scores(it), right.scores(it), scores_flags(it));
285                 if (r >= 0) return r;
286         });
287
288         if (left.sv_entnum < right.sv_entnum)
289                 return true;
290
291         return false;
292 }
293
294 void Scoreboard_UpdatePlayerPos(entity player)
295 {
296         entity ent;
297         for(ent = player.sort_next; ent && Scoreboard_ComparePlayerScores(player, ent); ent = player.sort_next)
298         {
299                 SORT_SWAP(player, ent);
300         }
301         for(ent = player.sort_prev; ent != players && Scoreboard_ComparePlayerScores(ent, player); ent = player.sort_prev)
302         {
303                 SORT_SWAP(ent, player);
304         }
305 }
306
307 float Scoreboard_CompareTeamScores(entity left, entity right)
308 {
309         int i, r;
310
311         if(left.team == NUM_SPECTATOR)
312                 return 1;
313         if(right.team == NUM_SPECTATOR)
314                 return 0;
315
316         r = Scoreboard_CompareScore(left.teamscores(ts_primary), right.teamscores(ts_primary), teamscores_flags(ts_primary));
317         if (r >= 0)
318                 return r;
319
320         r = Scoreboard_CompareScore(left.teamscores(ts_secondary), right.teamscores(ts_secondary), teamscores_flags(ts_secondary));
321         if (r >= 0)
322                 return r;
323
324         for(i = 0; i < MAX_TEAMSCORE; ++i)
325         {
326                 r = Scoreboard_CompareScore(left.teamscores(i), right.teamscores(i), teamscores_flags(i));
327                 if (r >= 0)
328                         return r;
329         }
330
331         if (left.team < right.team)
332                 return true;
333
334         return false;
335 }
336
337 void Scoreboard_UpdateTeamPos(entity Team)
338 {
339         entity ent;
340         for(ent = Team.sort_next; ent && Scoreboard_CompareTeamScores(Team, ent); ent = Team.sort_next)
341         {
342                 SORT_SWAP(Team, ent);
343         }
344         for(ent = Team.sort_prev; ent != teams && Scoreboard_CompareTeamScores(ent, Team); ent = Team.sort_prev)
345         {
346                 SORT_SWAP(ent, Team);
347         }
348 }
349
350 void Cmd_Scoreboard_Help()
351 {
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):"));
361         LOG_HELP("");
362
363         PrintScoresLabels();
364         LOG_HELP("");
365
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."));
370         LOG_HELP("");
371
372         LOG_HELP(_("The special game type names 'teams' and 'noteams' can be used to\n"
373                 "include/exclude ALL teams/noteams game modes."));
374         LOG_HELP("");
375
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."));
381 }
382
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" \
391 " +tdm/sum" \
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"
404
405 void Cmd_Scoreboard_SetFields(int argc)
406 {
407         TC(int, argc);
408         int i, slash;
409         string str, pattern;
410         bool have_name = false, have_primary = false, have_secondary = false, have_separator = false;
411         int missing;
412
413         if(!gametype)
414                 return; // do nothing, we don't know gametype and scores yet
415
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)");
420
421         // TODO: re enable with gametype dependant cvars?
422         if(argc < 3) // no arguments provided
423                 argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " ");
424
425         if(argc < 3)
426                 argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " ");
427
428         if(argc == 3)
429         {
430                 if(argv(2) == "default" || argv(2) == "expand_default")
431                 {
432                         if(argv(2) == "expand_default")
433                                 cvar_set("scoreboard_columns", SCOREBOARD_DEFAULT_COLUMNS);
434                         argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " ");
435                 }
436                 else if(argv(2) == "all")
437                 {
438                         string s = "ping pl cn name |"; // scores without a label
439                         FOREACH(Scores, true, {
440                                 if(it != ps_primary)
441                                 if(it != ps_secondary)
442                                 if(scores_label(it) != "")
443                                         s = strcat(s, " ", scores_label(it));
444                         });
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), " ");
449                 }
450         }
451
452
453         sbt_num_fields = 0;
454
455         hud_fontsize = HUD_GetFontsize("hud_fontsize");
456         
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);
460         
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);
464
465         for(i = 1; i < argc - 1; ++i)
466         {
467                 str = argv(i+1);
468                 bool nocomplain = false;
469                 if(substring(str, 0, 1) == "?")
470                 {
471                         nocomplain = true;
472                         str = substring(str, 1, strlen(str) - 1);
473                 }
474
475                 slash = strstrofs(str, "/", 0);
476                 if(slash >= 0)
477                 {
478                         pattern = substring(str, 0, slash);
479                         str = substring(str, slash + 1, strlen(str) - (slash + 1));
480
481                         if (!isGametypeInFilter(gametype, teamplay, false, pattern))
482                                 continue;
483                 }
484
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);
488
489                 PlayerScoreField j;
490                 switch(str)
491                 {
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;
503                         default:
504                         {
505                                 FOREACH(Scores, true, {
506                                         if (str == strtolower(scores_label(it))) {
507                                                 j = it;
508                                                 goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
509                                         }
510                                 });
511
512 LABEL(notfound)
513                                 if(str == "frags")
514                                         j = SP_FRAGS;
515                                 else
516                                 {
517                                         if(!nocomplain)
518                                                 LOG_INFOF("^1Error:^7 Unknown score field: '%s'", str);
519                                         continue;
520                                 }
521 LABEL(found)
522                                 sbt_field[sbt_num_fields] = j;
523                                 if(j == ps_primary)
524                                         have_primary = true;
525                                 if(j == ps_secondary)
526                                         have_secondary = true;
527
528                         }
529                 }
530                 ++sbt_num_fields;
531                 if(sbt_num_fields >= MAX_SBT_FIELDS)
532                         break;
533         }
534
535         if(scores_flags(ps_primary) & SFL_ALLOW_HIDE)
536                 have_primary = true;
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);
542
543         if(sbt_num_fields + missing < MAX_SBT_FIELDS)
544         {
545                 if(!have_name)
546                 {
547                         strunzone(sbt_field_title[sbt_num_fields]);
548                         for(i = sbt_num_fields; i > 0; --i)
549                         {
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];
553                         }
554                         sbt_field_title[0] = strzone(TranslateScoresLabel("name"));
555                         sbt_field[0] = SP_NAME;
556                         ++sbt_num_fields;
557                         LOG_INFO("fixed missing field 'name'");
558
559                         if(!have_separator)
560                         {
561                                 strunzone(sbt_field_title[sbt_num_fields]);
562                                 for(i = sbt_num_fields; i > 1; --i)
563                                 {
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];
567                                 }
568                                 sbt_field_title[1] = strzone("|");
569                                 sbt_field[1] = SP_SEPARATOR;
570                                 sbt_field_size[1] = stringwidth("|", false, hud_fontsize);
571                                 ++sbt_num_fields;
572                                 LOG_INFO("fixed missing field '|'");
573                         }
574                 }
575                 else if(!have_separator)
576                 {
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;
580                         ++sbt_num_fields;
581                         LOG_INFO("fixed missing field '|'");
582                 }
583                 if(!have_secondary)
584                 {
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;
588                         ++sbt_num_fields;
589                         LOG_INFOF("fixed missing field '%s'", scores_label(ps_secondary));
590                 }
591                 if(!have_primary)
592                 {
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;
596                         ++sbt_num_fields;
597                         LOG_INFOF("fixed missing field '%s'", scores_label(ps_primary));
598                 }
599         }
600
601         sbt_field[sbt_num_fields] = SP_END;
602 }
603
604 string Scoreboard_AddPlayerId(string pl_name, entity pl)
605 {
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);
609 }
610
611 // MOVEUP::
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)
621 {
622         if(ready_waiting && pl.ready)
623         {
624                 sbt_field_icon0 = "gfx/scoreboard/player_ready";
625         }
626         else if(!teamplay)
627         {
628                 int f = entcs_GetClientColors(pl.sv_entnum);
629                 {
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);
635                 }
636         }
637         
638         return entcs_GetName(pl.sv_entnum);
639 }
640
641 //LegendGuard adds GetCountrycode function 05-04-2021
642 string Scoreboard_GetCountrycode(entity pl)
643 {
644         int ccode = entcs_GetCountryCode(pl.sv_entnum);
645         if(ccode)
646                 sbt_field_icon3 = strcat("gfx/flags/", ftos(ccode));
647
648         return ftos(entcs_GetCountryCode(pl.sv_entnum));
649 }
650
651 vector getPingColor(float f)
652 {
653         if(f < 80) {
654                 // 20-80 range is green
655                 return '0 1 0' + '1 0 1' * max(0, min(60, f-20)) / 60;
656         } else {
657                 // 80-300 range is red
658                 return '1 1 1' - '0 1 1' * max(0, min(220, f-80)) / 220;
659         }
660 }
661 string Scoreboard_GetField(entity pl, PlayerScoreField field)
662 {
663         float tmp, num, denom;
664         int f;
665         string str;
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';
674         switch(field)
675         {
676                 case SP_PING:
677                         if (!pl.gotscores)
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");
680                         f = pl.ping;
681                         if(f == 0)
682                                 return _("N/A");
683                         sbt_field_rgb = getPingColor(f);
684                         
685                         return ftos(f);
686                         //return ftos(pl.team);
687
688                 case SP_PL:
689                         if (!pl.gotscores)
690                                 return _("N/A");
691                         f = pl.ping_packetloss;
692                         tmp = pl.ping_movementloss;
693                         if(f == 0 && tmp == 0)
694                                 return "";
695                         str = ftos(ceil(f * 100));
696                         if(tmp != 0)
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;
700                         return str;
701                 
702                 //LegendGuard adds Country REGISTER in the switch 05-04-2021
703                 case SP_COUNTRY:
704                 {
705                         str = Scoreboard_GetCountrycode(pl);
706                         return str;
707                 }
708
709                 case SP_NAME:
710                         str = Scoreboard_GetName(pl);
711                         if (autocvar_hud_panel_scoreboard_playerid)
712                                 str = Scoreboard_AddPlayerId(str, pl);
713                         return str;
714
715                 case SP_FRAGS:
716                         f = pl.(scores(SP_KILLS));
717                         f -= pl.(scores(SP_SUICIDES));
718                         return ftos(f);
719
720                 case SP_KDRATIO:
721                         num = pl.(scores(SP_KILLS));
722                         denom = pl.(scores(SP_DEATHS));
723
724                         if(denom == 0) {
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);
730                         } else
731                                 str = sprintf("%.1f", num/denom);
732                         return str;
733
734                 case SP_SUM:
735                         f = pl.(scores(SP_KILLS));
736                         f -= pl.(scores(SP_DEATHS));
737
738                         if(f > 0) {
739                                 sbt_field_rgb = '0 1 0';
740                         } else if(f == 0) {
741                                 sbt_field_rgb = '1 1 1';
742                         } else {
743                                 sbt_field_rgb = '1 0 0';
744                         }
745                         return ftos(f);
746
747                 case SP_ELO:
748                 {
749                         float elo = pl.(scores(SP_ELO));
750                         switch (elo) {
751                                 case -1: return "...";
752                                 case -2: return _("N/A");
753                                 default: return ftos(elo);
754                         }
755                 }
756
757                 case SP_FPS:
758                 {
759                         float fps = pl.(scores(SP_FPS));
760                         if(fps == 0)
761                         {
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)
764                         }
765
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);
768                         return ftos(fps);
769                 }
770
771                 case SP_DMG: case SP_DMGTAKEN:
772                         return sprintf("%.1f k", pl.(scores(field)) / 1000);
773
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';
781                         else
782                                 sbt_field_rgb = '1 1 1';
783                         return ScoreString(f, tmp);
784         }
785         //return "error";
786 }
787
788 float sbt_fixcolumnwidth_len;
789 float sbt_fixcolumnwidth_iconlen;
790 float sbt_fixcolumnwidth_marginlen;
791
792 string Scoreboard_FixColumnWidth(int i, string str)
793 {
794         TC(int, i);
795         float f;
796         vector sz;
797
798         sbt_fixcolumnwidth_iconlen = 0;
799
800         if(sbt_field_icon0 != "")
801         {
802                 sz = draw_getimagesize(sbt_field_icon0);
803                 f = sz.x / sz.y;
804                 if(sbt_fixcolumnwidth_iconlen < f)
805                         sbt_fixcolumnwidth_iconlen = f;
806         }
807
808         if(sbt_field_icon1 != "")
809         {
810                 sz = draw_getimagesize(sbt_field_icon1);
811                 f = sz.x / sz.y;
812                 if(sbt_fixcolumnwidth_iconlen < f)
813                         sbt_fixcolumnwidth_iconlen = f;
814         }
815
816         if(sbt_field_icon2 != "")
817         {
818                 sz = draw_getimagesize(sbt_field_icon2);
819                 f = sz.x / sz.y;
820                 if(sbt_fixcolumnwidth_iconlen < f)
821                         sbt_fixcolumnwidth_iconlen = f;
822         }
823
824         //LegendGuard adds conditional for Country column 05-04-2021
825         if(sbt_field_icon3 != "")
826         {
827                 sz = draw_getimagesize(sbt_field_icon3);
828                 f = sz.x / sz.y;
829                 if(sbt_fixcolumnwidth_iconlen < f)
830                         sbt_fixcolumnwidth_iconlen = f;
831         }
832
833         if(sbt_fixcolumnwidth_iconlen != 0)
834         {
835                 sbt_fixcolumnwidth_iconlen *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect
836                 sbt_fixcolumnwidth_marginlen = stringwidth(" ", false, hud_fontsize);
837         }
838         else
839                 sbt_fixcolumnwidth_marginlen = 0;
840
841         if(sbt_field[i] == SP_NAME) // name gets all remaining space
842         {
843                 int j;
844                 float remaining_space = 0;
845                 for(j = 0; j < sbt_num_fields; ++j)
846                         if(j != i)
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;
850
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);
856
857                 max_namesize = vid_conwidth - remaining_space;
858         }
859         else
860                 sbt_fixcolumnwidth_len = stringwidth(str, false, hud_fontsize);
861
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;
865
866         return str;
867 }
868
869 void Scoreboard_initFieldSizes()
870 {
871         for(int i = 0; i < sbt_num_fields; ++i)
872         {
873                 sbt_field_size[i] = stringwidth(sbt_field_title[i], false, hud_fontsize);
874                 Scoreboard_FixColumnWidth(i, "");
875         }
876 }
877
878 vector Scoreboard_DrawHeader(vector pos, vector rgb, bool other_players, int team)
879 {
880         int i;
881         string title_str;
882         vector title_rgb;
883         vector column_dim = eY * panel_size.y;
884         
885         if(other_players)
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)
890         {
891                 if(sbt_field[i] == SP_SEPARATOR)
892                         break;
893                 
894                 vector text_offset_center = '0 0 0';
895                 
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);
900                 } else {
901                         title_str = sbt_field_title[i];
902                         title_rgb = rgb * 1.5;
903                 }
904                 
905                 column_dim.x = sbt_field_size[i] + hud_fontsize.x;
906                 if (sbt_highlight)
907                         if (i % 2)
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;
911         }
912         if(sbt_field[i] == SP_SEPARATOR)
913         {
914                 pos.x = panel_pos.x + panel_size.x - hud_fontsize.x * 0.5;
915                 for(i = sbt_num_fields - 1; i > 0; --i)
916                 {
917                         if(sbt_field[i] == SP_SEPARATOR)
918                                 break;
919
920                         pos.x -= sbt_field_size[i];
921
922                         if (sbt_highlight)
923                                 if (!(i % 2))
924                                 {
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);
927                                 }
928
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;
932                 }
933         }
934
935         pos.x = panel_pos.x;
936         pos.y += 1.25 * hud_fontsize.y;
937         return pos;
938 }
939
940 void Scoreboard_DrawItem(vector item_pos, vector rgb, entity pl, bool is_self, int pl_number)
941 {
942         TC(bool, is_self); TC(int, pl_number);
943         string str;
944         bool is_spec = (entcs_GetSpecState(pl.sv_entnum) == ENTCS_SPEC_PURE);
945
946         vector h_pos = item_pos;
947         vector h_size = vec2(panel_size.x, hud_fontsize.y * 1.25);
948         // alternated rows highlighting
949         if(is_self)
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);
953
954         float fg_alpha = (is_self ? sbt_fg_alpha_self : sbt_fg_alpha);
955
956         vector pos = item_pos;
957         // put a "self indicator" beside the self row, unicode U+25C0 (black left-pointing triangle)
958         if (is_self)
959                 drawstring(pos + eX * (panel_size.x + 0.5 * hud_fontsize.x) + eY, "\xE2\x97\x80", hud_fontsize, rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
960
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';
964         int i;
965         PlayerScoreField field;
966         for(i = 0; i < sbt_num_fields; ++i)
967         {
968                 field = sbt_field[i];
969                 if(field == SP_SEPARATOR)
970                         break;
971
972                 if(is_spec && field != SP_NAME && field != SP_PING) {
973                         pos.x += sbt_field_size[i] + hud_fontsize.x;
974                         continue;
975                 }
976                 str = Scoreboard_GetField(pl, field);
977                 str = Scoreboard_FixColumnWidth(i, str);
978
979                 pos.x += sbt_field_size[i] + hud_fontsize.x;
980
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);
984                 } else {
985                         tmp.x = sbt_fixcolumnwidth_len + hud_fontsize.x;
986                         drawstring(pos - tmp, str, hud_fontsize, sbt_field_rgb, fg_alpha, DRAWFLAG_NORMAL);
987                 }
988
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);
998         }
999
1000         if(sbt_field[i] == SP_SEPARATOR)
1001         {
1002                 pos.x = item_pos.x + panel_size.x - hud_fontsize.x * 0.5;
1003                 for(i = sbt_num_fields-1; i > 0; --i)
1004                 {
1005                         field = sbt_field[i];
1006                         if(field == SP_SEPARATOR)
1007                                 break;
1008
1009                         if(is_spec && field != SP_NAME && field != SP_PING) {
1010                                 pos.x -= sbt_field_size[i] + hud_fontsize.x;
1011                                 continue;
1012                         }
1013
1014                         str = Scoreboard_GetField(pl, field);
1015                         str = Scoreboard_FixColumnWidth(i, str);
1016
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);
1020                         } else {
1021                                 tmp.x = sbt_fixcolumnwidth_len;
1022                                 drawstring(pos - tmp, str, hud_fontsize, sbt_field_rgb, fg_alpha, DRAWFLAG_NORMAL);
1023                         }
1024
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;
1035                 }
1036         }
1037
1038         if(pl.eliminated)
1039                 drawfill(h_pos, h_size, '0 0 0', sbt_highlight_alpha_eliminated, DRAWFLAG_NORMAL);
1040 }
1041
1042 vector Scoreboard_DrawOthers(vector item_pos, vector rgb, int this_team, entity ignored_pl, entity pl, int pl_number)
1043 {
1044         int i = 0;
1045         vector h_pos = item_pos;
1046         vector h_size = vec2(panel_size.x, hud_fontsize.y * 1.25);
1047
1048         bool complete = (this_team == NUM_SPECTATOR);
1049
1050         if(!complete)
1051         if((sbt_highlight) && (!(pl_number % 2)))
1052                 drawfill(h_pos, h_size, rgb, sbt_highlight_alpha, DRAWFLAG_NORMAL);
1053
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
1057
1058         float width_limit = item_pos.x + panel_size.x - hud_fontsize.x;
1059         if(!complete)
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;
1063         string field = "";
1064         float fieldsize = 0;
1065         float min_fieldsize = 0;
1066         float fieldpadding = hud_fontsize.x * 0.25;
1067         if(this_team == NUM_SPECTATOR)
1068         {
1069                 if(autocvar_hud_panel_scoreboard_spectators_showping)
1070                         min_fieldsize = stringwidth("999", false, hud_fontsize);
1071         }
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)
1075         {
1076                 if(pl.team != this_team)
1077                         continue;
1078                 if(pl == ignored_pl)
1079                         continue;
1080
1081                 field = "";
1082                 if(this_team == NUM_SPECTATOR)
1083                 {
1084                         if(autocvar_hud_panel_scoreboard_spectators_showping)
1085                                 field = Scoreboard_GetField(pl, SP_PING);
1086                 }
1087                 else if(autocvar_hud_panel_scoreboard_others_showscore)
1088                         field = Scoreboard_GetField(pl, SP_SCORE);
1089
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)
1096                 {
1097                         if(column_width > max_name_width)
1098                                 max_name_width = column_width;
1099                         column_width = max_name_width;
1100                 }
1101                 if(field != "")
1102                 {
1103                         fieldsize = stringwidth(field, false, hud_fontsize);
1104                         column_width += hud_fontsize.x * 0.25 + max(fieldsize, min_fieldsize) + 2 * fieldpadding;
1105                 }
1106
1107                 if(pos.x + column_width > width_limit)
1108                 {
1109                         ++i;
1110                         if(!complete)
1111                         {
1112                                 drawstring(pos, "...", hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
1113                                 break;
1114                         }
1115                         else
1116                         {
1117                                 pos.x = item_pos.x + hud_fontsize.x * 0.5;
1118                                 pos.y += hud_fontsize.y * 1.25;
1119                         }
1120                 }
1121
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);
1126                 if(field != "")
1127                 {
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;
1133                         if(sbt_highlight)
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);
1137                 }
1138                 if(pl.eliminated)
1139                 {
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);
1143                 }
1144                 pos.x += column_width;
1145                 pos.x += hud_fontsize.x;
1146         }
1147         return vec2(item_pos.x, item_pos.y + i * hud_fontsize.y * 1.25);
1148 }
1149
1150 vector Scoreboard_DrawMedal(vector pos, string icon, float height, float number)
1151 {
1152         if(!number) return pos;
1153         total_medals += number;
1154         
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);
1159         
1160         drawpic(pos, icon, tmp_sz2, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1161         
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);
1164         
1165         pos.x += stringwidth(val, false, hud_fontsize) + hud_fontsize.x * 0.5;
1166         return pos;
1167 }
1168
1169 vector Scoreboard_Duel_DrawPickup(vector pos, bool skinned, string icon, vector sz, float number, bool invert)
1170 {
1171         vector tmp_in = pos;
1172         vector tmp_sz, tmp_sz2;
1173         string picpath;
1174         
1175         // Icon
1176         if(skinned) {
1177                 picpath = strcat(hud_skin_path, "/", icon);
1178                 if(precache_pic(picpath) == "")
1179                         picpath = strcat("gfx/hud/default/", icon);
1180         } else {
1181                 picpath = icon;
1182         }
1183                 
1184         tmp_sz = draw_getimagesize(picpath);
1185         tmp_sz2 = vec2(sz.y*(tmp_sz.x/tmp_sz.y), sz.y);
1186         
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);
1189         
1190         // Number
1191         if(invert)
1192                 tmp_in.x += tmp_sz2.x + hud_fontsize.x * 0.25;
1193         else
1194                 tmp_in.x -= hud_fontsize.x * 0.25 + hud_fontsize.x;
1195         
1196         tmp_in.y += (tmp_sz2.y - hud_fontsize.y) / 2;
1197         drawstring(tmp_in,
1198                 ((number == -1) ? "?" : ftos(number)),
1199                 hud_fontsize, ((number > 0) ? '1 1 1' : '0.5 0.5 0.5'),
1200                 panel_fg_alpha,
1201                 DRAWFLAG_NORMAL);
1202         
1203         pos.y += sz.y * 1.1;
1204         return pos;
1205 }
1206
1207 void Scoreboard_Duel_DrawTable(vector pos, bool invert, entity pl, entity tm)
1208 {
1209         vector tmp, tmp_in, tmp_sz, tmp_acc;
1210         string tmp_str;
1211         float sz;
1212         float average_acc = 0;
1213         
1214         panel_pos = pos;
1215         
1216         HUD_Panel_DrawBg();
1217         
1218         // Stop here if there are no scores available
1219         if(pl.team != tm.team) return;
1220         
1221         tmp = pos;
1222         tmp.x += panel_bg_padding;
1223         tmp.y += panel_bg_padding;
1224         panel_size.x -= panel_bg_padding * 2;
1225         
1226         //if (sbt_bg_alpha)
1227         //      drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", tmp, panel_size, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL);
1228
1229         // Score: highlight
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);
1232         
1233         // Score: text
1234         tmp_str = ftos(pl.(scores(SP_SCORE)));
1235         tmp_in = tmp;
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);
1238         
1239         draw_beginBoldFont();
1240         drawstring(tmp_in, tmp_str, duel_score_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1241         draw_endBoldFont();
1242         
1243         // Player name
1244         tmp_str = Scoreboard_GetField(pl, SP_NAME);
1245         tmp_in = tmp;
1246         if(invert)
1247                 tmp_in.x -= stringwidth_colors(tmp_str, duel_name_fontsize) + duel_name_fontsize.x * 0.5;
1248         else
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);
1252         
1253         // Player icon/flag
1254         if(sbt_field_icon0 != "") {
1255                 vector rsz = draw_getimagesize(sbt_field_icon0);
1256                 sbt_fixcolumnwidth_iconlen = rsz.x / rsz.y;
1257                 if(invert)
1258                         tmp_in.x -= hud_fontsize.x * sbt_fixcolumnwidth_iconlen + duel_name_fontsize.x * 0.5;
1259                 else
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);
1263         }
1264
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;
1270                 if(invert)
1271                         tmp_in.x -= hud_fontsize.x * sbt_fixcolumnwidth_iconlen + duel_name_fontsize.x * 0.5;
1272                 else
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);
1276         }
1277         
1278         // Header
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;
1282         
1283         vector column_dim;
1284         int i;
1285
1286         i = (invert ? 4 : 0);
1287         column_dim = vec2(column_width * 4, hud_fontsize.y);
1288         
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);
1299         
1300         tmp.x = pos.x + panel_bg_padding;
1301         tmp.y += hud_fontsize.y;
1302         
1303         // Main row
1304         i = (invert ? 4 : 0);
1305         
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);
1309         
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);
1313                 
1314         tmp_acc = tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2);
1315                 
1316         if(invert)
1317                 i--;
1318         else
1319                 i++;
1320         
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);
1324         
1325         tmp.x = pos.x + panel_bg_padding;
1326         tmp.y += hud_fontsize.y * 2;
1327         
1328         tmp_in = tmp;
1329         
1330         int total_weapons = 0;
1331         
1332         // Accuracy rows
1333         WepSet weapons_inmap = WepSet_GetFromStat_InMap();
1334         FOREACH(Weapons, it != WEP_Null, {
1335                 WepSet set = it.m_wepset;
1336                 if (!(weapons_inmap & set))
1337                         continue;
1338                 if (it.spawnflags & WEP_TYPE_OTHER)
1339                         continue;
1340                 
1341                 int weapon_cnt_fired = pl.accuracy_cnt_fired[i - WEP_FIRST];
1342                 int weapon_cnt_hit   = pl.accuracy_cnt_hit[i - WEP_FIRST];
1343                 int weapon_acc = 0;
1344                 if(weapon_cnt_fired)
1345                         weapon_acc = floor((weapon_cnt_hit / weapon_cnt_fired) * 100);
1346                 average_acc += weapon_acc;
1347                 
1348                 string draw_str;
1349                 
1350                 // weapon stats
1351                 int c = (invert ? 4 : 0);
1352                 
1353                 drawfill(tmp_in + eX * column_width * (invert ? 1 : 0), column_dim, '0 0 0', sbt_highlight_alpha, DRAWFLAG_NORMAL);
1354                 
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);
1358                 
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);
1362                         
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);
1366                         
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);
1370         
1371                 // weapon icon
1372                 if(invert) {
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);
1375                 }
1376                 
1377                 tmp_in.x = pos.x + panel_bg_padding;
1378                 tmp_in.y += hud_fontsize.y * 1.25;
1379                 
1380                 if(weapon_cnt_fired)
1381                         total_weapons++;
1382         });
1383         if(total_weapons)
1384                 average_acc = floor((average_acc / total_weapons) + 0.5);
1385         
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);
1390         
1391         // Icon column
1392         vector icon_sz = vec2(column_width, hud_fontsize.y*1.5);
1393         
1394         if(!invert)
1395                 tmp.x += column_width * 4;
1396         // Medal rows
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;
1400         
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);
1404         
1405         // Item rows
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;
1409         
1410         float inv_num = -1;
1411         FOREACH(Items,
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);
1419                 
1420                 if(it.m_id == REGISTRY_MAX(Items))
1421                 break;
1422         });
1423 }
1424 vector Scoreboard_MakeDuelTable(vector pos, entity tm, vector rgb, vector bg_size)
1425 {
1426         vector end_pos = pos;
1427         float screen_half = panel_size.x / 2;
1428         float weapon_margin = hud_fontsize.x;
1429         
1430         panel_size.x = screen_half - weapon_margin;
1431         panel_size.y = (duel_score_size.y * 5.5);
1432         
1433         entity pl_left = players.sort_next;
1434         entity pl_right = pl_left.sort_next;
1435         
1436         Scoreboard_Duel_DrawTable(pos, true, pl_left, tm);
1437         Scoreboard_Duel_DrawTable(pos + eX * screen_half + eX * weapon_margin, false, pl_right, tm);
1438         
1439         end_pos.y += panel_size.y + (panel_bg_padding * 2);
1440         panel_size.x = screen_half * 2;
1441         return end_pos;
1442 }
1443
1444 vector Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
1445 {
1446         int max_players = 999;
1447         if(autocvar_hud_panel_scoreboard_maxheight > 0)
1448         {
1449                 float height = autocvar_hud_panel_scoreboard_maxheight * vid_conheight;
1450                 if(teamplay)
1451                 {
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;
1455                 }
1456                 else
1457                         height -= panel_bg_padding * 2; // - padding
1458                 max_players = floor(height / (hud_fontsize.y * 1.25));
1459                 if(max_players <= 1)
1460                         max_players = 1;
1461                 if(max_players == tm.team_size)
1462                         max_players = 999;
1463         }
1464
1465         entity pl;
1466         entity me = playerslots[current_player];
1467         panel_pos = pos;
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;
1470         HUD_Panel_DrawBg();
1471
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;
1475
1476         if(panel_bg_padding)
1477         {
1478                 panel_pos += '1 1 0' * panel_bg_padding;
1479                 panel_size -= '2 2 0' * panel_bg_padding;
1480         }
1481
1482         pos = panel_pos;
1483         vector tmp = vec2(panel_size.x, 1.25 * hud_fontsize.y);
1484
1485         // rounded header
1486         if (sbt_bg_alpha)
1487                 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', sbt_bg_alpha, DRAWFLAG_NORMAL);
1488
1489         pos.y += 1.25 * hud_fontsize.y;
1490
1491         // table background
1492         tmp.y = panel_size.y - 1.25 * hud_fontsize.y;
1493         if (sbt_bg_alpha)
1494                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL);
1495
1496
1497         // print header row and highlight columns
1498         pos = Scoreboard_DrawHeader(panel_pos, rgb, (max_players < tm.team_size), tm.team);
1499
1500         // fill the table and draw the rows
1501         bool is_self = false;
1502         bool self_shown = false;
1503         int i = 0;
1504         int with_ping = 0;
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)
1507         {
1508                 if(pl.team != tm.team)
1509                         continue;
1510                 if(i == max_players - 2 && pl != me)
1511                 {
1512                         if(!self_shown && me.team == tm.team)
1513                         {
1514                                 Scoreboard_DrawItem(pos, rgb, me, true, i);
1515                                 self_shown = true;
1516                                 pos.y += 1.25 * hud_fontsize.y;
1517                                 ++i;
1518                         }
1519                 }
1520                 if(i >= max_players - 1)
1521                 {
1522                         pos = Scoreboard_DrawOthers(pos, rgb, tm.team, (self_shown ? me : NULL), pl, i);
1523                         break;
1524                 }
1525                 is_self = (pl.sv_entnum == current_player);
1526                 Scoreboard_DrawItem(pos, rgb, pl, is_self, i);
1527                 
1528                 if(Team_IsValidTeam(tm.team) && pl.ping) {
1529                         average_ping[Team_TeamToIndex(tm.team) - 1] += pl.ping;
1530                         ++with_ping;
1531                 }
1532                 if(is_self)
1533                         self_shown = true;
1534                 pos.y += 1.25 * hud_fontsize.y;
1535                 ++i;
1536         }
1537         if(with_ping) average_ping[Team_TeamToIndex(tm.team) - 1] /= with_ping;
1538
1539         panel_size.x += panel_bg_padding * 2; // restore initial width
1540         return end_pos;
1541 }
1542
1543 bool Scoreboard_WouldDraw()
1544 {
1545         if (MUTATOR_CALLHOOK(DrawScoreboard))
1546                 return false;
1547         else if (QuickMenu_IsOpened())
1548                 return false;
1549         else if (HUD_Radar_Clickable())
1550                 return false;
1551         else if (scoreboard_showscores)
1552                 return true;
1553         else if (intermission == 1)
1554                 return true;
1555         else if (intermission == 2)
1556                 return false;
1557         else if (spectatee_status != -1 && STAT(HEALTH) <= 0 && autocvar_cl_deathscoreboard && !MUTATOR_CALLHOOK(DrawDeathScoreboard)
1558                 && (!HUD_MinigameMenu_IsOpened() || !active_minigame))
1559         {
1560                 return true;
1561         }
1562         else if (scoreboard_showscores_force || MUTATOR_CALLHOOK(DrawScoreboard_Force))
1563                 return true;
1564         return false;
1565 }
1566
1567 vector Scoreboard_MedalStats_Draw(vector pos)
1568 {
1569         vector orig = pos;
1570         float height = hud_fontsize.y * 2;
1571         
1572         entity pl = playerslots[current_player];
1573         
1574         vector title_pos = pos;
1575         pos.x += 0.5 * hud_fontsize.x + panel_bg_padding;
1576         pos.y += 1.25 * hud_fontsize.y;
1577         
1578         total_medals = 0;
1579         
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)));
1590         
1591         if(total_medals)
1592                 pos.x += hud_fontsize.x;
1593         
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)));
1599         
1600         if(!total_medals) return orig;
1601         
1602         drawstring(title_pos, sprintf(_("Medal stats (total %d)"), total_medals),
1603                 hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1604         
1605         pos.x = orig.x;
1606         pos.y += height + hud_fontsize.y * 0.5;
1607         return pos;
1608 }
1609
1610 float average_accuracy;
1611 vector Scoreboard_AccuracyStats_Draw(vector pos, vector rgb, vector bg_size)
1612 {
1613         scoreboard_acc_fade_alpha = min(scoreboard_fade_alpha, scoreboard_acc_fade_alpha + frametime * 10);
1614
1615         WepSet weapons_stat = WepSet_GetFromStat();
1616         WepSet weapons_inmap = WepSet_GetFromStat_InMap();
1617         int disownedcnt = 0;
1618         int nHidden = 0;
1619         FOREACH(Weapons, it != WEP_Null, {
1620                 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1621
1622                 WepSet set = it.m_wepset;
1623                 if(it.spawnflags & WEP_TYPE_OTHER)
1624                 {
1625                         ++nHidden;
1626                         continue;
1627                 }
1628                 if (weapon_stats < 0 && !((weapons_stat & set) || (weapons_inmap & set)))
1629                 {
1630                         if (it.spawnflags & (WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_SPECIALATTACK))
1631                                 ++nHidden;
1632                         else
1633                                 ++disownedcnt;
1634                 }
1635         });
1636
1637         int weapon_cnt = (REGISTRY_COUNT(Weapons) - 1) - disownedcnt - nHidden;
1638         if (weapon_cnt <= 0) return pos;
1639
1640         int rows = 1;
1641         if (autocvar_hud_panel_scoreboard_accuracy_doublerows && weapon_cnt >= floor((REGISTRY_COUNT(Weapons) - nHidden - 1) * 0.5))
1642                 rows = 2;
1643         int columnns = ceil(weapon_cnt / rows);
1644
1645         float weapon_height = 29;
1646         float height = hud_fontsize.y + weapon_height;
1647
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;
1652
1653         panel_pos = pos;
1654         panel_size.y = height * rows;
1655         panel_size.y += panel_bg_padding * 2;
1656
1657         float panel_bg_alpha_save = panel_bg_alpha;
1658         panel_bg_alpha *= scoreboard_acc_fade_alpha;
1659         HUD_Panel_DrawBg();
1660         panel_bg_alpha = panel_bg_alpha_save;
1661
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;
1665
1666         if(panel_bg_padding)
1667         {
1668                 panel_pos += '1 1 0' * panel_bg_padding;
1669                 panel_size -= '2 2 0' * panel_bg_padding;
1670         }
1671
1672         pos = panel_pos;
1673         vector tmp = panel_size;
1674
1675         float weapon_width = tmp.x / columnns / rows;
1676
1677         if (sbt_bg_alpha)
1678                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, sbt_bg_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL);
1679
1680         if(sbt_highlight)
1681         {
1682                 // column highlighting
1683                 for (int i = 0; i < columnns; ++i)
1684                         if ((i % 2) == 0)
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);
1686
1687                 // row highlighting
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);
1690         }
1691
1692         average_accuracy = 0;
1693         int weapons_with_stats = 0;
1694         if (rows == 2)
1695                 pos.x += weapon_width / 2;
1696
1697         if (autocvar_hud_panel_scoreboard_accuracy_nocolors)
1698                 rgb = '1 1 1';
1699         else
1700                 Accuracy_LoadColors();
1701
1702         float oldposx = pos.x;
1703         vector tmpos = pos;
1704
1705         int column = 0;
1706         FOREACH(Weapons, it != WEP_Null, {
1707                 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1708
1709                 WepSet set = it.m_wepset;
1710                 if (weapon_stats < 0 && !((weapons_stat & set) || (weapons_inmap & set)))
1711                         continue;
1712                 if (it.spawnflags & WEP_TYPE_OTHER)
1713                         continue;
1714
1715                 float weapon_alpha;
1716                 if (weapon_stats >= 0)
1717                         weapon_alpha = sbt_fg_alpha;
1718                 else
1719                         weapon_alpha = 0.2 * sbt_fg_alpha;
1720
1721                 // weapon icon
1722                 drawpic_aspect_skin(tmpos, it.model2, vec2(weapon_width, weapon_height), '1 1 1', weapon_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL);
1723                 // the accuracy
1724                 if (weapon_stats >= 0) {
1725                         weapons_with_stats += 1;
1726                         average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1727
1728                         string s;
1729                         s = sprintf("%d%%", weapon_stats * 100);
1730
1731                         float padding;
1732                         padding = (weapon_width - stringwidth(s, false, hud_fontsize)) / 2; // center the accuracy value
1733
1734                         if(!autocvar_hud_panel_scoreboard_accuracy_nocolors)
1735                                 rgb = Accuracy_GetColor(weapon_stats);
1736
1737                         drawstring(tmpos + vec2(padding, weapon_height), s, hud_fontsize, rgb, sbt_fg_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL);
1738                 }
1739                 tmpos.x += weapon_width * rows;
1740                 pos.x += weapon_width * rows;
1741                 if (rows == 2 && column == columnns - 1) {
1742                         tmpos.x = oldposx;
1743                         tmpos.y += height;
1744                         pos.y += height;
1745                 }
1746                 ++column;
1747         });
1748
1749         if (weapons_with_stats)
1750                 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1751
1752         panel_size.x += panel_bg_padding * 2; // restore initial width
1753
1754         return end_pos;
1755 }
1756
1757 .bool uninteresting;
1758 STATIC_INIT(default_order_items_label)
1759 {
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
1764                         ))
1765                 {
1766                         it.uninteresting = true;
1767                 }
1768         });
1769 }
1770
1771 vector Scoreboard_ItemStats_Draw(vector pos, vector rgb, vector bg_size)
1772 {
1773         scoreboard_itemstats_fade_alpha = min(scoreboard_fade_alpha, scoreboard_itemstats_fade_alpha + frametime * 10);
1774
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;
1782                 else if (!q)
1783                         ++disowned_cnt;
1784         });
1785         int items_cnt = REGISTRY_COUNT(Items) - uninteresting_cnt;
1786         int n = items_cnt - disowned_cnt;
1787         if (n <= 0) return pos;
1788
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));
1791
1792         float height = 40;
1793         float fontsize = height * 1/3;
1794         float item_height = height * 2/3;
1795
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;
1800
1801         panel_pos = pos;
1802         panel_size.y = height * rows;
1803         panel_size.y += panel_bg_padding * 2;
1804
1805         float panel_bg_alpha_save = panel_bg_alpha;
1806         panel_bg_alpha *= scoreboard_itemstats_fade_alpha;
1807         HUD_Panel_DrawBg();
1808         panel_bg_alpha = panel_bg_alpha_save;
1809
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;
1813
1814         if(panel_bg_padding)
1815         {
1816                 panel_pos += '1 1 0' * panel_bg_padding;
1817                 panel_size -= '2 2 0' * panel_bg_padding;
1818         }
1819
1820         pos = panel_pos;
1821         vector tmp = panel_size;
1822
1823         float item_width = tmp.x / columnns / rows;
1824
1825         if (sbt_bg_alpha)
1826                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, sbt_bg_alpha * scoreboard_itemstats_fade_alpha, DRAWFLAG_NORMAL);
1827
1828         if(sbt_highlight)
1829         {
1830                 // column highlighting
1831                 for (int i = 0; i < columnns; ++i)
1832                         if ((i % 2) == 0)
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);
1834
1835                 // row highlighting
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);
1838         }
1839
1840         if (rows == 2)
1841                 pos.x += item_width / 2;
1842
1843         float oldposx = pos.x;
1844         vector tmpos = pos;
1845
1846         int column = 0;
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);
1852                 string s = ftos(n);
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) {
1858                         tmpos.x = oldposx;
1859                         tmpos.y += height;
1860                         pos.y += height;
1861                 }
1862                 ++column;
1863         });
1864
1865         panel_size.x += panel_bg_padding * 2; // restore initial width
1866
1867         return end_pos;
1868 }
1869
1870 vector MapStats_DrawKeyValue(vector pos, string key, string value) {
1871         float px = pos.x;
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);
1876         pos.x = px;
1877         pos.y += hud_fontsize.y;
1878
1879         return pos;
1880 }
1881
1882 /*
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;
1886         float rows = 0;
1887         string val;
1888
1889         // get monster stats
1890         stat_monsters_killed = STAT(MONSTERS_KILLED);
1891         stat_monsters_total = STAT(MONSTERS_TOTAL);
1892
1893         // get secrets stats
1894         stat_secrets_found = STAT(SECRETS_FOUND);
1895         stat_secrets_total = STAT(SECRETS_TOTAL);
1896
1897         // get number of rows
1898         if(stat_secrets_total)
1899                 rows += 1;
1900         if(stat_monsters_total)
1901                 rows += 1;
1902
1903         // if no rows, return
1904         if (!rows)
1905                 return pos;
1906
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;
1912
1913         panel_pos = pos;
1914         panel_size.y = hud_fontsize.y * rows;
1915         panel_size.y += panel_bg_padding * 2;
1916         HUD_Panel_DrawBg();
1917
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;
1921
1922         if(panel_bg_padding)
1923         {
1924                 panel_pos += '1 1 0' * panel_bg_padding;
1925                 panel_size -= '2 2 0' * panel_bg_padding;
1926         }
1927
1928         pos = panel_pos;
1929         vector tmp = panel_size;
1930
1931         if (sbt_bg_alpha)
1932                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL);
1933
1934         // draw monsters
1935         if(stat_monsters_total)
1936         {
1937                 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1938                 pos = MapStats_DrawKeyValue(pos, _("Monsters killed:"), val);
1939         }
1940
1941         // draw secrets
1942         if(stat_secrets_total)
1943         {
1944                 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1945                 pos = MapStats_DrawKeyValue(pos, _("Secrets found:"), val);
1946         }
1947
1948         panel_size.x += panel_bg_padding * 2; // restore initial width
1949         return end_pos;
1950 }
1951 */
1952
1953 vector Scoreboard_Rankings_Draw(vector pos, string ranktitle, entity pl, vector rgb, vector bg_size)
1954 {
1955         int i;
1956         RANKINGS_RECEIVED_CNT = 0;
1957         for (i=RANKINGS_CNT-1; i>=0; --i)
1958                 if (grecordtime[i])
1959                         ++RANKINGS_RECEIVED_CNT;
1960
1961         if (RANKINGS_RECEIVED_CNT == 0)
1962                 return pos;
1963
1964         vector hl_rgb = rgb + '0.5 0.5 0.5';
1965
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;
1971
1972         panel_pos = pos;
1973
1974         float namesize = 0;
1975         for(i = 0; i < RANKINGS_RECEIVED_CNT; ++i)
1976         {
1977                 float f = stringwidth(ColorTranslateRGB(grecordholder[i]), true, hud_fontsize);
1978                 if(f > namesize)
1979                         namesize = f;
1980         }
1981         bool cut = false;
1982         if(namesize > autocvar_hud_panel_scoreboard_namesize * hud_fontsize.x)
1983         {
1984                 namesize = autocvar_hud_panel_scoreboard_namesize * hud_fontsize.x;
1985                 cut = true;
1986         }
1987
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);
1993
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;
1998
1999         panel_size.y = ceil(RANKINGS_RECEIVED_CNT / columns) * 1.25 * hud_fontsize.y;
2000         panel_size.y += panel_bg_padding * 2;
2001
2002         HUD_Panel_DrawBg();
2003
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;
2007
2008         if(panel_bg_padding)
2009         {
2010                 panel_pos += '1 1 0' * panel_bg_padding;
2011                 panel_size -= '2 2 0' * panel_bg_padding;
2012         }
2013
2014         pos = panel_pos;
2015
2016         if (sbt_bg_alpha)
2017                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, panel_size, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL);
2018
2019         vector text_ofs = vec2(0.5 * hud_fontsize.x, (1.25 - 1) / 2 * hud_fontsize.y); // center text vertically
2020         string str = "";
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)
2024         {
2025                 float t;
2026                 t = grecordtime[i];
2027                 if (t == 0)
2028                         continue;
2029
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);
2034
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]);
2039                 if(cut)
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);
2042
2043                 pos.y += 1.25 * hud_fontsize.y;
2044                 j++;
2045                 if(j >= ceil(RANKINGS_RECEIVED_CNT / columns))
2046                 {
2047                         column++;
2048                         j = 0;
2049                         pos.x += panel_size.x / columns;
2050                         pos.y = panel_pos.y;
2051                 }
2052         }
2053         strfree(zoned_name_self);
2054
2055         panel_size.x += panel_bg_padding * 2; // restore initial width
2056         return end_pos;
2057 }
2058
2059 float scoreboard_time;
2060 bool have_weapon_stats;
2061 bool Scoreboard_AccuracyStats_WouldDraw(float ypos)
2062 {
2063         if (MUTATOR_CALLHOOK(DrawScoreboardAccuracy))
2064                 return false;
2065         if (!autocvar_hud_panel_scoreboard_accuracy || warmup_stage || ypos > 0.91 * vid_conheight)
2066                 return false;
2067
2068         if (time < scoreboard_time + autocvar_hud_panel_scoreboard_accuracy_showdelay
2069                 && ypos > autocvar_hud_panel_scoreboard_accuracy_showdelay_minpos * vid_conheight
2070                 && !intermission)
2071         {
2072                 return false;
2073         }
2074
2075         if (!have_weapon_stats)
2076         {
2077                 FOREACH(Weapons, it != WEP_Null, {
2078                         int weapon_stats = weapon_accuracy[i - WEP_FIRST];
2079                         if (weapon_stats >= 0)
2080                         {
2081                                 have_weapon_stats = true;
2082                                 break;
2083                         }
2084                 });
2085                 if (!have_weapon_stats)
2086                         return false;
2087         }
2088
2089         return true;
2090 }
2091
2092 bool have_item_stats;
2093 bool Scoreboard_ItemStats_WouldDraw(float ypos)
2094 {
2095         if (MUTATOR_CALLHOOK(DrawScoreboardItemStats))
2096                 return false;
2097         if (!autocvar_hud_panel_scoreboard_itemstats || !g_inventory || warmup_stage || ypos > 0.91 * vid_conheight)
2098                 return false;
2099
2100         if (time < scoreboard_time + autocvar_hud_panel_scoreboard_itemstats_showdelay
2101                 && ypos > autocvar_hud_panel_scoreboard_itemstats_showdelay_minpos * vid_conheight
2102                 && !intermission)
2103         {
2104                 return false;
2105         }
2106
2107         if (!have_item_stats)
2108         {
2109                 IL_EACH(default_order_items, true, {
2110                         if (!(autocvar_hud_panel_scoreboard_itemstats_filter && it.uninteresting))
2111                         {
2112                                 int q = g_inventory.inv_items[it.m_id];
2113                                 //q = 1; // debug: display all items
2114                                 if (q)
2115                                 {
2116                                         have_item_stats = true;
2117                                         break;
2118                                 }
2119                         }
2120                 });
2121                 if (!have_item_stats)
2122                         return false;
2123         }
2124
2125         return true;
2126 }
2127
2128 void Scoreboard_Draw()
2129 {
2130         if(!autocvar__hud_configure)
2131         {
2132                 if(!hud_draw_maximized) return;
2133
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);
2143                         else
2144                                 scoreboard_fade_alpha = 1;
2145                         if(hud_fontsize_str != autocvar_hud_fontsize)
2146                         {
2147                                 hud_fontsize = HUD_GetFontsize("hud_fontsize");
2148                                 Scoreboard_initFieldSizes();
2149                                 strcpy(hud_fontsize_str, autocvar_hud_fontsize);
2150                         }
2151                 }
2152                 else {
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);
2156                         else
2157                                 scoreboard_fade_alpha = 0;
2158                 }
2159
2160                 if (!scoreboard_fade_alpha)
2161                 {
2162                         scoreboard_acc_fade_alpha = 0;
2163                         scoreboard_itemstats_fade_alpha = 0;
2164                         return;
2165                 }
2166         }
2167         else
2168                 scoreboard_fade_alpha = 0;
2169
2170         if (autocvar_hud_panel_scoreboard_dynamichud)
2171                 HUD_Scale_Enable();
2172         else
2173                 HUD_Scale_Disable();
2174
2175         if(scoreboard_fade_alpha <= 0)
2176                 return;
2177         panel_fade_alpha *= scoreboard_fade_alpha;
2178         HUD_Panel_LoadCvars();
2179
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;
2187
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);
2191
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;
2196
2197         Scoreboard_UpdatePlayerTeams();
2198
2199         float initial_pos_y = panel_pos.y;
2200         vector pos = panel_pos;
2201         entity pl, tm;
2202         string str;
2203         vector str_pos;
2204
2205         vector sb_gameinfo_type_fontsize, sb_gameinfo_detail_fontsize;
2206
2207         // Begin of Game Info Section
2208         sb_gameinfo_type_fontsize = hud_fontsize * 2.5;
2209         sb_gameinfo_detail_fontsize = hud_fontsize * 1.3;
2210
2211         // z411 server name
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;
2215         
2216         // Game Info: Game Type
2217         str = MapInfo_Type_ToText(gametype);
2218         
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);
2222         draw_endBoldFont();
2223         
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);
2227
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);
2229         
2230         pos.y += sb_gameinfo_type_fontsize.y;
2231         
2232         // z411 servername
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);
2234         
2235         pos.y += sb_gameinfo_detail_fontsize.y;
2236         
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);
2242         str = "";
2243         if(tl > 0)
2244                 str = strcat(str, sprintf(_("^3%1.0f minutes"), tl));
2245         if(!gametype.m_hidelimits)
2246         {
2247                 if(fl > 0)
2248                 {
2249                         if(tl > 0)
2250                                 str = strcat(str, "^7 / "); // delimiter
2251                         if(teamplay)
2252                         {
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))));
2257                         }
2258                         else
2259                         {
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))));
2264                         }
2265                 }
2266                 if(ll > 0)
2267                 {
2268                         if(tl > 0 || fl > 0)
2269                         {
2270                                 // delimiter
2271                                 if (ll_and_fl && fl > 0)
2272                                         str = strcat(str, "^7 & ");
2273                                 else
2274                                         str = strcat(str, "^7 / ");
2275                         }
2276
2277                         if(teamplay)
2278                         {
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))));
2283                         }
2284                         else
2285                         {
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))));
2290                         }
2291                 }
2292         }
2293
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
2295         // map name
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
2299
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;
2303
2304         // Draw the scoreboard
2305         float scale = autocvar_hud_panel_scoreboard_table_bg_scale;
2306         if(scale <= 0)
2307                 scale = 0.25;
2308         vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * scale;
2309
2310         if(teamplay)
2311         {
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
2316                 {
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")
2321                         {
2322                                 team_score_baseoffset.x -= panel_bg_border;
2323                                 team_size_baseoffset.x += panel_bg_border;
2324                         }
2325                 }
2326                 else
2327                 {
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")
2332                         {
2333                                 team_score_baseoffset.x += panel_bg_border;
2334                                 team_size_baseoffset.x -= panel_bg_border;
2335                         }
2336                 }
2337
2338                 int team_size_total = 0;
2339                 if (autocvar_hud_panel_scoreboard_team_size_position != 0) // team size not off
2340                 {
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;
2345                 }
2346
2347                 for(tm = teams.sort_next; tm; tm = tm.sort_next)
2348                 {
2349                         if(tm.team == NUM_SPECTATOR)
2350                                 continue;
2351                         if(!tm.team)
2352                                 continue;
2353
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
2358                         {
2359                                 // team score on the left (default)
2360                                 str_pos = pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 3);
2361                         }
2362                         else
2363                         {
2364                                 // team score on the right
2365                                 str_pos = pos + team_score_baseoffset + eX * (panel_size.x + hud_fontsize.x * 3);
2366                         }
2367                         drawstring(str_pos, str, hud_fontsize * 3, rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
2368
2369                         // team size (if set to show on the side)
2370                         if (autocvar_hud_panel_scoreboard_team_size_position != 0) // team size not off
2371                         {
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)
2375                                 {
2376                                         // team size on the left
2377                                         str_pos = pos + team_size_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5);
2378                                 }
2379                                 else
2380                                 {
2381                                         // team size on the right
2382                                         str_pos = pos + team_size_baseoffset + eX * (panel_size.x + hud_fontsize.x * 1.5);
2383                                 }
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);
2389                         }
2390
2391
2392                         // secondary score, e.g. keyhunt
2393                         if(ts_primary != ts_secondary)
2394                         {
2395                                 str = ftos(tm.(teamscores(ts_secondary)));
2396                                 if (autocvar_hud_panel_scoreboard_team_size_position != 1) // team size not on left
2397                                 {
2398                                         // left
2399                                         str_pos = pos + team_score_baseoffset - vec2(stringwidth(str, false, hud_fontsize), hud_fontsize.y * -1.5);
2400                                 }
2401                                 else
2402                                 {
2403                                         // right
2404                                         str_pos = pos + team_score_baseoffset + vec2(panel_size.x + hud_fontsize.x * 1.5, hud_fontsize.y * 1.5);
2405                                 }
2406
2407                                 drawstring(str_pos, str, hud_fontsize, rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
2408                         }
2409                         draw_endBoldFont();
2410                         */
2411                         
2412                         // z411 My team header
2413                         // Score: highlight
2414                         drawfill(pos, team_score_size, rgb * 0.5, sbt_highlight_alpha, DRAWFLAG_NORMAL);
2415                         
2416                         // Score: text
2417                         str = ftos(tm.(teamscores(ts_primary)));
2418                         str_pos = pos;
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);
2421                         
2422                         draw_beginBoldFont();
2423                         drawstring(str_pos, str, team_score_fontsize, rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
2424                         draw_endBoldFont();
2425                         
2426                         // Team name
2427                         str = Team_CustomName(tm.team);
2428                         str_pos = pos;
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);
2432                         
2433                         pos.y += team_score_size.y + (hud_fontsize.y * 0.5);
2434                         
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;
2439                         else
2440                                 panel_bg_color = rgb;
2441                         pos = Scoreboard_MakeTable(pos, tm, panel_bg_color, bg_size);
2442                 }
2443                 panel_bg_color = panel_bg_color_save;
2444         }
2445         else if(gametype == MAPINFO_TYPE_DUEL)
2446         {
2447                 for(tm = teams.sort_next; tm; tm = tm.sort_next)
2448                         if(tm.team != NUM_SPECTATOR)
2449                                 break;
2450                 
2451                 // z411 make DUEL TABLE
2452                 pos = Scoreboard_MakeDuelTable(pos, tm, panel_bg_color, bg_size);
2453         }
2454         else
2455         {
2456                 for(tm = teams.sort_next; tm; tm = tm.sort_next)
2457                         if(tm.team != NUM_SPECTATOR)
2458                                 break;
2459
2460                 // display it anyway
2461                 pos = Scoreboard_MakeTable(pos, tm, panel_bg_color, bg_size);
2462         }
2463
2464         pos = Scoreboard_MedalStats_Draw(pos);
2465         
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);
2470
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;
2476                 }
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;
2480                 }
2481                 pos = Scoreboard_Rankings_Draw(pos, ranktitle, playerslots[player_localnum], panel_bg_color, bg_size);
2482         }
2483
2484         //pos = Scoreboard_MapStats_Draw(pos, panel_bg_color, bg_size);
2485
2486         // List spectators
2487         for(pl = players.sort_next; pl; pl = pl.sort_next)
2488         {
2489                 if(pl.team == NUM_SPECTATOR)
2490                 {
2491                         for(tm = teams.sort_next; tm; tm = tm.sort_next)
2492                                 if(tm.team == NUM_SPECTATOR)
2493                                         break;
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);
2497                         draw_endBoldFont();
2498                         pos.y += 1.25 * hud_fontsize.y;
2499
2500                         pos = Scoreboard_DrawOthers(pos, '0 0 0', pl.team, NULL, pl, 0);
2501                         pos.y += 1.25 * hud_fontsize.y;
2502
2503                         break;
2504                 }
2505         }
2506
2507
2508         // print information about respawn status
2509         float respawn_time = STAT(RESPAWN_TIME);
2510         if(!intermission)
2511         if(respawn_time)
2512         {
2513                 if(respawn_time < 0)
2514                 {
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
2517
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
2520                         else
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)
2524                                                 :
2525                                                 count_seconds(ceil(respawn_time - time))
2526                                         )
2527                                 );
2528                 }
2529                 else if(time < respawn_time)
2530                 {
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)
2534                                         :
2535                                         count_seconds(ceil(respawn_time - time))
2536                                 )
2537                         );
2538                 }
2539                 else if(time >= respawn_time)
2540                         str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
2541
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);
2544         }
2545
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)
2550         {
2551                 if (pos.y > scoreboard_bottom)
2552                         scoreboard_bottom = min(pos.y, scoreboard_bottom + frametime * 10 * (pos.y - initial_pos_y));
2553                 else
2554                         scoreboard_bottom = max(pos.y, scoreboard_bottom - frametime * 10 * (pos.y - initial_pos_y));
2555         }
2556 }