]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/client/hud/panel/scoreboard.qc
Merge branch 'z411/team_queue' into z411/bai-server
[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/physics.qh>
6 #include <client/hud/panel/quickmenu.qh>
7 #include <client/hud/panel/racetimer.qh>
8 #include <client/hud/panel/weapons.qh>
9 #include <common/constants.qh>
10 #include <common/ent_cs.qh>
11 #include <common/mapinfo.qh>
12 #include <common/gamemodes/gamemode/duel/duel.qh>
13 #include <common/minigames/cl_minigames.qh>
14 #include <common/net_linked.qh>
15 #include <common/scores.qh>
16 #include <common/stats.qh>
17 #include <common/teams.qh>
18 #include <common/items/inventory.qh>
19
20 // Scoreboard (#24)
21
22 void Scoreboard_Draw_Export(int fh)
23 {
24         // allow saving cvars that aesthetically change the panel into hud skin files
25         HUD_Write_Cvar("hud_panel_scoreboard_fadeinspeed");
26         HUD_Write_Cvar("hud_panel_scoreboard_fadeoutspeed");
27         HUD_Write_Cvar("hud_panel_scoreboard_respawntime_decimals");
28         HUD_Write_Cvar("hud_panel_scoreboard_table_bg_alpha");
29         HUD_Write_Cvar("hud_panel_scoreboard_table_bg_scale");
30         HUD_Write_Cvar("hud_panel_scoreboard_table_fg_alpha");
31         HUD_Write_Cvar("hud_panel_scoreboard_table_fg_alpha_self");
32         HUD_Write_Cvar("hud_panel_scoreboard_table_highlight");
33         HUD_Write_Cvar("hud_panel_scoreboard_table_highlight_alpha");
34         HUD_Write_Cvar("hud_panel_scoreboard_table_highlight_alpha_self");
35         HUD_Write_Cvar("hud_panel_scoreboard_table_highlight_alpha_eliminated");
36         HUD_Write_Cvar("hud_panel_scoreboard_bg_teams_color_team");
37         HUD_Write_Cvar("hud_panel_scoreboard_accuracy_doublerows");
38         HUD_Write_Cvar("hud_panel_scoreboard_accuracy_nocolors");
39         HUD_Write_Cvar("hud_panel_scoreboard_spectators_position");
40 }
41
42 const int MAX_SBT_FIELDS = MAX_SCORE;
43
44 PlayerScoreField sbt_field[MAX_SBT_FIELDS + 1];
45 float sbt_field_size[MAX_SBT_FIELDS + 1];
46 string sbt_field_title[MAX_SBT_FIELDS + 1];
47 int sbt_num_fields;
48
49 string autocvar_hud_fontsize;
50 string hud_fontsize_str;
51 float max_namesize;
52
53 vector duel_score_fontsize;
54 vector duel_name_fontsize;
55 vector duel_score_size;
56 vector team_score_fontsize;
57 vector team_name_fontsize;
58 vector team_score_size;
59 int total_medals;
60
61 float autocvar_hud_panel_scoreboard_duel_weapon_scale = 1.25; // z411
62
63 float sbt_bg_alpha;
64 float sbt_fg_alpha;
65 float sbt_fg_alpha_self;
66 bool sbt_highlight;
67 float sbt_highlight_alpha;
68 float sbt_highlight_alpha_self;
69 float sbt_highlight_alpha_eliminated;
70
71 // provide basic panel cvars to old clients
72 // TODO remove them after a future release (0.8.2+)
73 noref string autocvar_hud_panel_scoreboard_pos = "0.150000 0.150000";
74 noref string autocvar_hud_panel_scoreboard_size = "0.700000 0.700000";
75 noref string autocvar_hud_panel_scoreboard_bg = "border_default";
76 noref string autocvar_hud_panel_scoreboard_bg_color = "0 0.3 0.5";
77 noref string autocvar_hud_panel_scoreboard_bg_color_team = "";
78 noref string autocvar_hud_panel_scoreboard_bg_alpha = "0.7";
79 noref string autocvar_hud_panel_scoreboard_bg_border = "";
80 noref string autocvar_hud_panel_scoreboard_bg_padding = "";
81
82 float autocvar_hud_panel_scoreboard_fadeinspeed = 10;
83 float autocvar_hud_panel_scoreboard_fadeoutspeed = 5;
84 float autocvar_hud_panel_scoreboard_respawntime_decimals = 1;
85 float autocvar_hud_panel_scoreboard_table_bg_alpha = 0;
86 float autocvar_hud_panel_scoreboard_table_bg_scale = 0.25;
87 float autocvar_hud_panel_scoreboard_table_fg_alpha = 0.9;
88 float autocvar_hud_panel_scoreboard_table_fg_alpha_self = 1;
89 bool autocvar_hud_panel_scoreboard_table_highlight = true;
90 float autocvar_hud_panel_scoreboard_table_highlight_alpha = 0.2;
91 float autocvar_hud_panel_scoreboard_table_highlight_alpha_self = 0.4;
92 float autocvar_hud_panel_scoreboard_table_highlight_alpha_eliminated = 0.6;
93 float autocvar_hud_panel_scoreboard_bg_teams_color_team = 0;
94 float autocvar_hud_panel_scoreboard_team_size_position = 0;
95 float autocvar_hud_panel_scoreboard_spectators_position = 1;
96
97 bool autocvar_hud_panel_scoreboard_accuracy = true;
98 bool autocvar_hud_panel_scoreboard_accuracy_doublerows = false;
99 bool autocvar_hud_panel_scoreboard_accuracy_nocolors = false;
100 float autocvar_hud_panel_scoreboard_accuracy_showdelay = 2;
101 float autocvar_hud_panel_scoreboard_accuracy_showdelay_minpos = 0.75;
102
103 bool autocvar_hud_panel_scoreboard_itemstats = true;
104 bool autocvar_hud_panel_scoreboard_itemstats_doublerows = false;
105 int autocvar_hud_panel_scoreboard_itemstats_filter = 1;
106 int autocvar_hud_panel_scoreboard_itemstats_filter_mask = 12;
107 float autocvar_hud_panel_scoreboard_itemstats_showdelay = 2.2; // slightly more delayed than accuracy
108 float autocvar_hud_panel_scoreboard_itemstats_showdelay_minpos = 0.75;
109
110 bool autocvar_hud_panel_scoreboard_dynamichud = false;
111
112 float autocvar_hud_panel_scoreboard_maxheight = 0.6;
113 bool autocvar_hud_panel_scoreboard_others_showscore = true;
114 bool autocvar_hud_panel_scoreboard_spectators_showping = true;
115 bool autocvar_hud_panel_scoreboard_spectators_aligned = false;
116 float autocvar_hud_panel_scoreboard_minwidth = 0.4;
117 bool autocvar_hud_panel_scoreboard_playerid = false;
118 string autocvar_hud_panel_scoreboard_playerid_prefix = "#";
119 string autocvar_hud_panel_scoreboard_playerid_suffix = " ";
120 bool autocvar_hud_panel_scoreboard_scores_per_round;
121
122 int average_ping[NUM_TEAMS];
123 int total_weapons;
124
125 float scoreboard_time;
126
127 SHUTDOWN(scoreboard)
128 {
129         if(autocvar_hud_panel_scoreboard_scores_per_round)
130                 cvar_set("hud_panel_scoreboard_scores_per_round", "0");
131 }
132
133 // mode 0: returns translated label
134 // mode 1: prints name and description of all the labels
135 string Label_getInfo(string label, int mode)
136 {
137         if (mode == 1)
138                 label = "bckills"; // first case in the switch
139
140         switch(label)
141         {
142                 case "bckills":      if (!mode) return CTX(_("SCO^bckills"));      else LOG_HELP(strcat("^3", "bckills", "            ^7", _("Number of ball carrier kills")));
143                 case "bctime":       if (!mode) return CTX(_("SCO^bctime"));       else LOG_HELP(strcat("^3", "bctime", "             ^7", _("Total amount of time holding the ball in Keepaway")));
144                 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")));
145                 case "captime":      if (!mode) return CTX(_("SCO^captime"));      else LOG_HELP(strcat("^3", "captime", "            ^7", _("Time of fastest capture (CTF)")));
146                 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
147                 case "deaths":       if (!mode) return CTX(_("SCO^deaths"));       else LOG_HELP(strcat("^3", "deaths", "             ^7", _("Number of deaths")));
148                 case "destroyed":    if (!mode) return CTX(_("SCO^destroyed"));    else LOG_HELP(strcat("^3", "destroyed", "          ^7", _("Number of keys destroyed by pushing them into void")));
149                 case "dmg":          if (!mode) return CTX(_("SCO^damage"));       else LOG_HELP(strcat("^3", "dmg", "                ^7", _("The total damage done")));
150                 case "dmgtaken":     if (!mode) return CTX(_("SCO^dmgtaken"));     else LOG_HELP(strcat("^3", "dmgtaken", "           ^7", _("The total damage taken")));
151                 case "drops":        if (!mode) return CTX(_("SCO^drops"));        else LOG_HELP(strcat("^3", "drops", "              ^7", _("Number of flag drops")));
152                 case "elo":          if (!mode) return CTX(_("SCO^elo"));          else LOG_HELP(strcat("^3", "elo", "                ^7", _("Player ELO")));
153                 case "fastest":      if (!mode) return CTX(_("SCO^fastest"));      else LOG_HELP(strcat("^3", "fastest", "            ^7", _("Time of fastest lap (Race/CTS)")));
154                 case "faults":       if (!mode) return CTX(_("SCO^faults"));       else LOG_HELP(strcat("^3", "faults", "             ^7", _("Number of faults committed")));
155                 case "fckills":      if (!mode) return CTX(_("SCO^fckills"));      else LOG_HELP(strcat("^3", "fckills", "            ^7", _("Number of flag carrier kills")));
156                 case "fps":          if (!mode) return CTX(_("SCO^fps"));          else LOG_HELP(strcat("^3", "fps", "                ^7", _("FPS")));
157                 case "frags":        if (!mode) return CTX(_("SCO^frags"));        else LOG_HELP(strcat("^3", "frags", "              ^7", _("Number of kills minus suicides")));
158                 case "goals":        if (!mode) return CTX(_("SCO^goals"));        else LOG_HELP(strcat("^3", "goals", "              ^7", _("Number of goals scored")));
159                 case "hunts":        if (!mode) return CTX(_("SCO^hunts"));        else LOG_HELP(strcat("^3", "hunts", "              ^7", _("Number of hunts (Survival)")));
160                 case "kckills":      if (!mode) return CTX(_("SCO^kckills"));      else LOG_HELP(strcat("^3", "kckills", "            ^7", _("Number of keys carrier kills")));
161                 case "kd":           if (!mode) return CTX(_("SCO^k/d"));          else LOG_HELP(strcat("^3", "kd", "                 ^7", _("The kill-death ratio")));
162                 case "kdr":          if (!mode) return CTX(_("SCO^kdr"));          else LOG_HELP(strcat("^3", "kdr", "                ^7", _("The kill-death ratio")));
163                 case "kdratio":      if (!mode) return CTX(_("SCO^kdratio"));      else LOG_HELP(strcat("^3", "kdratio", "            ^7", _("The kill-death ratio")));
164                 case "kills":        if (!mode) return CTX(_("SCO^kills"));        else LOG_HELP(strcat("^3", "kills", "              ^7", _("Number of kills")));
165                 case "laps":         if (!mode) return CTX(_("SCO^laps"));         else LOG_HELP(strcat("^3", "laps", "               ^7", _("Number of laps finished (Race/CTS)")));
166                 case "lives":        if (!mode) return CTX(_("SCO^lives"));        else LOG_HELP(strcat("^3", "lives", "              ^7", _("Number of lives (LMS)")));
167                 case "losses":       if (!mode) return CTX(_("SCO^losses"));       else LOG_HELP(strcat("^3", "losses", "             ^7", _("Number of times a key was lost")));
168                 case "name":         if (!mode) return CTX(_("SCO^name"));         else LOG_HELP(strcat("^3", "name", "               ^7", _("Player name")));
169                 case "nick":         if (!mode) return CTX(_("SCO^nick"));         else LOG_HELP(strcat("^3", "nick", "               ^7", _("Player name")));
170                 case "objectives":   if (!mode) return CTX(_("SCO^objectives"));   else LOG_HELP(strcat("^3", "objectives", "         ^7", _("Number of objectives destroyed")));
171                 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")));
172                 case "ping":         if (!mode) return CTX(_("SCO^ping"));         else LOG_HELP(strcat("^3", "ping", "               ^7", _("Ping time")));
173                 case "pl":           if (!mode) return CTX(_("SCO^pl"));           else LOG_HELP(strcat("^3", "pl", "                 ^7", _("Packet loss")));
174                 case "pushes":       if (!mode) return CTX(_("SCO^pushes"));       else LOG_HELP(strcat("^3", "pushes", "             ^7", _("Number of players pushed into void")));
175                 case "rank":         if (!mode) return CTX(_("SCO^rank"));         else LOG_HELP(strcat("^3", "rank", "               ^7", _("Player rank")));
176                 case "returns":      if (!mode) return CTX(_("SCO^returns"));      else LOG_HELP(strcat("^3", "returns", "            ^7", _("Number of flag returns")));
177                 case "revivals":     if (!mode) return CTX(_("SCO^revivals"));     else LOG_HELP(strcat("^3", "revivals", "           ^7", _("Number of revivals")));
178                 case "rounds":       if (!mode) return CTX(_("SCO^rounds won"));   else LOG_HELP(strcat("^3", "rounds", "             ^7", _("Number of rounds won")));
179                 case "rounds_pl":    if (!mode) return CTX(_("SCO^rounds played"));else LOG_HELP(strcat("^3", "rounds_pl", "          ^7", _("Number of rounds played")));
180                 case "score":        if (!mode) return CTX(_("SCO^score"));        else LOG_HELP(strcat("^3", "score", "              ^7", _("Total score")));
181                 case "suicides":     if (!mode) return CTX(_("SCO^suicides"));     else LOG_HELP(strcat("^3", "suicides", "           ^7", _("Number of suicides")));
182                 case "sum":          if (!mode) return CTX(_("SCO^sum"));          else LOG_HELP(strcat("^3", "sum", "                ^7", _("Number of kills minus deaths")));
183                 case "survivals":    if (!mode) return CTX(_("SCO^survivals"));    else LOG_HELP(strcat("^3", "survivals", "          ^7", _("Number of survivals")));
184                 case "takes":        if (!mode) return CTX(_("SCO^takes"));        else LOG_HELP(strcat("^3", "takes", "              ^7", _("Number of domination points taken (Domination)")));
185                 case "teamkills":    if (!mode) return CTX(_("SCO^teamkills"));    else LOG_HELP(strcat("^3", "teamkills", "          ^7", _("Number of teamkills")));
186                 case "ticks":        if (!mode) return CTX(_("SCO^ticks"));        else LOG_HELP(strcat("^3", "ticks", "              ^7", _("Number of ticks (Domination)")));
187                 case "time":         if (!mode) return CTX(_("SCO^time"));         else LOG_HELP(strcat("^3", "time", "               ^7", _("Total time raced (Race/CTS)")));
188                 default: return label;
189         }
190         return label;
191 }
192
193 bool scoreboard_ui_disabling;
194 void HUD_Scoreboard_UI_Disable()
195 {
196         scoreboard_ui_disabling = true;
197         sb_showscores = false;
198 }
199
200 void HUD_Scoreboard_UI_Disable_Instantly()
201 {
202         scoreboard_ui_disabling = false;
203         scoreboard_ui_enabled = 0;
204         scoreboard_selected_panel = 0;
205         scoreboard_selected_player = NULL;
206         scoreboard_selected_team = NULL;
207 }
208
209 // mode: 0 normal, 1 team selection
210 void Scoreboard_UI_Enable(int mode)
211 {
212         if(isdemo()) return;
213
214         if (mode == 1)
215         {
216                 if (scoreboard_ui_enabled == 2 || !teamplay || intermission)
217                         return;
218
219                 // release player's pressed keys as they aren't released elsewhere
220                 // in particular jump needs to be released as it may open the team selection
221                 // (when server detects jump has been pressed it sends the command to open the team selection)
222                 Release_Common_Keys();
223                 scoreboard_ui_enabled = 2;
224                 scoreboard_selected_panel = SB_PANEL_SCOREBOARD;
225         }
226         else
227         {
228                 if (scoreboard_ui_enabled == 1)
229                         return;
230                 scoreboard_ui_enabled = 1;
231                 scoreboard_selected_panel = SB_PANEL_FIRST;
232         }
233         scoreboard_selected_player = NULL;
234         scoreboard_selected_team = NULL;
235         scoreboard_selected_panel_time = time;
236 }
237
238 int rankings_start_column;
239 int rankings_rows = 0;
240 int rankings_columns = 0;
241 int rankings_cnt = 0;
242 float HUD_Scoreboard_InputEvent(float bInputType, float nPrimary, float nSecondary)
243 {
244         string s;
245
246         if(!scoreboard_ui_enabled || scoreboard_ui_disabling)
247                 return false;
248
249         if(bInputType == 3)
250         {
251                 mousepos.x = nPrimary;
252                 mousepos.y = nSecondary;
253                 return true;
254         }
255
256         if(bInputType == 2)
257                 return false;
258
259         // at this point bInputType can only be 0 or 1 (key pressed or released)
260         bool key_pressed = (bInputType == 0);
261
262         // ESC to exit (TAB-ESC works too)
263         if(nPrimary == K_ESCAPE)
264         {
265                 if (!key_pressed)
266                         return true;
267                 HUD_Scoreboard_UI_Disable();
268                 return true;
269         }
270
271         // block any input while a menu dialog is fading
272         if(autocvar__menu_alpha)
273         {
274                 hudShiftState = 0;
275                 return true;
276         }
277
278         // allow console bind to work
279         string con_keys = findkeysforcommand("toggleconsole", 0);
280         int keys = tokenize(con_keys); // findkeysforcommand returns data for this
281
282         bool hit_con_bind = false;
283         int i;
284         for (i = 0; i < keys; ++i)
285         {
286                 if(nPrimary == stof(argv(i)))
287                         hit_con_bind = true;
288         }
289
290         if(key_pressed) {
291                 if(nPrimary == K_ALT) hudShiftState |= S_ALT;
292                 if(nPrimary == K_CTRL) hudShiftState |= S_CTRL;
293                 if(nPrimary == K_SHIFT) hudShiftState |= S_SHIFT;
294                 if(nPrimary == K_TAB) hudShiftState |= S_TAB;
295         }
296         else {
297                 if(nPrimary == K_ALT) hudShiftState -= (hudShiftState & S_ALT);
298                 if(nPrimary == K_CTRL) hudShiftState -= (hudShiftState & S_CTRL);
299                 if(nPrimary == K_SHIFT) hudShiftState -= (hudShiftState & S_SHIFT);
300                 if(nPrimary == K_TAB) hudShiftState -= (hudShiftState & S_TAB);
301         }
302
303         if(nPrimary == K_TAB)
304         {
305                 if (!key_pressed)
306                         return true;
307                 if (scoreboard_ui_enabled == 2)
308                 {
309                         if (hudShiftState & S_SHIFT)
310                                 goto uparrow_action;
311                         else
312                                 goto downarrow_action;
313                 }
314
315                 if (hudShiftState & S_SHIFT)
316                 {
317                         --scoreboard_selected_panel;
318                         if (scoreboard_selected_panel == SB_PANEL_RANKINGS && !rankings_cnt)
319                                 --scoreboard_selected_panel;
320                         if (scoreboard_selected_panel < SB_PANEL_FIRST)
321                                 scoreboard_selected_panel = SB_PANEL_MAX;
322                 }
323                 else
324                 {
325                         ++scoreboard_selected_panel;
326                         if (scoreboard_selected_panel == SB_PANEL_RANKINGS && !rankings_cnt)
327                                 ++scoreboard_selected_panel;
328                         if (scoreboard_selected_panel > SB_PANEL_MAX)
329                                 scoreboard_selected_panel = SB_PANEL_FIRST;
330                 }
331
332                 scoreboard_selected_panel_time = time;
333         }
334         else if(nPrimary == K_DOWNARROW)
335         {
336                 if (!key_pressed)
337                         return true;
338                 LABEL(downarrow_action);
339                 if (scoreboard_selected_panel == SB_PANEL_SCOREBOARD)
340                 {
341                         if (scoreboard_ui_enabled == 2)
342                         {
343                                 entity curr_team = NULL;
344                                 bool scoreboard_selected_team_found = false;
345                                 if (!scoreboard_selected_team)
346                                         scoreboard_selected_team_found = true;
347
348                                 for(entity tm = teams.sort_next; tm; tm = tm.sort_next)
349                                 {
350                                         if(tm.team == NUM_SPECTATOR)
351                                                 continue;
352                                         curr_team = tm;
353                                         if (scoreboard_selected_team_found)
354                                                 goto ok_team;
355                                         if (scoreboard_selected_team == tm)
356                                                 scoreboard_selected_team_found = true;
357                                 }
358                                 LABEL(ok_team);
359                                 if (curr_team == scoreboard_selected_team) // loop reached the last team
360                                         curr_team = NULL;
361                                 scoreboard_selected_team = curr_team;
362                         }
363                         else
364                         {
365                                 entity pl, tm;
366                                 entity curr_pl = NULL;
367                                 bool scoreboard_selected_player_found = false;
368                                 if (!scoreboard_selected_player)
369                                         scoreboard_selected_player_found = true;
370
371                                 for(tm = teams.sort_next; tm; tm = tm.sort_next)
372                                 {
373                                         if(tm.team != NUM_SPECTATOR)
374                                         for(pl = players.sort_next; pl; pl = pl.sort_next)
375                                         {
376                                                 if(pl.team != tm.team)
377                                                         continue;
378                                                 curr_pl = pl;
379                                                 if (scoreboard_selected_player_found)
380                                                         goto ok_done;
381                                                 if (scoreboard_selected_player == pl)
382                                                         scoreboard_selected_player_found = true;
383                                         }
384                                 }
385                                 LABEL(ok_done);
386                                 if (curr_pl == scoreboard_selected_player) // loop reached the last player
387                                         curr_pl = NULL;
388                                 scoreboard_selected_player = curr_pl;
389                         }
390                 }
391         }
392         else if(nPrimary == K_UPARROW)
393         {
394                 if (!key_pressed)
395                         return true;
396                 LABEL(uparrow_action);
397                 if (scoreboard_selected_panel == SB_PANEL_SCOREBOARD)
398                 {
399                         if (scoreboard_ui_enabled == 2)
400                         {
401                                 entity prev_team = NULL;
402                                 for(entity tm = teams.sort_next; tm; tm = tm.sort_next)
403                                 {
404                                         if(tm.team == NUM_SPECTATOR)
405                                                 continue;
406                                         if (tm == scoreboard_selected_team)
407                                                 goto ok_team2;
408                                         prev_team = tm;
409                                 }
410                                 LABEL(ok_team2);
411                                 scoreboard_selected_team = prev_team;
412                         }
413                         else
414                         {
415                                 entity prev_pl = NULL;
416                                 entity pl, tm;
417                                 for(tm = teams.sort_next; tm; tm = tm.sort_next)
418                                 {
419                                         if(tm.team != NUM_SPECTATOR)
420                                         for(pl = players.sort_next; pl; pl = pl.sort_next)
421                                         {
422                                                 if(pl.team != tm.team)
423                                                         continue;
424                                                 if (pl == scoreboard_selected_player)
425                                                         goto ok_done2;
426                                                 prev_pl = pl;
427                                         }
428                                 }
429                                 LABEL(ok_done2);
430                                 scoreboard_selected_player = prev_pl;
431                         }
432                 }
433         }
434         else if(nPrimary == K_RIGHTARROW)
435         {
436                 if (!key_pressed)
437                         return true;
438                 if (scoreboard_selected_panel == SB_PANEL_RANKINGS)
439                         rankings_start_column = min(rankings_start_column + 1, (ceil(RANKINGS_RECEIVED_CNT / rankings_rows) - rankings_columns));
440         }
441         else if(nPrimary == K_LEFTARROW)
442         {
443                 if (!key_pressed)
444                         return true;
445                 if (scoreboard_selected_panel == SB_PANEL_RANKINGS)
446                         rankings_start_column = max(rankings_start_column - 1, 0);
447         }
448         else if(nPrimary == K_ENTER || nPrimary == K_SPACE || nPrimary == K_KP_ENTER)
449         {
450                 if (!key_pressed)
451                         return true;
452                 if (scoreboard_selected_panel == SB_PANEL_SCOREBOARD)
453                 {
454                         if (scoreboard_ui_enabled == 2)
455                         {
456                                 string team_name;
457                                 if (!scoreboard_selected_team || (hudShiftState & S_SHIFT))
458                                         team_name = "auto";
459                                 else
460                                         team_name = Static_Team_ColorName(scoreboard_selected_team.team);
461                                 localcmd(sprintf("cmd selectteam %s; cmd join\n", team_name));
462                                 HUD_Scoreboard_UI_Disable();
463                         }
464                         else if (scoreboard_selected_player)
465                                 localcmd(sprintf("spectate %d\n", scoreboard_selected_player.sv_entnum + 1));
466                 }
467         }
468         else if(nPrimary == 'c' && (hudShiftState & S_CTRL))
469         {
470                 if (!key_pressed)
471                         return true;
472                 if (scoreboard_ui_enabled == 1 && scoreboard_selected_panel == SB_PANEL_SCOREBOARD)
473                 {
474                         switch (scoreboard_selected_columns_layout)
475                         {
476                                 case 0:
477                                         if (autocvar_scoreboard_columns != "" && autocvar_scoreboard_columns != "all" && autocvar_scoreboard_columns != "default")
478                                         {
479                                                 localcmd(sprintf("scoreboard_columns_set\n")); // sets the layout saved in scoreboard_columns
480                                                 scoreboard_selected_columns_layout = 1;
481                                                 break;
482                                         }
483                                         // fallthrough
484                                 case 1:
485                                         localcmd(sprintf("scoreboard_columns_set default\n"));
486                                         scoreboard_selected_columns_layout = 2;
487                                         break;
488                                 case 2:
489                                         localcmd(sprintf("scoreboard_columns_set all\n"));
490                                         scoreboard_selected_columns_layout = 0;
491                                         break;
492                         }
493                 }
494         }
495         else if(nPrimary == 'r' && (hudShiftState & S_CTRL))
496         {
497                 if (!key_pressed)
498                         return true;
499                 if (scoreboard_selected_panel == SB_PANEL_SCOREBOARD)
500                         localcmd("toggle hud_panel_scoreboard_scores_per_round\n");
501         }
502         else if(nPrimary == 't' && (hudShiftState & S_CTRL))
503         {
504                 if (!key_pressed)
505                         return true;
506                 if (scoreboard_selected_panel == SB_PANEL_SCOREBOARD)
507                 {
508                         if (scoreboard_selected_player)
509                         {
510                                 localcmd(sprintf("commandmode tell \"%s^7\"\n", entcs_GetName(scoreboard_selected_player.sv_entnum)));
511                                 HUD_Scoreboard_UI_Disable();
512                         }
513                 }
514         }
515         else if(nPrimary == 'k' && (hudShiftState & S_CTRL))
516         {
517                 if (!key_pressed)
518                         return true;
519                 if (scoreboard_selected_panel == SB_PANEL_SCOREBOARD)
520                 {
521                         if (scoreboard_selected_player)
522                                 localcmd(sprintf("vcall kick \"%s^7\"\n", entcs_GetName(scoreboard_selected_player.sv_entnum)));
523                 }
524         }
525         else if(hit_con_bind || nPrimary == K_PAUSE)
526                 return false;
527
528         return true;
529 }
530
531 void PrintScoresLabels() { Label_getInfo(string_null, 1); }
532 string TranslateScoresLabel(string label) { return Label_getInfo(label, 0); }
533
534 void Scoreboard_InitScores()
535 {
536         int i, f;
537
538         ps_primary = ps_secondary = NULL;
539         ts_primary = ts_secondary = -1;
540         FOREACH(Scores, true, {
541                 if(scores_flags(it) & SFL_NOT_SORTABLE)
542                         continue;
543                 f = (scores_flags(it) & SFL_SORT_PRIO_MASK);
544                 if(f == SFL_SORT_PRIO_PRIMARY)
545                         ps_primary = it;
546                 if(f == SFL_SORT_PRIO_SECONDARY)
547                         ps_secondary = it;
548         });
549         if(ps_secondary == NULL)
550                 ps_secondary = ps_primary;
551
552         for(i = 0; i < MAX_TEAMSCORE; ++i)
553         {
554                 f = (teamscores_flags(i) & SFL_SORT_PRIO_MASK);
555                 if(f == SFL_SORT_PRIO_PRIMARY)
556                         ts_primary = i;
557                 if(f == SFL_SORT_PRIO_SECONDARY)
558                         ts_secondary = i;
559         }
560         if(ts_secondary == -1)
561                 ts_secondary = ts_primary;
562
563         Cmd_Scoreboard_SetFields(0);
564 }
565
566 //float lastpnum;
567 void Scoreboard_UpdatePlayerTeams()
568 {
569         static float update_time;
570         if (time <= update_time)
571                 return;
572         update_time = time;
573
574         entity pl, tmp;
575         numplayers = 0;
576         //int num = 0;
577         for(pl = players.sort_next; pl; pl = pl.sort_next)
578         {
579                 numplayers += pl.team != NUM_SPECTATOR;
580                 //num += 1;
581                 int Team = entcs_GetScoreTeam(pl.sv_entnum);
582                 if(SetTeam(pl, Team))
583                 {
584                         tmp = pl.sort_prev;
585                         Scoreboard_UpdatePlayerPos(pl);
586                         if(tmp)
587                                 pl = tmp;
588                         else
589                                 pl = players.sort_next;
590                 }
591         }
592         /*
593         if(num != lastpnum)
594                 print(strcat("PNUM: ", ftos(num), "\n"));
595         lastpnum = num;
596         */
597 }
598
599 int Scoreboard_CompareScore(int vl, int vr, int f)
600 {
601         TC(int, vl); TC(int, vr); TC(int, f);
602         if(f & SFL_ZERO_IS_WORST)
603         {
604                 if(vl == 0 && vr != 0)
605                         return 1;
606                 if(vl != 0 && vr == 0)
607                         return 0;
608         }
609         if(vl > vr)
610                 return IS_INCREASING(f);
611         if(vl < vr)
612                 return IS_DECREASING(f);
613         return -1;
614 }
615
616 float Scoreboard_ComparePlayerScores(entity left, entity right)
617 {
618         int vl = (left.gotscores) ? entcs_GetTeam(left.sv_entnum) : NUM_SPECTATOR;
619         int vr = (right.gotscores) ? entcs_GetTeam(right.sv_entnum) : NUM_SPECTATOR;
620
621         if(vl > vr)
622                 return true;
623         if(vl < vr)
624                 return false;
625
626         if(vl == NUM_SPECTATOR)
627         {
628                 // FIRST the one with scores (spectators), THEN the ones without (downloaders)
629                 // no other sorting
630                 if(!left.gotscores && right.gotscores)
631                         return true;
632                 return false;
633         }
634
635         int res = Scoreboard_CompareScore(left.scores(ps_primary), right.scores(ps_primary), scores_flags(ps_primary));
636         if (res >= 0) return res;
637
638         if (ps_secondary && ps_secondary != ps_primary)
639         {
640                 res = Scoreboard_CompareScore(left.scores(ps_secondary), right.scores(ps_secondary), scores_flags(ps_secondary));
641                 if (res >= 0) return res;
642         }
643
644         FOREACH(Scores, (it != ps_primary && it != ps_secondary), {
645                 res = Scoreboard_CompareScore(left.scores(it), right.scores(it), scores_flags(it));
646                 if (res >= 0) return res;
647         });
648
649         if (left.sv_entnum < right.sv_entnum)
650                 return true;
651
652         return false;
653 }
654
655 void Scoreboard_UpdatePlayerPos(entity player)
656 {
657         entity ent;
658         for(ent = player.sort_next; ent && Scoreboard_ComparePlayerScores(player, ent); ent = player.sort_next)
659         {
660                 SORT_SWAP(player, ent);
661         }
662         for(ent = player.sort_prev; ent != players && Scoreboard_ComparePlayerScores(ent, player); ent = player.sort_prev)
663         {
664                 SORT_SWAP(ent, player);
665         }
666 }
667
668 float Scoreboard_CompareTeamScores(entity left, entity right)
669 {
670         if(left.team == NUM_SPECTATOR)
671                 return 1;
672         if(right.team == NUM_SPECTATOR)
673                 return 0;
674
675         int fld_idx = -1;
676         int r;
677         for(int i = -2; i < MAX_TEAMSCORE; ++i)
678         {
679                 if (i < 0)
680                 {
681                         if (fld_idx == -1) fld_idx = ts_primary;
682                         else if (ts_secondary == ts_primary) continue;
683                         else fld_idx = ts_secondary;
684                 }
685                 else
686                 {
687                         fld_idx = i;
688                         if (fld_idx == ts_primary || fld_idx == ts_secondary) continue;
689                 }
690
691                 r = Scoreboard_CompareScore(left.teamscores(fld_idx), right.teamscores(fld_idx), teamscores_flags(fld_idx));
692                 if (r >= 0) return r;
693         }
694
695         if (left.team < right.team)
696                 return true;
697
698         return false;
699 }
700
701 void Scoreboard_UpdateTeamPos(entity Team)
702 {
703         entity ent;
704         for(ent = Team.sort_next; ent && Scoreboard_CompareTeamScores(Team, ent); ent = Team.sort_next)
705         {
706                 SORT_SWAP(Team, ent);
707         }
708         for(ent = Team.sort_prev; ent != teams && Scoreboard_CompareTeamScores(ent, Team); ent = Team.sort_prev)
709         {
710                 SORT_SWAP(ent, Team);
711         }
712 }
713
714 void Cmd_Scoreboard_Help()
715 {
716         LOG_HELP(_("You can modify the scoreboard using the ^2scoreboard_columns_set command."));
717         LOG_HELP(_("Usage:"));
718         LOG_HELP("^2scoreboard_columns_set ^3default");
719         LOG_HELP(_("^2scoreboard_columns_set ^3field1 field2 ..."));
720         LOG_HELP(_("^2scoreboard_columns_set ^7without arguments reads the arguments from the cvar scoreboard_columns"));
721         LOG_HELP(_("  ^5Note: ^7scoreboard_columns_set without arguments is executed on every map start"));
722         LOG_HELP(_("^2scoreboard_columns_set ^3expand_default ^7loads default layout and expands it into the cvar scoreboard_columns so you can edit it"));
723         LOG_HELP(_("You can use a ^3|^7 to start the right-aligned fields."));
724         LOG_HELP(_("The following field names are recognized (case insensitive):"));
725         LOG_HELP("");
726
727         PrintScoresLabels();
728         LOG_HELP("");
729
730         LOG_HELP(_("Before a field you can put a + or - sign, then a comma separated list\n"
731                 "of game types, then a slash, to make the field show up only in these\n"
732                 "or in all but these game types. You can also specify 'all' as a\n"
733                 "field to show all fields available for the current game mode."));
734         LOG_HELP("");
735
736         LOG_HELP(_("The special game type names 'teams' and 'noteams' can be used to\n"
737                 "include/exclude ALL teams/noteams game modes."));
738         LOG_HELP("");
739
740         LOG_HELP(_("Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4"));
741         LOG_HELP(_("will display name, ping and pl aligned to the left, and the fields\n"
742                 "right of the vertical bar aligned to the right."));
743         LOG_HELP(_("'field3' will only be shown in CTF, and 'field4' will be shown in all\n"
744                         "other gamemodes except DM."));
745 }
746
747 // NOTE: adding a gametype with ? to not warn for an optional field
748 // make sure it's excluded in a previous exclusive rule, if any
749 // otherwise the previous exclusive rule warns anyway
750 // e.g. -teams,rc,cts,lms/kills ?+rc/kills
751 #define SCOREBOARD_DEFAULT_COLUMNS \
752 "ping pl fps cn name |" \
753 " -teams,rc,cts,inv,lms/kills +ft,tdm,tmayhem/kills ?+rc,inv/kills" \
754 " -teams,lms/deaths +ft,tdm,tmayhem/deaths" \
755 " +tdm/sum" \
756 " -teams,lms,rc,cts,inv,ka/suicides +ft,tdm,tmayhem/suicides ?+rc,inv/suicides" \
757 " -cts,dm,tdm,ka,ft,mayhem,tmayhem/frags" /* tdm already has this in "score" */ \
758 " +tdm,ft,dom,ons,as,tmayhem/teamkills"\
759 " -rc,cts,nb/dmg -rc,cts,nb/dmgtaken" \
760 " +ctf/pickups +ctf/fckills +ctf/returns +ctf/caps +ons/takes +ons/caps" \
761 " +lms/lives +lms/rank" \
762 " +kh/kckills +kh/losses +kh/caps" \
763 " ?+rc/laps ?+rc/time +rc,cts/fastest" \
764 " +as/objectives +nb/faults +nb/goals" \
765 " +ka,tka/pickups +ka,tka/bckills +ka,tka/bctime +ft/revivals" \
766 " +dom/ticks +dom/takes" \
767 " -lms,rc,cts,inv,nb/score"
768
769 void Cmd_Scoreboard_SetFields(int argc)
770 {
771         TC(int, argc);
772         int i, slash;
773         string str, pattern;
774         bool have_name = false, have_primary = false, have_secondary = false, have_separator = false;
775         int missing;
776
777         if(!gametype)
778                 return; // do nothing, we don't know gametype and scores yet
779
780         // sbt_fields uses strunzone on the titles!
781         if(!sbt_field_title[0])
782                 for(i = 0; i < MAX_SBT_FIELDS; ++i)
783                         sbt_field_title[i] = strzone("(null)");
784
785         // TODO: re enable with gametype dependant cvars?
786         if(argc < 3) // no arguments provided
787                 argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " ");
788
789         if(argc < 3)
790                 argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " ");
791
792         if(argc == 3)
793         {
794                 if(argv(2) == "default" || argv(2) == "expand_default")
795                 {
796                         if(argv(2) == "expand_default")
797                                 cvar_set("scoreboard_columns", SCOREBOARD_DEFAULT_COLUMNS);
798                         argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " ");
799                 }
800                 else if(argv(2) == "all" || argv(2) == "ALL")
801                 {
802                         string s = "ping pl cn name |"; // scores without label (not really scores)
803                         if(argv(2) == "ALL")
804                         {
805                                 // scores without label
806                                 s = strcat(s, " ", "sum");
807                                 s = strcat(s, " ", "kdratio");
808                                 s = strcat(s, " ", "frags");
809                         }
810                         FOREACH(Scores, true, {
811                                 if(it != ps_primary)
812                                 if(it != ps_secondary)
813                                 if(scores_label(it) != "")
814                                         s = strcat(s, " ", scores_label(it));
815                         });
816                         if(ps_secondary != ps_primary)
817                                 s = strcat(s, " ", scores_label(ps_secondary));
818                         s = strcat(s, " ", scores_label(ps_primary));
819                         argc = tokenizebyseparator(strcat("0 1 ", s), " ");
820                 }
821         }
822
823
824         sbt_num_fields = 0;
825
826         hud_fontsize = HUD_GetFontsize("hud_fontsize");
827         
828         duel_score_fontsize = hud_fontsize * 3;
829         duel_name_fontsize = hud_fontsize * 1.5;
830         duel_score_size = vec2(duel_score_fontsize.x * 1.5, duel_score_fontsize.y * 1.25);
831         
832         team_score_fontsize = hud_fontsize * 2;
833         team_name_fontsize = hud_fontsize * 1.5;
834         team_score_size = vec2(team_score_fontsize.x * 1.5, team_score_fontsize.y * 1.25);
835
836         for(i = 1; i < argc - 1; ++i)
837         {
838                 str = argv(i+1);
839                 bool nocomplain = false;
840                 if(substring(str, 0, 1) == "?")
841                 {
842                         nocomplain = true;
843                         str = substring(str, 1, strlen(str) - 1);
844                 }
845
846                 slash = strstrofs(str, "/", 0);
847                 if(slash >= 0)
848                 {
849                         pattern = substring(str, 0, slash);
850                         str = substring(str, slash + 1, strlen(str) - (slash + 1));
851
852                         if (!isGametypeInFilter(gametype, teamplay, false, pattern))
853                                 continue;
854                 }
855
856                 str = strtolower(str);
857                 strcpy(sbt_field_title[sbt_num_fields], TranslateScoresLabel(str));
858                 sbt_field_size[sbt_num_fields] = stringwidth(sbt_field_title[sbt_num_fields], false, hud_fontsize);
859
860                 PlayerScoreField j;
861                 switch(str)
862                 {
863                         // fields without a label (not networked via the score system)
864                         case "ping": sbt_field[sbt_num_fields] = SP_PING; break;
865                         case "pl": sbt_field[sbt_num_fields] = SP_PL; break;
866                         case "cn": sbt_field[sbt_num_fields] = SP_COUNTRY; break; //LegendGuard adds cn label for Country column 05-04-2021
867                         case "name": case "nick": sbt_field[sbt_num_fields] = SP_NAME; have_name = true; break;
868                         case "|": sbt_field[sbt_num_fields] = SP_SEPARATOR; have_separator = true; break;
869                         case "kd": case "kdr": case "kdratio": sbt_field[sbt_num_fields] = SP_KDRATIO; break;
870                         case "sum": case "diff": case "k-d": sbt_field[sbt_num_fields] = SP_SUM; break;
871                         case "cn": sbt_field[sbt_num_fields] = SP_COUNTRY; break; //LegendGuard adds cn label for Country column 05-04-2021
872                         case "frags": sbt_field[sbt_num_fields] = SP_FRAGS; break;
873                         default: // fields with a label
874                         {
875                                 // map alternative labels
876                                 if (str == "damage") str = "dmg";
877                                 if (str == "damagetaken") str = "dmgtaken";
878
879                                 FOREACH(Scores, true, {
880                                         if (str == strtolower(scores_label(it))) {
881                                                 j = it;
882                                                 goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
883                                         }
884                                 });
885
886                                 // NOTE: can't check STAT(SHOWFPS) here, if checked too early it returns false anyway
887                                 if(!nocomplain && str != "fps") // server can disable the fps field
888                                         LOG_INFOF("^1Error:^7 Unknown score field: '%s'", str);
889
890                                 strfree(sbt_field_title[sbt_num_fields]);
891                                 sbt_field_size[sbt_num_fields] = 0;
892                                 continue;
893
894                                 LABEL(found)
895                                 sbt_field[sbt_num_fields] = j;
896                                 if(j == ps_primary)
897                                         have_primary = true;
898                                 if(j == ps_secondary)
899                                         have_secondary = true;
900
901                         }
902                 }
903                 ++sbt_num_fields;
904                 if(sbt_num_fields >= MAX_SBT_FIELDS)
905                         break;
906         }
907
908         if(scores_flags(ps_primary) & SFL_ALLOW_HIDE)
909                 have_primary = true;
910         if(scores_flags(ps_secondary) & SFL_ALLOW_HIDE)
911                 have_secondary = true;
912         if(ps_primary == ps_secondary)
913                 have_secondary = true;
914         missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
915
916         if(sbt_num_fields + missing < MAX_SBT_FIELDS)
917         {
918                 if(!have_name)
919                 {
920                         strfree(sbt_field_title[sbt_num_fields]);
921                         for(i = sbt_num_fields; i > 0; --i)
922                         {
923                                 sbt_field_title[i] = sbt_field_title[i-1];
924                                 sbt_field_size[i] = sbt_field_size[i-1];
925                                 sbt_field[i] = sbt_field[i-1];
926                         }
927                         sbt_field_title[0] = strzone(TranslateScoresLabel("name"));
928                         sbt_field[0] = SP_NAME;
929                         ++sbt_num_fields;
930                         LOG_INFO("fixed missing field 'name'");
931
932                         if(!have_separator)
933                         {
934                                 strfree(sbt_field_title[sbt_num_fields]);
935                                 for(i = sbt_num_fields; i > 1; --i)
936                                 {
937                                         sbt_field_title[i] = sbt_field_title[i-1];
938                                         sbt_field_size[i] = sbt_field_size[i-1];
939                                         sbt_field[i] = sbt_field[i-1];
940                                 }
941                                 sbt_field_title[1] = strzone("|");
942                                 sbt_field[1] = SP_SEPARATOR;
943                                 sbt_field_size[1] = stringwidth("|", false, hud_fontsize);
944                                 ++sbt_num_fields;
945                                 LOG_INFO("fixed missing field '|'");
946                         }
947                 }
948                 else if(!have_separator)
949                 {
950                         strcpy(sbt_field_title[sbt_num_fields], "|");
951                         sbt_field_size[sbt_num_fields] = stringwidth("|", false, hud_fontsize);
952                         sbt_field[sbt_num_fields] = SP_SEPARATOR;
953                         ++sbt_num_fields;
954                         LOG_INFO("fixed missing field '|'");
955                 }
956                 if(!have_secondary)
957                 {
958                         strcpy(sbt_field_title[sbt_num_fields], TranslateScoresLabel(scores_label(ps_secondary)));
959                         sbt_field_size[sbt_num_fields] = stringwidth(sbt_field_title[sbt_num_fields], false, hud_fontsize);
960                         sbt_field[sbt_num_fields] = ps_secondary;
961                         ++sbt_num_fields;
962                         LOG_INFOF("fixed missing field '%s'", scores_label(ps_secondary));
963                 }
964                 if(!have_primary)
965                 {
966                         strcpy(sbt_field_title[sbt_num_fields], TranslateScoresLabel(scores_label(ps_primary)));
967                         sbt_field_size[sbt_num_fields] = stringwidth(sbt_field_title[sbt_num_fields], false, hud_fontsize);
968                         sbt_field[sbt_num_fields] = ps_primary;
969                         ++sbt_num_fields;
970                         LOG_INFOF("fixed missing field '%s'", scores_label(ps_primary));
971                 }
972         }
973
974         sbt_field[sbt_num_fields] = SP_END;
975 }
976
977 string Scoreboard_AddPlayerId(string pl_name, entity pl)
978 {
979         string pref = autocvar_hud_panel_scoreboard_playerid_prefix;
980         string suf = autocvar_hud_panel_scoreboard_playerid_suffix;
981         return strcat(pref, itos(pl.sv_entnum + 1), suf, pl_name);
982 }
983
984 // MOVEUP::
985 vector sbt_field_rgb;
986 string sbt_field_icon0;
987 string sbt_field_icon1;
988 string sbt_field_icon2;
989 string sbt_field_icon3; //LegendGuard adds for Country player flags 05-04-2021
990 vector sbt_field_icon0_rgb;
991 vector sbt_field_icon1_rgb;
992 vector sbt_field_icon2_rgb;
993 string Scoreboard_GetName(entity pl)
994 {
995         if(ready_waiting && pl.ready)
996         {
997                 sbt_field_icon0 = "gfx/scoreboard/player_ready";
998         }
999         else if(!teamplay)
1000         {
1001                 int f = entcs_GetClientColors(pl.sv_entnum);
1002                 {
1003                         sbt_field_icon0 = "gfx/scoreboard/playercolor_base";
1004                         sbt_field_icon1 = "gfx/scoreboard/playercolor_shirt";
1005                         sbt_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
1006                         sbt_field_icon2 = "gfx/scoreboard/playercolor_pants";
1007                         sbt_field_icon2_rgb = colormapPaletteColor(f % 16, 1);
1008                 }
1009         }
1010         if(entcs_GetRank(pl.sv_entnum) != "")
1011                 return strcat(entcs_GetRank(pl.sv_entnum), "^7 ", entcs_GetName(pl.sv_entnum));
1012         else
1013                 return entcs_GetName(pl.sv_entnum);
1014 }
1015
1016 //LegendGuard adds GetCountrycode function 05-04-2021
1017 string Scoreboard_GetCountrycode(entity pl)
1018 {
1019         int ccode = entcs_GetCountryCode(pl.sv_entnum);
1020         if(ccode)
1021                 sbt_field_icon3 = strcat("gfx/flags/", ftos(ccode));
1022         else
1023                 sbt_field_icon3 = strcat("gfx/flags/", ftos(0)); //if user hasn't assigned country flag
1024         
1025         return "";
1026 }
1027
1028 int autocvar_hud_panel_scoreboard_ping_best = 0;
1029 int autocvar_hud_panel_scoreboard_ping_medium = 70;
1030 int autocvar_hud_panel_scoreboard_ping_high = 100;
1031 int autocvar_hud_panel_scoreboard_ping_worst = 150;
1032 vector autocvar_hud_panel_scoreboard_ping_best_color = '0 1 0';
1033 vector autocvar_hud_panel_scoreboard_ping_medium_color = '1 1 0';
1034 vector autocvar_hud_panel_scoreboard_ping_high_color = '1 0.5 0';
1035 vector autocvar_hud_panel_scoreboard_ping_worst_color = '1 0 0';
1036 #define PING_BEST autocvar_hud_panel_scoreboard_ping_best
1037 #define PING_MED autocvar_hud_panel_scoreboard_ping_medium
1038 #define PING_HIGH autocvar_hud_panel_scoreboard_ping_high
1039 #define PING_WORST autocvar_hud_panel_scoreboard_ping_worst
1040 #define COLOR_BEST autocvar_hud_panel_scoreboard_ping_best_color
1041 #define COLOR_MED autocvar_hud_panel_scoreboard_ping_medium_color
1042 #define COLOR_HIGH autocvar_hud_panel_scoreboard_ping_high_color
1043 #define COLOR_WORST autocvar_hud_panel_scoreboard_ping_worst_color
1044
1045 vector getPingColor(float f)
1046 {
1047         if(f < PING_BEST)
1048                 return COLOR_BEST;
1049         else if(f < PING_MED)
1050                 return COLOR_BEST + (COLOR_MED - COLOR_BEST) * ((f - PING_BEST) / (PING_MED - PING_BEST));
1051         else if(f < PING_HIGH)
1052                 return COLOR_MED + (COLOR_HIGH - COLOR_MED) * ((f - PING_MED) / (PING_HIGH - PING_MED));
1053         else if(f < PING_WORST)
1054                 return COLOR_HIGH + (COLOR_WORST - COLOR_HIGH) * ((f - PING_HIGH) / (PING_WORST - PING_HIGH));
1055         else
1056                 return COLOR_WORST;
1057 }
1058
1059 string Scoreboard_GetField(entity pl, PlayerScoreField field, bool per_round)
1060 {
1061         float tmp, num, denom;
1062         int f;
1063         string str;
1064         sbt_field_rgb = '1 1 1';
1065         sbt_field_icon0 = "";
1066         sbt_field_icon1 = "";
1067         sbt_field_icon2 = "";
1068         sbt_field_icon3 = ""; //LegendGuard adds for Country column 05-04-2021
1069         sbt_field_icon0_rgb = '1 1 1';
1070         sbt_field_icon1_rgb = '1 1 1';
1071         sbt_field_icon2_rgb = '1 1 1';
1072         int rounds_played = 0;
1073         if (per_round)
1074                 rounds_played = pl.(scores(SP_ROUNDS_PL));
1075         switch(field)
1076         {
1077                 case SP_PING:
1078                         if (!pl.gotscores)
1079                                 return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6 (Black Right-Pointing Triangle)
1080                         //str = getplayerkeyvalue(pl.sv_entnum, "ping");
1081                         f = pl.ping;
1082                         if(f == 0)
1083                                 return _("N/A");
1084                         sbt_field_rgb = getPingColor(f);
1085                         return ftos(f);
1086
1087                 case SP_PL:
1088                         if (!pl.gotscores)
1089                                 return _("N/A");
1090                         f = pl.ping_packetloss;
1091                         tmp = pl.ping_movementloss;
1092                         if(f == 0 && tmp == 0)
1093                                 return "";
1094                         str = ftos(ceil(f * 100));
1095                         if(tmp != 0)
1096                                 str = strcat(str, "~", ftos(ceil(tmp * 100)));
1097                         tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
1098                         sbt_field_rgb = '1 0.5 0.5' - '0 0.5 0.5' * tmp;
1099                         return str;
1100                 
1101                 //LegendGuard adds Country REGISTER in the switch 05-04-2021
1102                 case SP_COUNTRY:
1103                         str = Scoreboard_GetCountrycode(pl);
1104                         return str;
1105
1106                 //LegendGuard adds Country REGISTER in the switch 05-04-2021
1107                 case SP_COUNTRY:
1108                         str = Scoreboard_GetCountrycode(pl);
1109                         return str;
1110
1111                 case SP_NAME:
1112                         str = Scoreboard_GetName(pl);
1113                         if (autocvar_hud_panel_scoreboard_playerid)
1114                                 str = Scoreboard_AddPlayerId(str, pl);
1115                         return str;
1116
1117                 case SP_FRAGS:
1118                         f = pl.(scores(SP_KILLS));
1119                         f -= pl.(scores(SP_SUICIDES));
1120                         if (rounds_played)
1121                                 return sprintf("%.1f", f / rounds_played);
1122                         return ftos(f);
1123
1124                 case SP_KDRATIO:
1125                         num = pl.(scores(SP_KILLS));
1126                         denom = pl.(scores(SP_DEATHS));
1127
1128                         if(denom == 0) {
1129                                 sbt_field_rgb = '0 1 0';
1130                                 if (rounds_played)
1131                                         str = sprintf("%.1f", num / rounds_played);
1132                                 else
1133                                         str = sprintf("%d", num);
1134                         } else if(num <= 0) {
1135                                 sbt_field_rgb = '1 0 0';
1136                                 if (rounds_played)
1137                                         str = sprintf("%.2f", num / (denom * rounds_played));
1138                                 else
1139                                         str = sprintf("%.1f", num / denom);
1140                         } else
1141                         {
1142                                 if (rounds_played)
1143                                         str = sprintf("%.2f", num / (denom * rounds_played));
1144                                 else
1145                                         str = sprintf("%.1f", num / denom);
1146                         }
1147                         return str;
1148
1149                 case SP_SUM:
1150                         f = pl.(scores(SP_KILLS));
1151                         f -= pl.(scores(SP_DEATHS));
1152
1153                         if(f > 0) {
1154                                 sbt_field_rgb = '0 1 0';
1155                         } else if(f == 0) {
1156                                 sbt_field_rgb = '1 1 1';
1157                         } else {
1158                                 sbt_field_rgb = '1 0 0';
1159                         }
1160                         if (rounds_played)
1161                                 return sprintf("%.1f", f / rounds_played);
1162                         return ftos(f);
1163
1164                 case SP_ELO:
1165                 {
1166                         float elo = pl.(scores(SP_ELO));
1167                         switch (elo) {
1168                                 case -1: return "...";
1169                                 case -2: return _("N/A");
1170                                 default: return ftos(elo);
1171                         }
1172                 }
1173
1174                 case SP_FPS:
1175                 {
1176                         float fps = pl.(scores(SP_FPS));
1177                         if(fps == 0)
1178                         {
1179                                 sbt_field_rgb = '1 1 1';
1180                                 return ((pl.ping == 0) ? _("N/A") : "..."); // if 0 ping, either connecting or bot (either case can't show proper score)
1181                         }
1182
1183                         //sbt_field_rgb = HUD_Get_Num_Color(fps, 200, true);
1184                         sbt_field_rgb = '1 0 0' + '0 1 1' * (bound(0, fps, 60) / 60);
1185                         return ftos(fps);
1186                 }
1187
1188                 case SP_ROUNDS_PL:
1189                         return ftos(pl.(scores(field)));
1190
1191                 case SP_DMG: case SP_DMGTAKEN:
1192                         if (rounds_played)
1193                                 return sprintf("%.2f k", pl.(scores(field)) / (1000 * rounds_played));
1194                         return sprintf("%.1f k", pl.(scores(field)) / 1000);
1195
1196                 default: case SP_SCORE:
1197                         tmp = pl.(scores(field));
1198                         f = scores_flags(field);
1199                         if(field == ps_primary)
1200                                 sbt_field_rgb = '1 1 0';
1201                         else if(field == ps_secondary)
1202                                 sbt_field_rgb = '0 1 1';
1203                         else
1204                                 sbt_field_rgb = '1 1 1';
1205                         return ScoreString(f, tmp, rounds_played);
1206         }
1207         //return "error";
1208 }
1209
1210 float sbt_fixcolumnwidth_len;
1211 float sbt_fixcolumnwidth_iconlen;
1212 float sbt_fixcolumnwidth_marginlen;
1213
1214 string Scoreboard_FixColumnWidth(int i, string str)
1215 {
1216         TC(int, i);
1217         float f;
1218         vector sz;
1219
1220         sbt_fixcolumnwidth_iconlen = 0;
1221
1222         if(sbt_field_icon0 != "")
1223         {
1224                 sz = draw_getimagesize(sbt_field_icon0);
1225                 f = sz.x / sz.y;
1226                 if(sbt_fixcolumnwidth_iconlen < f)
1227                         sbt_fixcolumnwidth_iconlen = f;
1228         }
1229
1230         if(sbt_field_icon1 != "")
1231         {
1232                 sz = draw_getimagesize(sbt_field_icon1);
1233                 f = sz.x / sz.y;
1234                 if(sbt_fixcolumnwidth_iconlen < f)
1235                         sbt_fixcolumnwidth_iconlen = f;
1236         }
1237
1238         if(sbt_field_icon2 != "")
1239         {
1240                 sz = draw_getimagesize(sbt_field_icon2);
1241                 f = sz.x / sz.y;
1242                 if(sbt_fixcolumnwidth_iconlen < f)
1243                         sbt_fixcolumnwidth_iconlen = f;
1244         }
1245
1246         //LegendGuard adds conditional for Country column 05-04-2021
1247         if(sbt_field_icon3 != "")
1248         {
1249                 sz = draw_getimagesize(sbt_field_icon3);
1250                 f = sz.x / sz.y;
1251                 if(sbt_fixcolumnwidth_iconlen < f)
1252                         sbt_fixcolumnwidth_iconlen = f;
1253         }
1254
1255         if(sbt_fixcolumnwidth_iconlen != 0)
1256         {
1257                 sbt_fixcolumnwidth_iconlen *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect
1258                 sbt_fixcolumnwidth_marginlen = stringwidth(" ", false, hud_fontsize);
1259         }
1260         else
1261                 sbt_fixcolumnwidth_marginlen = 0;
1262
1263         if(sbt_field[i] == SP_NAME) // name gets all remaining space
1264         {
1265                 int j;
1266                 float remaining_space = 0;
1267                 for(j = 0; j < sbt_num_fields; ++j)
1268                         if(j != i)
1269                                 if (sbt_field[i] != SP_SEPARATOR)
1270                                         remaining_space += sbt_field_size[j] + hud_fontsize.x;
1271                 sbt_field_size[i] = panel_size.x - remaining_space;
1272
1273                 if (sbt_fixcolumnwidth_iconlen != 0)
1274                         remaining_space += sbt_fixcolumnwidth_marginlen + sbt_fixcolumnwidth_iconlen * hud_fontsize.x;
1275                 float namesize = panel_size.x - remaining_space;
1276                 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
1277                 sbt_fixcolumnwidth_len = stringwidth(str, true, hud_fontsize);
1278
1279                 max_namesize = vid_conwidth - remaining_space;
1280         }
1281         else
1282                 sbt_fixcolumnwidth_len = stringwidth(str, false, hud_fontsize);
1283
1284         f = sbt_fixcolumnwidth_len + sbt_fixcolumnwidth_marginlen + sbt_fixcolumnwidth_iconlen * hud_fontsize.x;
1285         if(sbt_field_size[i] < f)
1286                 sbt_field_size[i] = f;
1287
1288         return str;
1289 }
1290
1291 void Scoreboard_initFieldSizes()
1292 {
1293         for(int i = 0; i < sbt_num_fields; ++i)
1294         {
1295                 sbt_field_size[i] = stringwidth(sbt_field_title[i], false, hud_fontsize);
1296                 Scoreboard_FixColumnWidth(i, "");
1297         }
1298 }
1299
1300 vector Scoreboard_DrawHeader(vector pos, vector rgb, bool other_players, int team)
1301 {
1302         int i;
1303         string title_str;
1304         vector title_rgb;
1305         vector column_dim = eY * panel_size.y;
1306         
1307         if(other_players)
1308                 column_dim.y -= 1.25 * hud_fontsize.y;
1309         vector text_offset = eY * (1.25 - 1) / 2 * hud_fontsize.y;
1310         pos.x += hud_fontsize.x * 0.5;
1311         for(i = 0; i < sbt_num_fields; ++i)
1312         {
1313                 if(sbt_field[i] == SP_SEPARATOR)
1314                         break;
1315                 
1316                 vector text_offset_center = '0 0 0';
1317                 
1318                 if(sbt_field[i] == SP_PING && teamplay) {
1319                         title_str = sprintf("(%d)", average_ping[Team_TeamToIndex(team) - 1]);
1320                         title_rgb = getPingColor(average_ping[Team_TeamToIndex(team) - 1]);
1321                         text_offset_center.x = sbt_field_size[i] - stringwidth(title_str, false, hud_fontsize);
1322                 } else {
1323                         title_str = sbt_field_title[i];
1324                         title_rgb = rgb * 1.5;
1325                 }
1326                 
1327                 column_dim.x = sbt_field_size[i] + hud_fontsize.x;
1328                 if (sbt_highlight)
1329                         if (i % 2)
1330                                 drawfill(pos - eX * hud_fontsize.x * 0.5, column_dim, '0 0 0', sbt_highlight_alpha, DRAWFLAG_NORMAL);
1331                 drawstring(pos + text_offset + text_offset_center, title_str, hud_fontsize, title_rgb, sbt_fg_alpha, DRAWFLAG_NORMAL);
1332                 pos.x += column_dim.x;
1333         }
1334         if(sbt_field[i] == SP_SEPARATOR)
1335         {
1336                 pos.x = panel_pos.x + panel_size.x - hud_fontsize.x * 0.5;
1337                 for(i = sbt_num_fields - 1; i > 0; --i)
1338                 {
1339                         if(sbt_field[i] == SP_SEPARATOR)
1340                                 break;
1341
1342                         pos.x -= sbt_field_size[i];
1343
1344                         if (sbt_highlight)
1345                                 if (!(i % 2))
1346                                 {
1347                                         column_dim.x = sbt_field_size[i] + hud_fontsize.x;
1348                                         drawfill(pos - eX * hud_fontsize.x * 0.5, column_dim, '0 0 0', sbt_highlight_alpha, DRAWFLAG_NORMAL);
1349                                 }
1350
1351                         text_offset.x = sbt_field_size[i] - stringwidth(sbt_field_title[i], false, hud_fontsize);
1352                         drawstring(pos + text_offset, sbt_field_title[i], hud_fontsize, rgb * 1.5, sbt_fg_alpha, DRAWFLAG_NORMAL);
1353                         pos.x -= hud_fontsize.x;
1354                 }
1355         }
1356
1357         pos.x = panel_pos.x;
1358         pos.y += 1.25 * hud_fontsize.y;
1359         return pos;
1360 }
1361
1362 void Scoreboard_DrawItem(vector item_pos, vector rgb, entity pl, bool is_self, int pl_number)
1363 {
1364         TC(bool, is_self); TC(int, pl_number);
1365         string str;
1366         bool is_spec = (entcs_GetSpecState(pl.sv_entnum) == ENTCS_SPEC_PURE);
1367
1368         vector h_pos = item_pos;
1369         vector h_size = vec2(panel_size.x, hud_fontsize.y * 1.25);
1370         // alternated rows highlighting
1371         if (scoreboard_selected_panel == SB_PANEL_SCOREBOARD && scoreboard_ui_enabled == 1)
1372         {
1373                 if (pl == scoreboard_selected_player)
1374                         drawfill(h_pos, h_size, rgb, 0.44 * panel_fg_alpha, DRAWFLAG_NORMAL);
1375         }
1376         else if(is_self)
1377                 drawfill(h_pos, h_size, rgb, sbt_highlight_alpha_self, DRAWFLAG_NORMAL);
1378         else if((sbt_highlight) && (!(pl_number % 2)))
1379                 drawfill(h_pos, h_size, rgb, sbt_highlight_alpha, DRAWFLAG_NORMAL);
1380
1381         float fg_alpha = (is_self ? sbt_fg_alpha_self : sbt_fg_alpha);
1382
1383         vector pos = item_pos;
1384         // put a "self indicator" beside the self row, unicode U+25C0 (black left-pointing triangle)
1385         if (is_self)
1386                 drawstring(pos + eX * (panel_size.x + 0.5 * hud_fontsize.x) + eY, "\xE2\x97\x80", hud_fontsize, rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
1387
1388         pos.x += hud_fontsize.x * 0.5;
1389         pos.y += (1.25 - 1) / 2 * hud_fontsize.y; // center text vertically
1390         vector tmp = '0 0 0';
1391         int i;
1392         PlayerScoreField field;
1393         for(i = 0; i < sbt_num_fields; ++i)
1394         {
1395                 field = sbt_field[i];
1396                 if(field == SP_SEPARATOR)
1397                         break;
1398
1399                 if(is_spec && field != SP_NAME && field != SP_PING) {
1400                         pos.x += sbt_field_size[i] + hud_fontsize.x;
1401                         continue;
1402                 }
1403                 str = Scoreboard_GetField(pl, field, autocvar_hud_panel_scoreboard_scores_per_round);
1404                 str = Scoreboard_FixColumnWidth(i, str);
1405
1406                 pos.x += sbt_field_size[i] + hud_fontsize.x;
1407
1408                 if(field == SP_NAME) {
1409                         tmp.x = sbt_field_size[i] - hud_fontsize.x * sbt_fixcolumnwidth_iconlen - sbt_fixcolumnwidth_marginlen + hud_fontsize.x;
1410                         drawcolorcodedstring(pos - tmp, str, hud_fontsize, fg_alpha, DRAWFLAG_NORMAL);
1411                 } else {
1412                         tmp.x = sbt_fixcolumnwidth_len + hud_fontsize.x;
1413                         drawstring(pos - tmp, str, hud_fontsize, sbt_field_rgb, fg_alpha, DRAWFLAG_NORMAL);
1414                 }
1415
1416                 tmp.x = sbt_field_size[i] + hud_fontsize.x;
1417                 if(sbt_field_icon0 != "")
1418                         drawpic(pos - tmp, sbt_field_icon0, vec2(hud_fontsize.x * sbt_fixcolumnwidth_iconlen, hud_fontsize.y), sbt_field_icon1_rgb, fg_alpha, DRAWFLAG_NORMAL);
1419                 if(sbt_field_icon1 != "")
1420                         drawpic(pos - tmp, sbt_field_icon1, vec2(hud_fontsize.x * sbt_fixcolumnwidth_iconlen, hud_fontsize.y), sbt_field_icon1_rgb, fg_alpha, DRAWFLAG_NORMAL);
1421                 if(sbt_field_icon2 != "")
1422                         drawpic(pos - tmp, sbt_field_icon2, vec2(hud_fontsize.x * sbt_fixcolumnwidth_iconlen, hud_fontsize.y), sbt_field_icon2_rgb, fg_alpha, DRAWFLAG_NORMAL);
1423                 if(sbt_field_icon3 != "") //LegendGuard adds conditional for Country column 05-04-2021
1424                         drawpic(pos - tmp, sbt_field_icon3, vec2(hud_fontsize.x * sbt_fixcolumnwidth_iconlen, hud_fontsize.y), sbt_field_icon1_rgb, fg_alpha, DRAWFLAG_NORMAL);
1425         }
1426
1427         if(sbt_field[i] == SP_SEPARATOR)
1428         {
1429                 pos.x = item_pos.x + panel_size.x - hud_fontsize.x * 0.5;
1430                 for(i = sbt_num_fields-1; i > 0; --i)
1431                 {
1432                         field = sbt_field[i];
1433                         if(field == SP_SEPARATOR)
1434                                 break;
1435
1436                         if(is_spec && field != SP_NAME && field != SP_PING) {
1437                                 pos.x -= sbt_field_size[i] + hud_fontsize.x;
1438                                 continue;
1439                         }
1440
1441                         str = Scoreboard_GetField(pl, field, autocvar_hud_panel_scoreboard_scores_per_round);
1442                         str = Scoreboard_FixColumnWidth(i, str);
1443
1444                         if(field == SP_NAME) {
1445                                 tmp.x = sbt_fixcolumnwidth_len; // left or right aligned? let's put it right...
1446                                 drawcolorcodedstring(pos - tmp, str, hud_fontsize, fg_alpha, DRAWFLAG_NORMAL);
1447                         } else {
1448                                 tmp.x = sbt_fixcolumnwidth_len;
1449                                 drawstring(pos - tmp, str, hud_fontsize, sbt_field_rgb, fg_alpha, DRAWFLAG_NORMAL);
1450                         }
1451
1452                         tmp.x = sbt_field_size[i];
1453                         if(sbt_field_icon0 != "")
1454                                 drawpic(pos - tmp, sbt_field_icon0, vec2(hud_fontsize.x * sbt_fixcolumnwidth_iconlen, hud_fontsize.y), sbt_field_icon1_rgb, fg_alpha, DRAWFLAG_NORMAL);
1455                         if(sbt_field_icon1 != "")
1456                                 drawpic(pos - tmp, sbt_field_icon1, vec2(hud_fontsize.x * sbt_fixcolumnwidth_iconlen, hud_fontsize.y), sbt_field_icon1_rgb, fg_alpha, DRAWFLAG_NORMAL);
1457                         if(sbt_field_icon2 != "")
1458                                 drawpic(pos - tmp, sbt_field_icon2, vec2(hud_fontsize.x * sbt_fixcolumnwidth_iconlen, hud_fontsize.y), sbt_field_icon2_rgb, fg_alpha, DRAWFLAG_NORMAL);
1459                         if(sbt_field_icon3 != "") //LegendGuard adds conditional for Country column 05-04-2021
1460                                 drawpic(pos - tmp, sbt_field_icon3, vec2(hud_fontsize.x * sbt_fixcolumnwidth_iconlen, hud_fontsize.y), sbt_field_icon1_rgb, fg_alpha, DRAWFLAG_NORMAL);
1461                         pos.x -= sbt_field_size[i] + hud_fontsize.x;
1462                 }
1463         }
1464
1465         if(pl.eliminated)
1466                 drawfill(h_pos, h_size, '0 0 0', sbt_highlight_alpha_eliminated, DRAWFLAG_NORMAL);
1467 }
1468
1469 vector Scoreboard_DrawOthers(vector item_pos, vector rgb, int this_team, entity ignored_pl, entity pl, int pl_number)
1470 {
1471         int i = 0;
1472         vector h_pos = item_pos;
1473         vector h_size = vec2(panel_size.x, hud_fontsize.y * 1.25);
1474         vector sz;
1475
1476         bool complete = (this_team == NUM_SPECTATOR);
1477
1478         if(!complete)
1479         if((sbt_highlight) && (!(pl_number % 2)))
1480                 drawfill(h_pos, h_size, rgb, sbt_highlight_alpha, DRAWFLAG_NORMAL);
1481
1482         vector pos = item_pos;
1483         pos.x += hud_fontsize.x * 0.5;
1484         pos.y += (1.25 - 1) / 2 * hud_fontsize.y; // center text vertically
1485
1486         float width_limit = item_pos.x + panel_size.x - hud_fontsize.x;
1487         if(!complete)
1488                 width_limit -= stringwidth("...", false, hud_fontsize);
1489         float namesize = autocvar_hud_panel_scoreboard_namesize * hud_fontsize.x;
1490         static float max_name_width = 0;
1491         string field = "";
1492         float fieldsize = 0;
1493         float min_fieldsize = 0;
1494         float fieldpadding = hud_fontsize.x * 0.25;
1495         if(this_team == NUM_SPECTATOR)
1496         {
1497                 if(autocvar_hud_panel_scoreboard_spectators_showping)
1498                         min_fieldsize = stringwidth("999", false, hud_fontsize);
1499         }
1500         else if(autocvar_hud_panel_scoreboard_others_showscore)
1501                 min_fieldsize = stringwidth("99", false, hud_fontsize);
1502         for(i = 0; pl; pl = pl.sort_next)
1503         {
1504                 if(pl.team != this_team)
1505                         continue;
1506                 if(pl == ignored_pl)
1507                         continue;
1508                 
1509                 string flag_name = "";
1510                 vector flag_size = '0 0 0';
1511                 Scoreboard_GetField(pl, SP_COUNTRY, autocvar_hud_panel_scoreboard_scores_per_round);
1512                 
1513                 if(sbt_field_icon3 != "") {
1514                         sz = draw_getimagesize(sbt_field_icon3);
1515                         flag_name = sbt_field_icon3;
1516                         flag_size = vec2(hud_fontsize.x * (sz.x / sz.y), hud_fontsize.y);
1517                 }
1518                 
1519                 if(entcs_GetWantsJoin(pl.sv_entnum))
1520                 {
1521                         vector tmcolor = Team_ColorRGB(Team_IndexToTeam(entcs_GetWantsJoin(pl.sv_entnum)));
1522                         tmcolor -= tmcolor * sin(2*M_PI*time);
1523
1524                         drawstring(pos, "(Q)", hud_fontsize, tmcolor, sbt_fg_alpha, DRAWFLAG_NORMAL);
1525                         pos.x += stringwidth("(Q) ", true, hud_fontsize);
1526                 }
1527
1528                 field = "";
1529                 if(this_team == NUM_SPECTATOR)
1530                 {
1531                         if(autocvar_hud_panel_scoreboard_spectators_showping)
1532                                 field = Scoreboard_GetField(pl, SP_PING, autocvar_hud_panel_scoreboard_scores_per_round);
1533                 }
1534                 else if(autocvar_hud_panel_scoreboard_others_showscore)
1535                         field = Scoreboard_GetField(pl, SP_SCORE, autocvar_hud_panel_scoreboard_scores_per_round);
1536
1537                 string str;
1538                 if(entcs_GetRank(pl.sv_entnum) != "")
1539                         str = strcat(entcs_GetRank(pl.sv_entnum), "^7 ", entcs_GetName(pl.sv_entnum));
1540                 else
1541                         str = entcs_GetName(pl.sv_entnum);
1542                 if (autocvar_hud_panel_scoreboard_playerid)
1543                         str = Scoreboard_AddPlayerId(str, pl);
1544                 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
1545                 float column_width = stringwidth(str, true, hud_fontsize);
1546                 if((this_team == NUM_SPECTATOR) && autocvar_hud_panel_scoreboard_spectators_aligned)
1547                 {
1548                         if(column_width > max_name_width)
1549                                 max_name_width = column_width;
1550                         column_width = max_name_width;
1551                 }
1552                 if(field != "")
1553                 {
1554                         fieldsize = stringwidth(field, false, hud_fontsize);
1555                         column_width += hud_fontsize.x * 0.25 + max(fieldsize, min_fieldsize) + 2 * fieldpadding;
1556                 }
1557
1558                 if(pos.x + column_width > width_limit)
1559                 {
1560                         ++i;
1561                         if(!complete)
1562                         {
1563                                 drawstring(pos, "...", hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
1564                                 break;
1565                         }
1566                         else
1567                         {
1568                                 pos.x = item_pos.x + hud_fontsize.x * 0.5;
1569                                 pos.y += hud_fontsize.y * 1.25;
1570                         }
1571                 }
1572
1573                 if(flag_name != "") {
1574                         drawpic(pos, flag_name, flag_size, sbt_field_icon1_rgb, sbt_fg_alpha, DRAWFLAG_NORMAL);
1575                         pos.x += flag_size.x + hud_fontsize.x * 0.5;
1576                 }
1577
1578                 if (scoreboard_selected_panel == SB_PANEL_SCOREBOARD && scoreboard_ui_enabled == 1)
1579                 {
1580                         if (pl == scoreboard_selected_player)
1581                         {
1582                                 h_size.x = column_width + hud_fontsize.x * 0.25;
1583                                 h_size.y = hud_fontsize.y;
1584                                 drawfill(pos - hud_fontsize.x * 0.25 * eX, h_size, rgb, 0.44 * panel_fg_alpha, DRAWFLAG_NORMAL);
1585                         }
1586                 }
1587
1588                 vector name_pos = pos;
1589                 if((this_team == NUM_SPECTATOR) && autocvar_hud_panel_scoreboard_spectators_aligned)
1590                         name_pos.x += max(fieldsize, min_fieldsize) + 2 * fieldpadding + hud_fontsize.x * 0.25;
1591
1592                 drawcolorcodedstring(name_pos, str, hud_fontsize, sbt_fg_alpha, DRAWFLAG_NORMAL);
1593                 if(field != "")
1594                 {
1595                         h_size.x = max(fieldsize, min_fieldsize) + 2 * fieldpadding;
1596                         h_size.y = hud_fontsize.y;
1597                         vector field_pos = pos;
1598                         if(!((this_team == NUM_SPECTATOR) && autocvar_hud_panel_scoreboard_spectators_aligned))
1599                                 field_pos.x += column_width - h_size.x;
1600                         if(sbt_highlight)
1601                                 drawfill(field_pos, h_size, '1 1 1', sbt_highlight_alpha, DRAWFLAG_NORMAL);
1602                         field_pos.x += fieldpadding + (max(fieldsize, min_fieldsize) - fieldsize) * 0.5;
1603                         drawstring(field_pos, field, hud_fontsize, sbt_field_rgb, sbt_fg_alpha, DRAWFLAG_NORMAL);
1604                 }
1605                 if(pl.eliminated)
1606                 {
1607                         h_size.x = column_width + hud_fontsize.x * 0.25;
1608                         h_size.y = hud_fontsize.y;
1609                         drawfill(pos - hud_fontsize.x * 0.25 * eX, h_size, '0 0 0', sbt_highlight_alpha_eliminated, DRAWFLAG_NORMAL);
1610                 }
1611                 pos.x += column_width;
1612                 pos.x += hud_fontsize.x;
1613         }
1614         return vec2(item_pos.x, item_pos.y + i * hud_fontsize.y * 1.25);
1615 }
1616
1617 vector Scoreboard_DrawMedal(vector pos, string icon, float height, float number)
1618 {
1619         if(!number) return pos;
1620         total_medals += number;
1621         
1622         vector tmp_sz, tmp_sz2;
1623         tmp_sz = draw_getimagesize(icon);
1624         tmp_sz2 = vec2(height*(tmp_sz.x/tmp_sz.y), height);
1625         string val = ftos(number);
1626         
1627         drawpic(pos, icon, tmp_sz2, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1628         
1629         pos.x += tmp_sz2.x + hud_fontsize.x * 0.25;
1630         drawstring(pos + eY * ((tmp_sz2.y - hud_fontsize.y) / 2), val, hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1631         
1632         pos.x += stringwidth(val, false, hud_fontsize) + hud_fontsize.x * 0.5;
1633         return pos;
1634 }
1635
1636 vector Scoreboard_Duel_DrawPickup(vector pos, bool skinned, string icon, vector sz, float number, bool invert)
1637 {
1638         vector tmp_in = pos;
1639         vector tmp_sz, tmp_sz2;
1640         string picpath;
1641         
1642         // Icon
1643         if(skinned) {
1644                 picpath = strcat(hud_skin_path, "/", icon);
1645                 if(precache_pic(picpath) == "")
1646                         picpath = strcat("gfx/hud/default/", icon);
1647         } else {
1648                 picpath = icon;
1649         }
1650                 
1651         tmp_sz = draw_getimagesize(picpath);
1652         tmp_sz2 = vec2(sz.y*(tmp_sz.x/tmp_sz.y), sz.y);
1653         
1654         tmp_in.x = pos.x + ((sz.x - tmp_sz2.x) / 2);
1655         drawpic(tmp_in, picpath, tmp_sz2, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1656         
1657         // Number
1658         if(invert)
1659                 tmp_in.x += tmp_sz2.x + hud_fontsize.x * 0.25;
1660         else
1661                 tmp_in.x -= hud_fontsize.x * 0.25 + hud_fontsize.x;
1662         
1663         tmp_in.y += (tmp_sz2.y - hud_fontsize.y) / 2;
1664         drawstring(tmp_in,
1665                 ((number == -1) ? "?" : ftos(number)),
1666                 hud_fontsize, ((number > 0) ? '1 1 1' : '0.5 0.5 0.5'),
1667                 panel_fg_alpha,
1668                 DRAWFLAG_NORMAL);
1669         
1670         pos.y += sz.y * 1.1;
1671         return pos;
1672 }
1673
1674 int left_pl_dmg = 50;
1675 int right_pl_dmg = 50;
1676 void Scoreboard_Duel_DrawTable(vector pos, bool invert, entity pl, entity tm)
1677 {
1678         vector tmp, tmp_in, tmp_sz, tmp_acc;
1679         string tmp_str;
1680         float sz;
1681         float average_acc = 0;
1682         
1683         panel_pos = pos;
1684         
1685         HUD_Panel_DrawBg();
1686         
1687         // Stop here if there are no scores available
1688         if(!pl) return;
1689         if(entcs_GetSpecState(pl.sv_entnum) == ENTCS_SPEC_PURE) return;
1690         
1691         tmp = pos;
1692         tmp.x += panel_bg_padding;
1693         tmp.y += panel_bg_padding;
1694         panel_size.x -= panel_bg_padding * 2;
1695         
1696         //if (sbt_bg_alpha)
1697         //      drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", tmp, panel_size, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL);
1698
1699         // Score: highlight
1700         if(invert) { tmp.x += panel_size.x; tmp.x -= duel_score_size.x; }
1701         drawfill(tmp, duel_score_size, '0 0 0', sbt_highlight_alpha, DRAWFLAG_NORMAL);
1702         
1703         // Score: text
1704         tmp_str = ftos(pl.(scores(SP_SCORE)));
1705         tmp_in = tmp;
1706         tmp_in.x += (duel_score_size.x / 2) - (stringwidth(tmp_str, true, duel_score_fontsize) / 2);
1707         tmp_in.y += (duel_score_size.y / 2) - (duel_score_fontsize.y / 2);
1708         
1709         draw_beginBoldFont();
1710         drawstring(tmp_in, tmp_str, duel_score_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1711         draw_endBoldFont();
1712         
1713         tmp_in = tmp;
1714         tmp_in.y += (duel_score_size.y - duel_name_fontsize.y) / 2;
1715         
1716         // RJZ rank
1717         string rank_str = entcs_GetRank(pl.sv_entnum);
1718         if(rank_str != "") {
1719                 if(invert)
1720                         tmp_in.x -= stringwidth_colors(rank_str, duel_name_fontsize) + duel_name_fontsize.x * 0.5;
1721                 else
1722                         tmp_in.x += duel_score_size.x + duel_name_fontsize.x * 0.5;
1723                 
1724                 draw_beginBoldFont();
1725                 drawcolorcodedstring(tmp_in, rank_str, duel_name_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
1726                 draw_endBoldFont();
1727         }
1728         
1729         // Player name
1730         tmp_str = entcs_GetName(pl.sv_entnum);
1731         if(invert)
1732                 tmp_in.x -= stringwidth_colors(tmp_str, duel_name_fontsize) + duel_name_fontsize.x * 0.5;
1733         else
1734                 tmp_in.x += (rank_str != "" ? stringwidth_colors(rank_str, duel_name_fontsize) : duel_score_size.x) + duel_name_fontsize.x * 0.5;
1735         drawcolorcodedstring(tmp_in, tmp_str, duel_name_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
1736         
1737         //LegendGuard adds a conditional sentence for country column 05-04-2021
1738         // Player country icon/flag
1739         Scoreboard_GetField(pl, SP_COUNTRY, autocvar_hud_panel_scoreboard_scores_per_round);
1740         if(sbt_field_icon3 != "") {
1741                 vector rsz = draw_getimagesize(sbt_field_icon3);
1742                 sbt_fixcolumnwidth_iconlen = rsz.x / rsz.y;
1743                 if(invert)
1744                         tmp_in.x -= hud_fontsize.x * sbt_fixcolumnwidth_iconlen + duel_name_fontsize.x * 0.5;
1745                 else
1746                         tmp_in.x += stringwidth_colors(tmp_str, duel_name_fontsize) + duel_name_fontsize.x * 0.5;
1747                 tmp_in.y += (duel_name_fontsize.y - hud_fontsize.y) / 2;
1748                 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);
1749         }
1750         
1751         // Header
1752         float column_width = panel_size.x / 5;
1753         tmp.x = pos.x + panel_bg_padding;
1754         tmp.y += hud_fontsize.y * 3 + hud_fontsize.y;
1755         
1756         vector column_dim;
1757         int i;
1758
1759         i = (invert ? 4 : 0);
1760         column_dim = vec2(column_width * 4, hud_fontsize.y);
1761         
1762         drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth("kills", false, hud_fontsize) / 2),
1763                 "kills", hud_fontsize, '0.5 0.5 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1764         drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth("dmg", false, hud_fontsize) / 2),
1765                 "dmg", hud_fontsize, '0.5 0.5 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1766         drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth("acc", false, hud_fontsize) / 2),
1767                 "acc", hud_fontsize, '0.5 0.5 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1768         drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth("hits", false, hud_fontsize) / 2),
1769                 "hits", hud_fontsize, '0.5 0.5 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1770         drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth("ping", false, hud_fontsize) / 2),
1771                 "ping", hud_fontsize, '0.5 0.5 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1772         
1773         tmp.x = pos.x + panel_bg_padding;
1774         tmp.y += hud_fontsize.y;
1775         
1776         // Main row
1777         i = (invert ? 4 : 0);
1778         
1779         tmp_str = ftos(pl.(scores(SP_KILLS)));
1780         drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth(tmp_str, false, hud_fontsize * 1.25) / 2),
1781                 tmp_str, hud_fontsize  * 1.25, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1782         
1783         tmp_str = ftos(pl.(scores(SP_DMG)));
1784         drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth(tmp_str, false, hud_fontsize * 1.25) / 2),
1785                 tmp_str, hud_fontsize  * 1.25, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1786         
1787         tmp_acc = tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2);
1788         
1789         if(invert)
1790                 i--;
1791         else
1792                 i++;
1793         
1794         tmp_str = Scoreboard_GetField(pl, SP_PING, autocvar_hud_panel_scoreboard_scores_per_round);
1795         drawstring(tmp + eX * column_width * i + (eX * column_width / 2) - eX * (stringwidth(tmp_str, false, hud_fontsize * 1.25) / 2),
1796                 tmp_str, hud_fontsize * 1.25, sbt_field_rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
1797
1798         tmp_str = Scoreboard_GetField(pl, SP_PL, autocvar_hud_panel_scoreboard_scores_per_round);
1799         drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth(tmp_str, false, hud_fontsize * 0.75) / 2) + eY * (hud_fontsize.y * 1.25),
1800                 tmp_str, hud_fontsize * 0.75, sbt_field_rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
1801
1802         tmp.x = pos.x + panel_bg_padding;
1803         tmp.y += hud_fontsize.y * 2;
1804         
1805         tmp_in = tmp;
1806         
1807         // Accuracy rows
1808         int dmg_percent;
1809         vector dmg_color;
1810         
1811         total_weapons = 0;
1812         int used_weapons = 0;
1813         
1814         WepSet weapons_inmap = WepSet_GetFromStat_InMap();
1815         FOREACH(Weapons, it != WEP_Null, {
1816                 WepSet set = it.m_wepset;
1817                 if (!(weapons_inmap & set) && it != WEP_BLASTER && it != WEP_SHOTGUN) // z411 TODO : We'll be hardcoding this for now.
1818                         continue;
1819                 if (it.spawnflags & WEP_TYPE_OTHER)
1820                         continue;
1821                 
1822                 int weapon_cnt_fired = pl.accuracy_cnt_fired[i - WEP_FIRST];
1823                 int weapon_cnt_hit   = pl.accuracy_cnt_hit[i - WEP_FIRST];
1824                 int weapon_acc = 0;
1825                 if(weapon_cnt_fired)
1826                         weapon_acc = floor((weapon_cnt_hit / weapon_cnt_fired) * 100);
1827                 average_acc += weapon_acc;
1828                 
1829                 // center vertically
1830                 vector row_in = tmp_in;
1831                 row_in.y += ((hud_fontsize.y * autocvar_hud_panel_scoreboard_duel_weapon_scale) - column_dim.y) / 2;
1832                 
1833                 // draw row background
1834                 drawfill(row_in + eX * column_width * (invert ? 1 : 0), column_dim, '0 0 0', sbt_highlight_alpha, DRAWFLAG_NORMAL);
1835                 
1836                 if(weapon_cnt_fired) {
1837                         if(invert) {
1838                                 if(pl.accuracy_hit[i - WEP_FIRST] > left_pl_dmg)
1839                                         left_pl_dmg = pl.accuracy_hit[i - WEP_FIRST];
1840                                 dmg_percent = pl.accuracy_hit[i - WEP_FIRST] / left_pl_dmg;
1841                         } else {
1842                                 if(pl.accuracy_hit[i - WEP_FIRST] > right_pl_dmg)
1843                                         right_pl_dmg = pl.accuracy_hit[i - WEP_FIRST];
1844                                 dmg_percent = pl.accuracy_hit[i - WEP_FIRST] / right_pl_dmg;
1845                         }
1846                         
1847                         // convert percentage range to 0.4 - 1
1848                         dmg_percent = dmg_percent * (1 - 0.4) + 0.4;
1849                         
1850                         dmg_color.x = dmg_percent;
1851                         dmg_color.y = dmg_percent;
1852                         dmg_color.z = dmg_percent;
1853                         
1854                         string draw_str;
1855                         
1856                         // weapon stats
1857                         int c = (invert ? 4 : 0);
1858                         
1859                         draw_str = ftos(pl.accuracy_frags[i - WEP_FIRST]);
1860                         drawstring(row_in + eX * column_width * (invert ? c-- : c++) + eX * ((column_width - stringwidth(draw_str, false, hud_fontsize)) / 2),
1861                                 draw_str, hud_fontsize, dmg_color, panel_fg_alpha, DRAWFLAG_NORMAL);
1862                         
1863                         draw_str = ftos(pl.accuracy_hit[i - WEP_FIRST]);
1864                         drawstring(row_in + eX * column_width * (invert ? c-- : c++) + eX * ((column_width - stringwidth(draw_str, false, hud_fontsize)) / 2),
1865                                 draw_str, hud_fontsize, dmg_color, panel_fg_alpha, DRAWFLAG_NORMAL);
1866                         
1867                         draw_str = sprintf("%d%%", weapon_acc);
1868                         drawstring(row_in + eX * column_width * (invert ? c-- : c++) + eX * ((column_width - stringwidth(draw_str, false, hud_fontsize)) / 2),
1869                                 draw_str, hud_fontsize, dmg_color, panel_fg_alpha, DRAWFLAG_NORMAL);
1870                         
1871                         draw_str = strcat(ftos(weapon_cnt_hit), " / ", ftos(weapon_cnt_fired));
1872                         drawstring(row_in + eX * column_width * (invert ? c-- : c++) + eX * (column_width / 2) - eX * stringwidth(ftos(weapon_cnt_hit), false, hud_fontsize) - eX * hud_fontsize.x * 0.5,
1873                                 draw_str, hud_fontsize, dmg_color, panel_fg_alpha, DRAWFLAG_NORMAL);
1874
1875                         used_weapons++;
1876                 }
1877                 
1878                 // weapon icon
1879                 if(invert) {
1880                         tmp_in.x = pos.x + panel_size.x - panel_bg_padding - hud_fontsize.x / 2;
1881                         drawpic_aspect_skin(tmp_in, it.model2, vec2(50, hud_fontsize.y * autocvar_hud_panel_scoreboard_duel_weapon_scale), '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1882                 }
1883                 
1884                 tmp_in.x = pos.x + panel_bg_padding;
1885                 tmp_in.y += hud_fontsize.y * autocvar_hud_panel_scoreboard_duel_weapon_scale;
1886                 
1887                 total_weapons++;
1888         });
1889         
1890         if(used_weapons)
1891                 average_acc = floor((average_acc / used_weapons) + 0.5);
1892         
1893         // draw total accuracy now
1894         tmp_str = sprintf("%d%%", average_acc);
1895         drawstring(tmp_acc - eX * (stringwidth(tmp_str, false, hud_fontsize * 1.25) / 2),
1896                 tmp_str, hud_fontsize * 1.25, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1897         
1898         // Icon column
1899         vector icon_sz = vec2(column_width, hud_fontsize.y*1.5);
1900         
1901         if(!invert)
1902                 tmp.x += column_width * 4;
1903         // Medal rows
1904         drawstring(tmp + eX * ((column_width - stringwidth("medals", false, hud_fontsize)) / 2),
1905                 "medals", hud_fontsize, '0.5 0.5 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1906         tmp.y += hud_fontsize.y * 1.25;
1907         
1908         tmp = Scoreboard_Duel_DrawPickup(tmp, false, "gfx/medal/humiliation", icon_sz, pl.(scores(SP_MEDAL_HUMILIATION)), invert);
1909         tmp = Scoreboard_Duel_DrawPickup(tmp, false, "gfx/medal/impressive", icon_sz, pl.(scores(SP_MEDAL_IMPRESSIVE)), invert);
1910         tmp = Scoreboard_Duel_DrawPickup(tmp, false, "gfx/medal/excellent", icon_sz, pl.(scores(SP_MEDAL_EXCELLENT)), invert);
1911         
1912         // Item rows
1913         drawstring(tmp + eX * ((column_width - stringwidth("items", false, hud_fontsize)) / 2),
1914                 "items", hud_fontsize, '0.5 0.5 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1915         tmp.y += hud_fontsize.y * 1.25;
1916         
1917         float inv_num = -1;
1918         FOREACH(Items,
1919                 it.m_id == ITEM_ArmorMega.m_id ||
1920                 it.m_id == ITEM_HealthMega.m_id ||
1921                 it.m_id == ITEM_ArmorBig.m_id, {
1922                 // If the match isn't over, Only show pickups if we're spectating or they're our own
1923                 if(intermission || warmup_stage || spectatee_status || pl.sv_entnum == current_player)
1924                         inv_num = inventoryslots[pl.sv_entnum].inv_items[it.m_id];
1925                 tmp = Scoreboard_Duel_DrawPickup(tmp, true, it.m_icon, icon_sz, inv_num, invert);
1926                 
1927                 if(it.m_id == REGISTRY_MAX(Items))
1928                 break;
1929         });
1930 }
1931 vector Scoreboard_MakeDuelTable(vector pos, entity tm, vector rgb, vector bg_size)
1932 {
1933         vector end_pos = pos;
1934         float screen_half = panel_size.x / 2;
1935         float weapon_margin = hud_fontsize.x;
1936         
1937         panel_size.x = screen_half - weapon_margin;
1938         if(total_weapons)
1939                 panel_size.y = max(duel_score_size.y * 5.5, duel_score_size.y * 2.25 + (hud_fontsize.y * autocvar_hud_panel_scoreboard_duel_weapon_scale * total_weapons));
1940         else
1941                 panel_size.y = duel_score_size.y * 5.5;
1942         
1943         entity pl_left = players.sort_next;
1944         entity pl_right = pl_left.sort_next;
1945         
1946         Scoreboard_Duel_DrawTable(pos, true, pl_left, tm);
1947         Scoreboard_Duel_DrawTable(pos + eX * screen_half + eX * weapon_margin, false, pl_right, tm);
1948         
1949         end_pos.y += panel_size.y + (panel_bg_padding * 2);
1950         panel_size.x = screen_half * 2;
1951         return end_pos;
1952 }
1953
1954 vector Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
1955 {
1956         int max_players = 999;
1957         if(autocvar_hud_panel_scoreboard_maxheight > 0)
1958         {
1959                 float height = autocvar_hud_panel_scoreboard_maxheight * vid_conheight;
1960                 if(teamplay)
1961                 {
1962                         height -= (panel_bg_padding * 2 + hud_fontsize.y * 1.25) * team_count; // - padding and header
1963                         height -= hud_fontsize.y * (team_count - 1); // - spacing between tables
1964                         height /= team_count;
1965                 }
1966                 else
1967                         height -= panel_bg_padding * 2; // - padding
1968                 max_players = floor(height / (hud_fontsize.y * 1.25));
1969                 if(max_players <= 1)
1970                         max_players = 1;
1971                 if(max_players == tm.team_size)
1972                         max_players = 999;
1973         }
1974
1975         entity pl;
1976         entity me = playerslots[current_player];
1977         panel_pos = pos;
1978         panel_size.y = 1.25 * hud_fontsize.y * (1 + bound(1, tm.team_size, max_players));
1979         panel_size.y += panel_bg_padding * 2;
1980
1981         vector scoreboard_selected_hl_pos = pos;
1982         vector scoreboard_selected_hl_size = '0 0 0';
1983         scoreboard_selected_hl_size.x = scoreboard_right - scoreboard_left;
1984         scoreboard_selected_hl_size.y = panel_size.y;
1985
1986         HUD_Panel_DrawBg();
1987
1988         vector end_pos = panel_pos + eY * (panel_size.y + 0.5 * hud_fontsize.y);
1989         if(panel.current_panel_bg != "0")
1990                 end_pos.y += panel_bg_border * 2;
1991
1992         if(panel_bg_padding)
1993         {
1994                 panel_pos += '1 1 0' * panel_bg_padding;
1995                 panel_size -= '2 2 0' * panel_bg_padding;
1996         }
1997
1998         pos = panel_pos;
1999         vector tmp = vec2(panel_size.x, 1.25 * hud_fontsize.y);
2000
2001         // rounded header
2002         if (sbt_bg_alpha)
2003                 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', sbt_bg_alpha, DRAWFLAG_NORMAL);
2004
2005         pos.y += 1.25 * hud_fontsize.y;
2006
2007         // table background
2008         tmp.y = panel_size.y - 1.25 * hud_fontsize.y;
2009         if (sbt_bg_alpha)
2010                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL);
2011
2012
2013         // print header row and highlight columns
2014         pos = Scoreboard_DrawHeader(panel_pos, rgb, (max_players < tm.team_size), tm.team);
2015
2016         // fill the table and draw the rows
2017         bool is_self = false;
2018         bool self_shown = false;
2019         int i = 0;
2020         int with_ping = 0;
2021         if(Team_IsValidTeam(tm.team)) average_ping[Team_TeamToIndex(tm.team) - 1] = 0;
2022         for(pl = players.sort_next; pl; pl = pl.sort_next)
2023         {
2024                 if(pl.team != tm.team)
2025                         continue;
2026                 if(i == max_players - 2 && pl != me)
2027                 {
2028                         if(!self_shown && me.team == tm.team)
2029                         {
2030                                 Scoreboard_DrawItem(pos, rgb, me, true, i);
2031                                 self_shown = true;
2032                                 pos.y += 1.25 * hud_fontsize.y;
2033                                 ++i;
2034                         }
2035                 }
2036                 if(i >= max_players - 1)
2037                 {
2038                         pos = Scoreboard_DrawOthers(pos, rgb, tm.team, (self_shown ? me : NULL), pl, i);
2039                         break;
2040                 }
2041                 is_self = (pl.sv_entnum == current_player);
2042                 Scoreboard_DrawItem(pos, rgb, pl, is_self, i);
2043                 
2044                 if(Team_IsValidTeam(tm.team) && pl.ping) {
2045                         average_ping[Team_TeamToIndex(tm.team) - 1] += pl.ping;
2046                         ++with_ping;
2047                 }
2048                 if(is_self)
2049                         self_shown = true;
2050                 pos.y += 1.25 * hud_fontsize.y;
2051                 ++i;
2052         }
2053         if(with_ping) average_ping[Team_TeamToIndex(tm.team) - 1] /= with_ping;
2054
2055         if (scoreboard_selected_panel == SB_PANEL_SCOREBOARD)
2056         {
2057                 if (scoreboard_ui_enabled == 1 || (tm && scoreboard_selected_team == tm))
2058                 {
2059                         float _alpha = (scoreboard_ui_enabled == 2) ? 0.2 : 0.3 * max(0, (1 - (time - scoreboard_selected_panel_time) * 2));
2060                         _alpha *= panel_fg_alpha;
2061                         if (_alpha)
2062                                 drawfill(scoreboard_selected_hl_pos, scoreboard_selected_hl_size, '1 1 1', _alpha, DRAWFLAG_NORMAL);
2063                 }
2064         }
2065
2066         panel_size.x += panel_bg_padding * 2; // restore initial width
2067         return end_pos;
2068 }
2069
2070 bool Scoreboard_WouldDraw()
2071 {
2072         if (scoreboard_ui_enabled)
2073         {
2074                 if (scoreboard_ui_disabling)
2075                 {
2076                         if (scoreboard_fade_alpha == 0)
2077                                 HUD_Scoreboard_UI_Disable_Instantly();
2078                         return false;
2079                 }
2080                 if (intermission && scoreboard_ui_enabled == 2)
2081                 {
2082                         HUD_Scoreboard_UI_Disable_Instantly();
2083                         return false;
2084                 }
2085                 return true;
2086         }
2087         else if (MUTATOR_CALLHOOK(DrawScoreboard))
2088                 return false;
2089         else if (QuickMenu_IsOpened())
2090                 return false;
2091         else if (HUD_Radar_Clickable())
2092                 return false;
2093         else if (sb_showscores) // set by +showscores engine command
2094                 return true;
2095         else if (intermission == 1)
2096                 return true;
2097         else if (intermission == 2)
2098                 return false;
2099         else if (!spectatee_status && STAT(HEALTH) <= 0 && autocvar_cl_deathscoreboard && !MUTATOR_CALLHOOK(DrawDeathScoreboard)
2100                 && (!HUD_MinigameMenu_IsOpened() || !active_minigame))
2101         {
2102                 return true;
2103         }
2104         else if (scoreboard_showscores_force || MUTATOR_CALLHOOK(DrawScoreboard_Force))
2105                 return true;
2106         return false;
2107 }
2108
2109 vector Scoreboard_MedalStats_Draw(vector pos)
2110 {
2111         vector orig = pos;
2112         float height = hud_fontsize.y * 2;
2113         
2114         entity pl = playerslots[current_player];
2115         
2116         vector title_pos = pos;
2117         pos.x += 0.5 * hud_fontsize.x + panel_bg_padding;
2118         pos.y += 1.25 * hud_fontsize.y;
2119         
2120         total_medals = 0;
2121         
2122         pos = Scoreboard_DrawMedal(pos, "gfx/medal/airshot",            height, pl.(scores(SP_MEDAL_AIRSHOT)));
2123         pos = Scoreboard_DrawMedal(pos, "gfx/medal/damage",             height, pl.(scores(SP_MEDAL_DAMAGE)));
2124         pos = Scoreboard_DrawMedal(pos, "gfx/medal/electrobitch",       height, pl.(scores(SP_MEDAL_ELECTROBITCH)));
2125         pos = Scoreboard_DrawMedal(pos, "gfx/medal/excellent",          height, pl.(scores(SP_MEDAL_EXCELLENT)));
2126         pos = Scoreboard_DrawMedal(pos, "gfx/medal/firstblood",         height, pl.(scores(SP_MEDAL_FIRSTBLOOD)));
2127         pos = Scoreboard_DrawMedal(pos, "gfx/medal/headshot",           height, pl.(scores(SP_MEDAL_HEADSHOT)));
2128         pos = Scoreboard_DrawMedal(pos, "gfx/medal/humiliation",        height, pl.(scores(SP_MEDAL_HUMILIATION)));
2129         pos = Scoreboard_DrawMedal(pos, "gfx/medal/impressive",         height, pl.(scores(SP_MEDAL_IMPRESSIVE)));
2130         pos = Scoreboard_DrawMedal(pos, "gfx/medal/yoda",                       height, pl.(scores(SP_MEDAL_YODA)));
2131         pos = Scoreboard_DrawMedal(pos, "gfx/medal/telefrag",           height, pl.(scores(SP_MEDAL_TELEFRAG)));
2132         
2133         if(total_medals)
2134                 pos.x += hud_fontsize.x;
2135         
2136         pos = Scoreboard_DrawMedal(pos, "gfx/medal/accuracy",           height, pl.(scores(SP_MEDAL_ACCURACY)));
2137         pos = Scoreboard_DrawMedal(pos, "gfx/medal/assist",             height, pl.(scores(SP_MEDAL_ASSIST)));
2138         pos = Scoreboard_DrawMedal(pos, "gfx/medal/capture",            height, pl.(scores(SP_MEDAL_CAPTURE)));
2139         pos = Scoreboard_DrawMedal(pos, "gfx/medal/defense",            height, pl.(scores(SP_MEDAL_DEFENSE)));
2140         pos = Scoreboard_DrawMedal(pos, "gfx/medal/perfect",            height, pl.(scores(SP_MEDAL_PERFECT)));
2141         
2142         if(!total_medals) return orig;
2143         
2144         drawstring(title_pos, sprintf(_("Medal stats (total %d)"), total_medals),
2145                 hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
2146         
2147         pos.x = orig.x;
2148         pos.y += height + hud_fontsize.y * 0.5;
2149         return pos;
2150 }
2151
2152 float average_accuracy;
2153 vector Scoreboard_AccuracyStats_Draw(vector pos, vector rgb, vector bg_size)
2154 {
2155         scoreboard_acc_fade_alpha = min(scoreboard_fade_alpha, scoreboard_acc_fade_alpha + frametime * 10);
2156
2157         WepSet weapons_stat = WepSet_GetFromStat();
2158         WepSet weapons_inmap = WepSet_GetFromStat_InMap();
2159         int disownedcnt = 0;
2160         int nHidden = 0;
2161         FOREACH(Weapons, it != WEP_Null, {
2162                 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
2163
2164                 WepSet set = it.m_wepset;
2165                 if(it.spawnflags & WEP_TYPE_OTHER)
2166                 {
2167                         ++nHidden;
2168                         continue;
2169                 }
2170                 if (weapon_stats < 0 && !((weapons_stat & set) || (weapons_inmap & set)))
2171                 {
2172                         if (it.spawnflags & (WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_SPECIALATTACK))
2173                                 ++nHidden;
2174                         else
2175                                 ++disownedcnt;
2176                 }
2177         });
2178
2179         int weapon_cnt = (REGISTRY_COUNT(Weapons) - 1) - disownedcnt - nHidden;
2180         if (weapon_cnt <= 0) return pos;
2181
2182         int rows = 1;
2183         if (autocvar_hud_panel_scoreboard_accuracy_doublerows && weapon_cnt >= floor((REGISTRY_COUNT(Weapons) - nHidden - 1) * 0.5))
2184                 rows = 2;
2185         int columns = ceil(weapon_cnt / rows);
2186
2187         float aspect = max(0.001, autocvar_hud_panel_weapons_aspect);
2188         float weapon_height = hud_fontsize.y * 2.3 / aspect;
2189         float height = weapon_height + hud_fontsize.y;
2190
2191         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);
2192         pos.y += 1.25 * hud_fontsize.y;
2193         if(panel.current_panel_bg != "0")
2194                 pos.y += panel_bg_border;
2195
2196         panel_pos = pos;
2197         panel_size.y = height * rows;
2198         panel_size.y += panel_bg_padding * 2;
2199
2200         float panel_bg_alpha_save = panel_bg_alpha;
2201         panel_bg_alpha *= scoreboard_acc_fade_alpha;
2202         HUD_Panel_DrawBg();
2203         panel_bg_alpha = panel_bg_alpha_save;
2204
2205         vector end_pos = panel_pos + eY * (panel_size.y + 0.5 * hud_fontsize.y);
2206         if(panel.current_panel_bg != "0")
2207                 end_pos.y += panel_bg_border * 2;
2208
2209         if(panel_bg_padding)
2210         {
2211                 panel_pos += '1 1 0' * panel_bg_padding;
2212                 panel_size -= '2 2 0' * panel_bg_padding;
2213         }
2214
2215         pos = panel_pos;
2216         vector tmp = panel_size;
2217
2218         float weapon_width = tmp.x / columns / rows;
2219
2220         if (sbt_bg_alpha)
2221                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, sbt_bg_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL);
2222
2223         if(sbt_highlight)
2224         {
2225                 // column highlighting
2226                 for (int i = 0; i < columns; ++i)
2227                         if ((i % 2) == 0)
2228                                 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);
2229
2230                 // row highlighting
2231                 for (int i = 0; i < rows; ++i)
2232                         drawfill(pos + eY * (weapon_height + height * i), vec2(tmp.x, hud_fontsize.y), rgb, sbt_highlight_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL);
2233         }
2234
2235         average_accuracy = 0;
2236         int weapons_with_stats = 0;
2237         if (rows == 2)
2238                 pos.x += weapon_width / 2;
2239
2240         if (autocvar_hud_panel_scoreboard_accuracy_nocolors)
2241                 rgb = '1 1 1';
2242         else
2243                 Accuracy_LoadColors();
2244
2245         float oldposx = pos.x;
2246         vector tmpos = pos;
2247
2248         int column = 0;
2249         FOREACH(Weapons, it != WEP_Null, {
2250                 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
2251
2252                 WepSet set = it.m_wepset;
2253                 if (weapon_stats < 0 && !((weapons_stat & set) || (weapons_inmap & set)))
2254                         continue;
2255                 if (it.spawnflags & WEP_TYPE_OTHER)
2256                         continue;
2257
2258                 float weapon_alpha;
2259                 if (weapon_stats >= 0)
2260                         weapon_alpha = sbt_fg_alpha;
2261                 else
2262                         weapon_alpha = 0.2 * sbt_fg_alpha;
2263
2264                 // weapon icon
2265                 drawpic_aspect_skin(tmpos, it.model2, vec2(weapon_width, weapon_height), '1 1 1', weapon_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL);
2266                 // the accuracy
2267                 if (weapon_stats >= 0) {
2268                         weapons_with_stats += 1;
2269                         average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
2270
2271                         string s = sprintf("%d%%", weapon_stats * 100);
2272                         float padding = (weapon_width - stringwidth(s, false, hud_fontsize)) / 2;
2273
2274                         if(!autocvar_hud_panel_scoreboard_accuracy_nocolors)
2275                                 rgb = Accuracy_GetColor(weapon_stats);
2276
2277                         drawstring(tmpos + vec2(padding, weapon_height), s, hud_fontsize, rgb, sbt_fg_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL);
2278                 }
2279                 tmpos.x += weapon_width * rows;
2280                 pos.x += weapon_width * rows;
2281                 if (rows == 2 && column == columns - 1) {
2282                         tmpos.x = oldposx;
2283                         tmpos.y += height;
2284                         pos.y += height;
2285                 }
2286                 ++column;
2287         });
2288
2289         if (weapons_with_stats)
2290                 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
2291
2292         panel_size.x += panel_bg_padding * 2; // restore initial width
2293
2294         return end_pos;
2295 }
2296
2297 bool is_item_filtered(entity it)
2298 {
2299         if (!autocvar_hud_panel_scoreboard_itemstats_filter)
2300                 return false;
2301         int mask = autocvar_hud_panel_scoreboard_itemstats_filter_mask;
2302         if (mask <= 0)
2303                 return false;
2304         if (it.instanceOfArmor || it.instanceOfHealth)
2305         {
2306                 int ha_mask = floor(mask) % 10;
2307                 switch (ha_mask)
2308                 {
2309                         default: return false;
2310                         case 4: if (it == ITEM_HealthMega || it == ITEM_ArmorMega) return true; // else fallthrough
2311                         case 3: if (it == ITEM_HealthBig || it == ITEM_ArmorBig) return true; // else fallthrough
2312                         case 2: if (it == ITEM_HealthMedium || it == ITEM_ArmorMedium) return true; // else fallthrough
2313                         case 1: if (it == ITEM_HealthSmall || it == ITEM_ArmorSmall) return true; // else fallthrough
2314                 }
2315         }
2316         if (it.instanceOfAmmo)
2317         {
2318                 int ammo_mask = floor(mask / 10) % 10;
2319                 return (ammo_mask == 1);
2320         }
2321         return false;
2322 }
2323
2324 vector Scoreboard_ItemStats_Draw(vector pos, vector rgb, vector bg_size)
2325 {
2326         Inventory g_inventory = inventoryslots[current_player];
2327         scoreboard_itemstats_fade_alpha = min(scoreboard_fade_alpha, scoreboard_itemstats_fade_alpha + frametime * 10);
2328
2329         int disowned_cnt = 0;
2330         int uninteresting_cnt = 0;
2331         IL_EACH(default_order_items, true, {
2332                 int q = g_inventory.inv_items[it.m_id];
2333                 //q = 1; // debug: display all items
2334                 if (is_item_filtered(it))
2335                         ++uninteresting_cnt;
2336                 else if (!q)
2337                         ++disowned_cnt;
2338         });
2339         int items_cnt = REGISTRY_COUNT(Items) - uninteresting_cnt;
2340         int n = items_cnt - disowned_cnt;
2341         if (n <= 0) return pos;
2342
2343         int rows = (autocvar_hud_panel_scoreboard_itemstats_doublerows && n >= floor(REGISTRY_COUNT(Items) / 2)) ? 2 : 1;
2344         int columns = max(6, ceil(n / rows));
2345
2346         float item_height = hud_fontsize.y * 2.3;
2347         float height = item_height + hud_fontsize.y;
2348
2349         drawstring(pos + eX * panel_bg_padding, _("Item stats"), hud_fontsize, '1 1 1', panel_fg_alpha * scoreboard_itemstats_fade_alpha, DRAWFLAG_NORMAL);
2350         pos.y += 1.25 * hud_fontsize.y;
2351         if(panel.current_panel_bg != "0")
2352                 pos.y += panel_bg_border;
2353
2354         panel_pos = pos;
2355         panel_size.y = height * rows;
2356         panel_size.y += panel_bg_padding * 2;
2357
2358         float panel_bg_alpha_save = panel_bg_alpha;
2359         panel_bg_alpha *= scoreboard_itemstats_fade_alpha;
2360         HUD_Panel_DrawBg();
2361         panel_bg_alpha = panel_bg_alpha_save;
2362
2363         vector end_pos = panel_pos + eY * (panel_size.y + 0.5 * hud_fontsize.y);
2364         if(panel.current_panel_bg != "0")
2365                 end_pos.y += panel_bg_border * 2;
2366
2367         if(panel_bg_padding)
2368         {
2369                 panel_pos += '1 1 0' * panel_bg_padding;
2370                 panel_size -= '2 2 0' * panel_bg_padding;
2371         }
2372
2373         pos = panel_pos;
2374         vector tmp = panel_size;
2375
2376         float item_width = tmp.x / columns / rows;
2377
2378         if (sbt_bg_alpha)
2379                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, sbt_bg_alpha * scoreboard_itemstats_fade_alpha, DRAWFLAG_NORMAL);
2380
2381         if(sbt_highlight)
2382         {
2383                 // column highlighting
2384                 for (int i = 0; i < columns; ++i)
2385                         if ((i % 2) == 0)
2386                                 drawfill(pos + eX * item_width * rows * i, vec2(item_width * rows, height * rows), '0 0 0', sbt_highlight_alpha * scoreboard_itemstats_fade_alpha, DRAWFLAG_NORMAL);
2387
2388                 // row highlighting
2389                 for (int i = 0; i < rows; ++i)
2390                         drawfill(pos + eY * (item_height + height * i), vec2(panel_size.x, hud_fontsize.y), rgb, sbt_highlight_alpha * scoreboard_itemstats_fade_alpha, DRAWFLAG_NORMAL);
2391         }
2392
2393         if (rows == 2)
2394                 pos.x += item_width / 2;
2395
2396         float oldposx = pos.x;
2397         vector tmpos = pos;
2398
2399         int column = 0;
2400         IL_EACH(default_order_items, !is_item_filtered(it), {
2401                 int n = g_inventory.inv_items[it.m_id];
2402                 //n = 1 + floor(i * 3 + 4.8) % 7; // debug: display a value for each item
2403                 if (n <= 0) continue;
2404                 drawpic_aspect_skin(tmpos, it.m_icon, eX * item_width + eY * item_height, '1 1 1', panel_fg_alpha * scoreboard_itemstats_fade_alpha, DRAWFLAG_NORMAL);
2405                 string s = ftos(n);
2406                 float padding = (item_width - stringwidth(s, false, hud_fontsize)) / 2;
2407                 drawstring(tmpos + vec2(padding, item_height), s, hud_fontsize, '1 1 1', panel_fg_alpha * scoreboard_itemstats_fade_alpha, DRAWFLAG_NORMAL);
2408                 tmpos.x += item_width * rows;
2409                 pos.x += item_width * rows;
2410                 if (rows == 2 && column == columns - 1) {
2411                         tmpos.x = oldposx;
2412                         tmpos.y += height;
2413                         pos.y += height;
2414                 }
2415                 ++column;
2416         });
2417
2418         panel_size.x += panel_bg_padding * 2; // restore initial width
2419
2420         return end_pos;
2421 }
2422
2423 vector MapStats_DrawKeyValue(vector pos, string key, string value) {
2424         float px = pos.x;
2425         pos.x += hud_fontsize.x * 0.25;
2426         drawstring(pos, key, hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
2427         pos.x = panel_pos.x + panel_size.x - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
2428         drawstring(pos, value, hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
2429         pos.x = px;
2430         pos.y += hud_fontsize.y;
2431
2432         return pos;
2433 }
2434
2435 /*
2436 vector Scoreboard_MapStats_Draw(vector pos, vector rgb, vector bg_size) {
2437         float stat_secrets_found, stat_secrets_total;
2438         float stat_monsters_killed, stat_monsters_total;
2439         float rows = 0;
2440         string val;
2441
2442         // get monster stats
2443         stat_monsters_killed = STAT(MONSTERS_KILLED);
2444         stat_monsters_total = STAT(MONSTERS_TOTAL);
2445
2446         // get secrets stats
2447         stat_secrets_found = STAT(SECRETS_FOUND);
2448         stat_secrets_total = STAT(SECRETS_TOTAL);
2449
2450         // get number of rows
2451         if(stat_secrets_total)
2452                 rows += 1;
2453         if(stat_monsters_total)
2454                 rows += 1;
2455
2456         // if no rows, return
2457         if (!rows)
2458                 return pos;
2459
2460         //  draw table header
2461         drawstring(pos + eX * panel_bg_padding, _("Map stats:"), hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
2462         pos.y += 1.25 * hud_fontsize.y;
2463         if(panel.current_panel_bg != "0")
2464                 pos.y += panel_bg_border;
2465
2466         panel_pos = pos;
2467         panel_size.y = hud_fontsize.y * rows;
2468         panel_size.y += panel_bg_padding * 2;
2469         HUD_Panel_DrawBg();
2470
2471         vector end_pos = panel_pos + eY * (panel_size.y + 0.5 * hud_fontsize.y);
2472         if(panel.current_panel_bg != "0")
2473                 end_pos.y += panel_bg_border * 2;
2474
2475         if(panel_bg_padding)
2476         {
2477                 panel_pos += '1 1 0' * panel_bg_padding;
2478                 panel_size -= '2 2 0' * panel_bg_padding;
2479         }
2480
2481         pos = panel_pos;
2482         vector tmp = panel_size;
2483
2484         if (sbt_bg_alpha)
2485                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL);
2486
2487         // draw monsters
2488         if(stat_monsters_total)
2489         {
2490                 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
2491                 pos = MapStats_DrawKeyValue(pos, _("Monsters killed:"), val);
2492         }
2493
2494         // draw secrets
2495         if(stat_secrets_total)
2496         {
2497                 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
2498                 pos = MapStats_DrawKeyValue(pos, _("Secrets found:"), val);
2499         }
2500
2501         panel_size.x += panel_bg_padding * 2; // restore initial width
2502         return end_pos;
2503 }
2504 */
2505
2506 vector Scoreboard_Rankings_Draw(vector pos, string ranktitle, entity pl, vector rgb, vector bg_size)
2507 {
2508         int i;
2509         RANKINGS_RECEIVED_CNT = 0;
2510         for (i=RANKINGS_CNT-1; i>=0; --i)
2511                 if (grecordtime[i])
2512                         ++RANKINGS_RECEIVED_CNT;
2513
2514         if (RANKINGS_RECEIVED_CNT == 0)
2515                 return pos;
2516
2517         vector hl_rgb = rgb + '0.5 0.5 0.5';
2518
2519         vector scoreboard_selected_hl_pos = pos;
2520
2521         drawstring(pos + eX * panel_bg_padding, ranktitle, hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
2522         pos.y += 1.25 * hud_fontsize.y;
2523         if(panel.current_panel_bg != "0")
2524                 pos.y += panel_bg_border;
2525
2526         vector scoreboard_selected_hl_size = '0 0 0';
2527         scoreboard_selected_hl_size.x = scoreboard_right - scoreboard_left;
2528         scoreboard_selected_hl_size.y = pos.y - scoreboard_selected_hl_pos.y;
2529
2530         panel_pos = pos;
2531
2532         float namesize = 0;
2533         for(i = 0; i < RANKINGS_RECEIVED_CNT; ++i)
2534         {
2535                 float f = stringwidth(ColorTranslateRGB(grecordholder[i]), true, hud_fontsize);
2536                 if(f > namesize)
2537                         namesize = f;
2538         }
2539         bool cut = false;
2540         if(namesize > autocvar_hud_panel_scoreboard_namesize * hud_fontsize.x)
2541         {
2542                 namesize = autocvar_hud_panel_scoreboard_namesize * hud_fontsize.x;
2543                 cut = true;
2544         }
2545
2546         float ranksize = 3 * hud_fontsize.x;
2547         float timesize = 5 * hud_fontsize.x;
2548         vector columnsize = vec2(ranksize + timesize + namesize + hud_fontsize.x, 1.25 * hud_fontsize.y);
2549         rankings_columns = max(1, floor((panel_size.x - 2 * panel_bg_padding) / columnsize.x));
2550         rankings_columns = min(rankings_columns, RANKINGS_RECEIVED_CNT);
2551         if (!rankings_cnt)
2552         {
2553                 rankings_cnt = RANKINGS_RECEIVED_CNT;
2554                 rankings_rows = ceil(rankings_cnt / rankings_columns);
2555         }
2556
2557         // expand name column to fill the entire row
2558         float available_space = (panel_size.x - 2 * panel_bg_padding - columnsize.x * rankings_columns) / rankings_columns;
2559         namesize += available_space;
2560         columnsize.x += available_space;
2561
2562         panel_size.y = rankings_rows * 1.25 * hud_fontsize.y;
2563         panel_size.y += panel_bg_padding * 2;
2564         scoreboard_selected_hl_size.y += panel_size.y;
2565
2566         HUD_Panel_DrawBg();
2567
2568         vector end_pos = panel_pos + eY * (panel_size.y + 0.5 * hud_fontsize.y);
2569         if(panel.current_panel_bg != "0")
2570                 end_pos.y += panel_bg_border * 2;
2571
2572         if(panel_bg_padding)
2573         {
2574                 panel_pos += '1 1 0' * panel_bg_padding;
2575                 panel_size -= '2 2 0' * panel_bg_padding;
2576         }
2577
2578         pos = panel_pos;
2579
2580         if (sbt_bg_alpha)
2581                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, panel_size, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL);
2582
2583         vector text_ofs = vec2(0.5 * hud_fontsize.x, (1.25 - 1) / 2 * hud_fontsize.y); // center text vertically
2584         string str = "";
2585         int column = 0, j = 0;
2586         string zoned_name_self = strzone(strdecolorize(entcs_GetName(player_localnum)));
2587         int start_item = rankings_start_column * rankings_rows;
2588         for(i = start_item; i < start_item + rankings_cnt; ++i)
2589         {
2590                 int t = grecordtime[i];
2591                 if (t == 0)
2592                         continue;
2593
2594                 if(strdecolorize(grecordholder[i]) == zoned_name_self)
2595                         drawfill(pos, columnsize, hl_rgb, sbt_highlight_alpha_self, DRAWFLAG_NORMAL);
2596                 else if(!((j + rankings_start_column + column) & 1) && sbt_highlight)
2597                         drawfill(pos, columnsize, hl_rgb, sbt_highlight_alpha, DRAWFLAG_NORMAL);
2598
2599                 str = count_ordinal(i+1);
2600                 drawstring(pos + text_ofs, str, hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
2601                 drawstring(pos + text_ofs + eX * ranksize, TIME_ENCODED_TOSTRING(t, true), hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
2602                 str = ColorTranslateRGB(grecordholder[i]);
2603                 if(cut)
2604                         str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
2605                 drawcolorcodedstring(pos + text_ofs + eX * (ranksize + timesize), str, hud_fontsize, sbt_fg_alpha, DRAWFLAG_NORMAL);
2606
2607                 pos.y += 1.25 * hud_fontsize.y;
2608                 j++;
2609                 if(j >= rankings_rows)
2610                 {
2611                         column++;
2612                         j = 0;
2613                         pos.x += panel_size.x / rankings_columns;
2614                         pos.y = panel_pos.y;
2615                 }
2616         }
2617         strfree(zoned_name_self);
2618
2619         if (scoreboard_selected_panel == SB_PANEL_RANKINGS)
2620         {
2621                 float fade = max(0, (1 - (time - scoreboard_selected_panel_time) * 2));
2622                 drawfill(scoreboard_selected_hl_pos, scoreboard_selected_hl_size, '1 1 1', fade * 0.44, DRAWFLAG_NORMAL);
2623         }
2624
2625         panel_size.x += panel_bg_padding * 2; // restore initial width
2626         return end_pos;
2627 }
2628
2629 bool have_weapon_stats;
2630 bool Scoreboard_AccuracyStats_WouldDraw(float ypos)
2631 {
2632         if (MUTATOR_CALLHOOK(DrawScoreboardAccuracy))
2633                 return false;
2634         if (!autocvar_hud_panel_scoreboard_accuracy || warmup_stage || ypos > 0.91 * vid_conheight)
2635                 return false;
2636
2637         if (time < scoreboard_time + autocvar_hud_panel_scoreboard_accuracy_showdelay
2638                 && ypos > autocvar_hud_panel_scoreboard_accuracy_showdelay_minpos * vid_conheight
2639                 && !intermission)
2640         {
2641                 return false;
2642         }
2643
2644         if (!have_weapon_stats)
2645         {
2646                 FOREACH(Weapons, it != WEP_Null, {
2647                         int weapon_stats = weapon_accuracy[i - WEP_FIRST];
2648                         if (weapon_stats >= 0)
2649                         {
2650                                 have_weapon_stats = true;
2651                                 break;
2652                         }
2653                 });
2654                 if (!have_weapon_stats)
2655                         return false;
2656         }
2657
2658         return true;
2659 }
2660
2661 bool have_item_stats;
2662 bool Scoreboard_ItemStats_WouldDraw(float ypos)
2663 {
2664         Inventory g_inventory = inventoryslots[current_player];
2665         
2666         if (MUTATOR_CALLHOOK(DrawScoreboardItemStats))
2667                 return false;
2668         if (!autocvar_hud_panel_scoreboard_itemstats || !g_inventory || warmup_stage || ypos > 0.91 * vid_conheight)
2669                 return false;
2670         if (gametype == MAPINFO_TYPE_DUEL) // z411 : We already show items in our duel scoreboard.
2671                 return false;
2672
2673         if (time < scoreboard_time + autocvar_hud_panel_scoreboard_itemstats_showdelay
2674                 && ypos > autocvar_hud_panel_scoreboard_itemstats_showdelay_minpos * vid_conheight
2675                 && !intermission)
2676         {
2677                 return false;
2678         }
2679
2680         if (!have_item_stats)
2681         {
2682                 IL_EACH(default_order_items, true, {
2683                         if (!is_item_filtered(it))
2684                         {
2685                                 int q = g_inventory.inv_items[it.m_id];
2686                                 //q = 1; // debug: display all items
2687                                 if (q)
2688                                 {
2689                                         have_item_stats = true;
2690                                         break;
2691                                 }
2692                         }
2693                 });
2694                 if (!have_item_stats)
2695                         return false;
2696         }
2697
2698         return true;
2699 }
2700
2701 vector Scoreboard_Spectators_Draw(vector pos) {
2702
2703         entity pl, tm;
2704         string str = "";
2705
2706         for(pl = players.sort_next; pl; pl = pl.sort_next)
2707         {
2708                 if(pl.team == NUM_SPECTATOR)
2709                 {
2710                         for(tm = teams.sort_next; tm; tm = tm.sort_next)
2711                                 if(tm.team == NUM_SPECTATOR)
2712                                         break;
2713                         str = sprintf("%s (%d)", _("Spectators"), tm.team_size);
2714                         draw_beginBoldFont();
2715                         drawstring(pos, str, hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
2716                         draw_endBoldFont();
2717                         pos.y += 1.25 * hud_fontsize.y;
2718
2719                         pos = Scoreboard_DrawOthers(pos, '0 0 0', pl.team, NULL, pl, 0);
2720                         pos.y += 1.25 * hud_fontsize.y;
2721
2722                         break;
2723                 }
2724         }
2725         if (str != "") // if there's at least one spectator
2726                 pos.y += 0.5 * hud_fontsize.y;
2727
2728         return pos;
2729 }
2730
2731 string Scoreboard_Fraglimit_Draw(float limit, bool is_leadlimit)
2732 {
2733         string s_label = (teamplay) ? teamscores_label(ts_primary) : scores_label(ps_primary);
2734         int s_flags = (teamplay) ? teamscores_flags(ts_primary) : scores_flags(ps_primary);
2735         return sprintf((is_leadlimit ? _("^2+%s %s") : _("^5%s %s")), ScoreString(s_flags, limit, 0),
2736                 (s_label == "score") ? CTX(_("SCO^points")) :
2737                 (s_label == "fastest") ? "" : TranslateScoresLabel(s_label));
2738 }
2739
2740 void Scoreboard_Draw()
2741 {
2742         if(!autocvar__hud_configure)
2743         {
2744                 if(!hud_draw_maximized) return;
2745
2746                 // frametime checks allow to toggle the scoreboard even when the game is paused
2747                 if(scoreboard_active) {
2748                         if (scoreboard_fade_alpha == 0)
2749                                 scoreboard_time = time;
2750                         if(hud_configure_menu_open == 1)
2751                                 scoreboard_fade_alpha = 1;
2752                         float scoreboard_fadeinspeed = autocvar_hud_panel_scoreboard_fadeinspeed;
2753                         if (scoreboard_fadeinspeed && frametime)
2754                                 scoreboard_fade_alpha = min(1, scoreboard_fade_alpha + frametime * scoreboard_fadeinspeed);
2755                         else
2756                                 scoreboard_fade_alpha = 1;
2757                         if(hud_fontsize_str != autocvar_hud_fontsize)
2758                         {
2759                                 hud_fontsize = HUD_GetFontsize("hud_fontsize");
2760                                 Scoreboard_initFieldSizes();
2761                                 strcpy(hud_fontsize_str, autocvar_hud_fontsize);
2762                         }
2763                 }
2764                 else {
2765                         float scoreboard_fadeoutspeed = autocvar_hud_panel_scoreboard_fadeoutspeed;
2766                         if (scoreboard_fadeoutspeed && frametime)
2767                                 scoreboard_fade_alpha = max(0, scoreboard_fade_alpha - frametime * scoreboard_fadeoutspeed);
2768                         else
2769                                 scoreboard_fade_alpha = 0;
2770                 }
2771
2772                 if (!scoreboard_fade_alpha)
2773                 {
2774                         scoreboard_acc_fade_alpha = 0;
2775                         scoreboard_itemstats_fade_alpha = 0;
2776                         return;
2777                 }
2778         }
2779         else
2780                 scoreboard_fade_alpha = 0;
2781
2782         if (autocvar_hud_panel_scoreboard_dynamichud)
2783                 HUD_Scale_Enable();
2784         else
2785                 HUD_Scale_Disable();
2786
2787         if(scoreboard_fade_alpha <= 0)
2788                 return;
2789         panel_fade_alpha *= scoreboard_fade_alpha;
2790         HUD_Panel_LoadCvars();
2791
2792         sbt_bg_alpha = autocvar_hud_panel_scoreboard_table_bg_alpha * panel_fg_alpha;
2793         sbt_highlight = autocvar_hud_panel_scoreboard_table_highlight;
2794         sbt_highlight_alpha = autocvar_hud_panel_scoreboard_table_highlight_alpha * panel_fg_alpha;
2795         sbt_highlight_alpha_self = autocvar_hud_panel_scoreboard_table_highlight_alpha_self * panel_fg_alpha;
2796         sbt_highlight_alpha_eliminated = autocvar_hud_panel_scoreboard_table_highlight_alpha_eliminated * panel_fg_alpha;
2797         sbt_fg_alpha = autocvar_hud_panel_scoreboard_table_fg_alpha * panel_fg_alpha;
2798         sbt_fg_alpha_self = autocvar_hud_panel_scoreboard_table_fg_alpha_self * panel_fg_alpha;
2799
2800         // don't overlap with con_notify
2801         if(!autocvar__hud_configure)
2802                 panel_pos.y = max((autocvar_con_notify * autocvar_con_notifysize), panel_pos.y);
2803
2804         float excess = max(0, max_namesize - autocvar_hud_panel_scoreboard_namesize * hud_fontsize.x);
2805         float fixed_scoreboard_width = bound(vid_conwidth * autocvar_hud_panel_scoreboard_minwidth, vid_conwidth - excess, vid_conwidth * 0.93);
2806         scoreboard_left = 0.5 * (vid_conwidth - fixed_scoreboard_width);
2807         scoreboard_right = scoreboard_left + fixed_scoreboard_width;
2808         panel_pos.x = scoreboard_left;
2809         panel_size.x = fixed_scoreboard_width;
2810
2811         Scoreboard_UpdatePlayerTeams();
2812
2813         scoreboard_top = panel_pos.y;
2814         vector pos = panel_pos;
2815         entity tm;
2816         string str;
2817         vector str_pos;
2818
2819         vector sb_gameinfo_type_fontsize, sb_gameinfo_detail_fontsize;
2820
2821         // Begin of Game Info Section
2822         sb_gameinfo_type_fontsize = hud_fontsize * 2.5;
2823         sb_gameinfo_detail_fontsize = hud_fontsize * 1.3;
2824
2825         // z411 server name
2826         //drawcolorcodedstring(pos, "bienvenidoainternet.org", sb_gameinfo_type_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
2827         //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);
2828         //pos.y += sb_gameinfo_type_fontsize.y;
2829         
2830         // Game Info: Game Type
2831         if (scoreboard_ui_enabled == 2)
2832                 str = _("Team Selection");
2833         else
2834                 str = MapInfo_Type_ToText(gametype);
2835         draw_beginBoldFont();
2836         //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);
2837         drawcolorcodedstring(pos, str, sb_gameinfo_type_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
2838         draw_endBoldFont();
2839         
2840         vector tmp_old_sz = draw_getimagesize("gfx/bai_logo");
2841         float tmp_aspect = tmp_old_sz.x/tmp_old_sz.y;
2842         vector tmp_new_sz = vec2(sb_gameinfo_type_fontsize.y * tmp_aspect, sb_gameinfo_type_fontsize.y);
2843
2844         // z411 Server logo
2845         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);
2846         
2847         pos.y += sb_gameinfo_type_fontsize.y;
2848         
2849         // z411 servername
2850         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);
2851         
2852         pos.y += sb_gameinfo_detail_fontsize.y;
2853         
2854         // Game Info: Game Detail
2855         if (scoreboard_ui_enabled == 2)
2856         {
2857                 if (scoreboard_selected_team)
2858                         str = sprintf(_("^7Press ^3%s^7 to join the selected team"), translate_key("SPACE"));
2859                 else
2860                         str = sprintf(_("^7Press ^3%s^7 to auto-select a team and join"), translate_key("SPACE"));
2861                 drawcolorcodedstring(pos + '0.5 0 0' * (panel_size.x - stringwidth(str, true, sb_gameinfo_detail_fontsize)), str, sb_gameinfo_detail_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
2862
2863                 pos.y += sb_gameinfo_detail_fontsize.y + hud_fontsize.y * 0.3;
2864                 str = sprintf(_("^7Press ^3%s ^7to select a specific team"), translate_key("TAB"));
2865                 drawcolorcodedstring(pos + '0.5 0 0' * (panel_size.x - stringwidth(str, true, sb_gameinfo_detail_fontsize)), str, sb_gameinfo_detail_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
2866         }
2867         else
2868         {
2869                 float tl = STAT(TIMELIMIT);
2870                 float fl = STAT(FRAGLIMIT);
2871                 float ll = STAT(LEADLIMIT);
2872                 float ll_and_fl = STAT(LEADLIMIT_AND_FRAGLIMIT);
2873                 str = "";
2874                 if(tl > 0)
2875                         str = strcat(str, sprintf(_("^3%1.0f minutes"), tl));
2876                 if(!gametype.m_hidelimits)
2877                 {
2878                         if(fl > 0)
2879                         {
2880                                 if(tl > 0)
2881                                         str = strcat(str, "^7 / "); // delimiter
2882                                 str = strcat(str, Scoreboard_Fraglimit_Draw(fl, false));
2883                         }
2884                         if(ll > 0)
2885                         {
2886                                 if(tl > 0 || fl > 0)
2887                                 {
2888                                         // delimiter
2889                                         if (ll_and_fl && fl > 0)
2890                                                 str = strcat(str, "^7 & ");
2891                                         else
2892                                                 str = strcat(str, "^7 / ");
2893                                 }
2894                                 str = strcat(str, Scoreboard_Fraglimit_Draw(ll, true));
2895                         }
2896                 }
2897
2898                 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
2899                 // map name and player count
2900                 if (campaign)
2901                         str = "";
2902                 else
2903                         str = sprintf(_("^5%d^7/^5%d ^7players"), numplayers, srv_maxplayers ? srv_maxplayers : maxclients);
2904                 str = strcat("^7", _("Map:"), " ^2", mi_shortname, "    ", str); // reusing "Map:" translatable string
2905                 drawcolorcodedstring(pos, str, sb_gameinfo_detail_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL); // align left
2906         }
2907         // End of Game Info Section
2908
2909         pos.y += sb_gameinfo_detail_fontsize.y + hud_fontsize.y * 0.3; // space between Game Info Section and score table
2910         if(panel.current_panel_bg != "0")
2911                 pos.y += panel_bg_border;
2912
2913         // Draw the scoreboard
2914         float scale = autocvar_hud_panel_scoreboard_table_bg_scale;
2915         if(scale <= 0)
2916                 scale = 0.25;
2917         vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * scale;
2918
2919         if(teamplay)
2920         {
2921                 vector panel_bg_color_save = panel_bg_color;
2922                 vector team_score_baseoffset;
2923                 vector team_size_baseoffset;
2924                 if (autocvar_hud_panel_scoreboard_team_size_position != 1) // team size not on left
2925                 {
2926                         // put team score to the left of scoreboard (and team size to the right)
2927                         team_score_baseoffset = eY * hud_fontsize.y - eX * hud_fontsize.x * 1.5;
2928                         team_size_baseoffset = eY * hud_fontsize.y + eX * hud_fontsize.x * 0.5;
2929                         if(panel.current_panel_bg != "0")
2930                         {
2931                                 team_score_baseoffset.x -= panel_bg_border;
2932                                 team_size_baseoffset.x += panel_bg_border;
2933                         }
2934                 }
2935                 else
2936                 {
2937                         // put team score to the right of scoreboard (and team size to the left)
2938                         team_score_baseoffset = eY * hud_fontsize.y + eX * hud_fontsize.x * 1.5;
2939                         team_size_baseoffset = eY * hud_fontsize.y - eX * hud_fontsize.x * 0.5;
2940                         if(panel.current_panel_bg != "0")
2941                         {
2942                                 team_score_baseoffset.x += panel_bg_border;
2943                                 team_size_baseoffset.x -= panel_bg_border;
2944                         }
2945                 }
2946
2947                 int team_size_total = 0;
2948                 if (autocvar_hud_panel_scoreboard_team_size_position != 0) // team size not off
2949                 {
2950                         // calculate team size total (sum of all team sizes)
2951                         for(tm = teams.sort_next; tm; tm = tm.sort_next)
2952                                 if(tm.team != NUM_SPECTATOR)
2953                                         team_size_total += tm.team_size;
2954                 }
2955
2956                 for(tm = teams.sort_next; tm; tm = tm.sort_next)
2957                 {
2958                         if(tm.team == NUM_SPECTATOR)
2959                                 continue;
2960                         if(!tm.team)
2961                                 continue;
2962
2963                         vector rgb = Team_ColorRGB(tm.team);
2964                         /*draw_beginBoldFont();
2965                         str = ftos(tm.(teamscores(ts_primary)));
2966                         if (autocvar_hud_panel_scoreboard_team_size_position != 1) // team size not on left
2967                         {
2968                                 // team score on the left (default)
2969                                 str_pos = pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 3);
2970                         }
2971                         else
2972                         {
2973                                 // team score on the right
2974                                 str_pos = pos + team_score_baseoffset + eX * (panel_size.x + hud_fontsize.x * 3);
2975                         }
2976                         drawstring(str_pos, str, hud_fontsize * 3, rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
2977
2978                         // team size (if set to show on the side)
2979                         if (autocvar_hud_panel_scoreboard_team_size_position != 0) // team size not off
2980                         {
2981                                 // calculate the starting position for the whole team size info string
2982                                 str = sprintf("%d/%d", tm.team_size, team_size_total);
2983                                 if (autocvar_hud_panel_scoreboard_team_size_position == 1)
2984                                 {
2985                                         // team size on the left
2986                                         str_pos = pos + team_size_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5);
2987                                 }
2988                                 else
2989                                 {
2990                                         // team size on the right
2991                                         str_pos = pos + team_size_baseoffset + eX * (panel_size.x + hud_fontsize.x * 1.5);
2992                                 }
2993                                 str = sprintf("%d", tm.team_size);
2994                                 drawstring(str_pos, str, hud_fontsize * 1.5, rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
2995                                 str_pos += eX * stringwidth(str, true, hud_fontsize * 1.5) + eY * hud_fontsize.y * .5;
2996                                 str = sprintf("/%d", team_size_total);
2997                                 drawstring(str_pos, str, hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
2998                         }
2999
3000
3001                         // secondary score, e.g. keyhunt
3002                         if(ts_primary != ts_secondary)
3003                         {
3004                                 str = ftos(tm.(teamscores(ts_secondary)));
3005                                 if (autocvar_hud_panel_scoreboard_team_size_position != 1) // team size not on left
3006                                 {
3007                                         // left
3008                                         str_pos = pos + team_score_baseoffset - vec2(stringwidth(str, false, hud_fontsize), hud_fontsize.y * -1.5);
3009                                 }
3010                                 else
3011                                 {
3012                                         // right
3013                                         str_pos = pos + team_score_baseoffset + vec2(panel_size.x + hud_fontsize.x * 1.5, hud_fontsize.y * 1.5);
3014                                 }
3015
3016                                 drawstring(str_pos, str, hud_fontsize, rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
3017                         }
3018                         draw_endBoldFont();
3019                         */
3020                         
3021                         // z411 My team header
3022                         // Score: highlight
3023                         drawfill(pos, team_score_size, rgb * 0.5, sbt_highlight_alpha, DRAWFLAG_NORMAL);
3024                         
3025                         // Score: text
3026                         str = ftos(tm.(teamscores(ts_primary)));
3027                         str_pos = pos;
3028                         str_pos.x += (team_score_size.x / 2) - (stringwidth(str, true, team_score_fontsize) / 2);
3029                         str_pos.y += (team_score_size.y / 2) - (team_score_fontsize.y / 2);
3030                         
3031                         draw_beginBoldFont();
3032                         drawstring(str_pos, str, team_score_fontsize, rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
3033                         draw_endBoldFont();
3034                         
3035                         // Team name
3036                         str = Team_CustomName(tm.team);
3037                         str_pos = pos;
3038                         str_pos.x += team_score_size.x + team_name_fontsize.x * 0.5;
3039                         str_pos.y += (team_score_size.y / 2) - (team_name_fontsize.y / 2);
3040                         drawcolorcodedstring(str_pos, str, team_name_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
3041                         
3042                         pos.y += team_score_size.y + (hud_fontsize.y * 0.5);
3043                         
3044                         if(autocvar_hud_panel_scoreboard_bg_teams_color_team > 0)
3045                                 panel_bg_color = rgb * autocvar_hud_panel_scoreboard_bg_teams_color_team;
3046                         else if(panel_bg_color_team > 0)
3047                                 panel_bg_color = rgb * panel_bg_color_team;
3048                         else
3049                                 panel_bg_color = rgb;
3050                         pos = Scoreboard_MakeTable(pos, tm, panel_bg_color, bg_size);
3051                 }
3052                 panel_bg_color = panel_bg_color_save;
3053         }
3054         else if(gametype == MAPINFO_TYPE_DUEL)
3055         {
3056                 for(tm = teams.sort_next; tm; tm = tm.sort_next)
3057                         if(tm.team != NUM_SPECTATOR)
3058                                 break;
3059                 
3060                 // z411 make DUEL TABLE
3061                 pos = Scoreboard_MakeDuelTable(pos, tm, panel_bg_color, bg_size);
3062         }
3063         else
3064         {
3065                 for(tm = teams.sort_next; tm; tm = tm.sort_next)
3066                         if(tm.team != NUM_SPECTATOR)
3067                                 break;
3068
3069                 // display it anyway
3070                 pos = Scoreboard_MakeTable(pos, tm, panel_bg_color, bg_size);
3071         }
3072
3073         pos = Scoreboard_MedalStats_Draw(pos);
3074         
3075         // draw scoreboard spectators before accuracy and item stats
3076         if (autocvar_hud_panel_scoreboard_spectators_position == 0) {
3077                 pos = Scoreboard_Spectators_Draw(pos);
3078         }
3079
3080         // draw accuracy and item stats
3081         if (Scoreboard_AccuracyStats_WouldDraw(pos.y))
3082                 pos = Scoreboard_AccuracyStats_Draw(pos, panel_bg_color, bg_size);
3083         if (Scoreboard_ItemStats_WouldDraw(pos.y))
3084                 pos = Scoreboard_ItemStats_Draw(pos, panel_bg_color, bg_size);
3085
3086         // draw scoreboard spectators after accuracy and item stats and before rankings
3087         if (autocvar_hud_panel_scoreboard_spectators_position == 1) {
3088                 pos = Scoreboard_Spectators_Draw(pos);
3089         }
3090
3091         if(MUTATOR_CALLHOOK(ShowRankings)) {
3092                 string ranktitle = M_ARGV(0, string);
3093                 string unit = GetSpeedUnit(autocvar_hud_speed_unit);
3094                 float conversion_factor = GetSpeedUnitFactor(autocvar_hud_speed_unit);
3095                 if(race_speedaward_alltimebest)
3096                 {
3097                         string name;
3098                         float namesize = autocvar_hud_panel_scoreboard_namesize * hud_fontsize.x;
3099                         str = "";
3100                         if(race_speedaward)
3101                         {
3102                                 name = textShortenToWidth(ColorTranslateRGB(race_speedaward_holder), namesize, hud_fontsize, stringwidth_colors);
3103                                 str = sprintf(_("Speed award: %d%s ^7(%s^7)"), race_speedaward * conversion_factor, unit, name);
3104                                 str = strcat(str, " / ");
3105                         }
3106                         name = textShortenToWidth(ColorTranslateRGB(race_speedaward_alltimebest_holder), namesize, hud_fontsize, stringwidth_colors);
3107                         str = strcat(str, sprintf(_("All-time fastest: %d%s ^7(%s^7)"), race_speedaward_alltimebest * conversion_factor, unit, name));
3108                         drawcolorcodedstring(pos, str, hud_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
3109                         pos.y += 1.25 * hud_fontsize.y; // line height + line spacing
3110                 }
3111                 pos = Scoreboard_Rankings_Draw(pos, ranktitle, playerslots[player_localnum], panel_bg_color, bg_size);
3112         }
3113         else
3114                 rankings_cnt = 0;
3115
3116         // draw scoreboard spectators after rankings
3117         if (autocvar_hud_panel_scoreboard_spectators_position == 2) {
3118                 pos = Scoreboard_Spectators_Draw(pos);
3119         }
3120
3121         //pos = Scoreboard_MapStats_Draw(pos, panel_bg_color, bg_size);
3122
3123         // draw scoreboard spectators after mapstats
3124         if (autocvar_hud_panel_scoreboard_spectators_position == 3) {
3125                 pos = Scoreboard_Spectators_Draw(pos);
3126         }
3127
3128
3129         // print information about respawn status
3130         float respawn_time = STAT(RESPAWN_TIME);
3131         if(!intermission && respawn_time)
3132         {
3133                 if(respawn_time < 0)
3134                 {
3135                         // a negative number means we are awaiting respawn, time value is still the same
3136                         respawn_time *= -1; // remove mark now that we checked it
3137
3138                         if(respawn_time < time) // it happens for a few frames when server is respawning the player
3139                                 str = ""; // draw an empty string to not change suddenly scoreboard_bottom
3140                         else
3141                                 str = sprintf(_("^1Respawning in ^3%s^1..."),
3142                                         (autocvar_hud_panel_scoreboard_respawntime_decimals ?
3143                                                 count_seconds_decs(respawn_time - time, autocvar_hud_panel_scoreboard_respawntime_decimals)
3144                                                 :
3145                                                 count_seconds(ceil(respawn_time - time))
3146                                         )
3147                                 );
3148                 }
3149                 else if(time < respawn_time)
3150                 {
3151                         str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
3152                                 (autocvar_hud_panel_scoreboard_respawntime_decimals ?
3153                                         count_seconds_decs(respawn_time - time, autocvar_hud_panel_scoreboard_respawntime_decimals)
3154                                         :
3155                                         count_seconds(ceil(respawn_time - time))
3156                                 )
3157                         );
3158                 }
3159                 else if(time >= respawn_time)
3160                         str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
3161
3162                 pos.y += 1.2 * hud_fontsize.y;
3163                 drawcolorcodedstring(pos + '0.5 0 0' * (panel_size.x - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
3164         }
3165
3166         pos.y += hud_fontsize.y;
3167         if (scoreboard_fade_alpha < 1)
3168                 scoreboard_bottom = scoreboard_top + (pos.y - scoreboard_top) * scoreboard_fade_alpha;
3169         else if (pos.y != scoreboard_bottom)
3170         {
3171                 if (pos.y > scoreboard_bottom)
3172                         scoreboard_bottom = min(pos.y, scoreboard_bottom + frametime * 10 * (pos.y - scoreboard_top));
3173                 else
3174                         scoreboard_bottom = max(pos.y, scoreboard_bottom - frametime * 10 * (pos.y - scoreboard_top));
3175         }
3176
3177         if (rankings_cnt)
3178         {
3179                 if (scoreboard_fade_alpha == 1)
3180                 {
3181                         if (scoreboard_bottom > 0.95 * vid_conheight)
3182                                 rankings_rows = max(1, rankings_rows - 1);
3183                         else if (scoreboard_bottom + 1.25 * hud_fontsize.y < 0.95 * vid_conheight)
3184                                 rankings_rows = min(ceil(RANKINGS_RECEIVED_CNT / rankings_columns), rankings_rows + 1);
3185                 }
3186                 rankings_cnt = rankings_rows * rankings_columns;
3187         }
3188 }