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