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