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