]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/client/scoreboard.qc
Merge remote branch 'origin/master' into mirceakitsune/preview_images
[xonotic/xonotic-data.pk3dir.git] / qcsrc / client / scoreboard.qc
1 float scoreboard_alpha_bg;
2 float scoreboard_alpha_fg;
3 float scoreboard_highlight;
4 float scoreboard_highlight_alpha;
5 float scoreboard_highlight_alpha_self;
6 float scoreboard_alpha_name;
7 float scoreboard_alpha_name_self;
8
9 void drawstringright(vector, string, vector, vector, float, float);
10 void drawstringcenter(vector, string, vector, vector, float, float);
11
12 float SCOREBOARD_OFFSET = 50;
13
14 // wrapper to put all possible scores titles through gettext
15 string TranslateScoresLabel(string l)
16 {
17         switch(l)
18         {
19                 case "bckills": return CTX(_("SCO^bckills"));
20                 case "bctime": return CTX(_("SCO^bctime"));
21                 case "caps": return CTX(_("SCO^caps"));
22                 case "deaths": return CTX(_("SCO^deaths"));
23                 case "destroyed": return CTX(_("SCO^destroyed"));
24                 case "drops": return CTX(_("SCO^drops"));
25                 case "faults": return CTX(_("SCO^faults"));
26                 case "fckills": return CTX(_("SCO^fckills"));
27                 case "goals": return CTX(_("SCO^goals"));
28                 case "kckills": return CTX(_("SCO^kckills"));
29                 case "kdratio": return CTX(_("SCO^kdratio"));
30                 case "k/d": return CTX(_("SCO^k/d"));
31                 case "kd": return CTX(_("SCO^kd"));
32                 case "kdr": return CTX(_("SCO^kdr"));
33                 case "kills": return CTX(_("SCO^kills"));
34                 case "laps": return CTX(_("SCO^laps"));
35                 case "lives": return CTX(_("SCO^lives"));
36                 case "losses": return CTX(_("SCO^losses"));
37                 case "name": return CTX(_("SCO^name"));
38                 case "nick": return CTX(_("SCO^nick"));
39                 case "objectives": return CTX(_("SCO^objectives"));
40                 case "pickups": return CTX(_("SCO^pickups"));
41                 case "ping": return CTX(_("SCO^ping"));
42                 case "pl": return CTX(_("SCO^pl"));
43                 case "pushes": return CTX(_("SCO^pushes"));
44                 case "rank": return CTX(_("SCO^rank"));
45                 case "returns": return CTX(_("SCO^returns"));
46                 case "revivals": return CTX(_("SCO^revivals"));
47                 case "score": return CTX(_("SCO^score"));
48                 case "suicides": return CTX(_("SCO^suicides"));
49                 case "takes": return CTX(_("SCO^takes"));
50                 case "ticks": return CTX(_("SCO^ticks"));
51                 default: return l;
52         }
53 }
54
55 void MapVote_Draw();
56 void HUD_FinaleOverlay()
57 {
58         /*vector pos;
59         pos_x = (vid_conwidth - 1)/2;
60         pos_y = 16;
61         pos_z = 0;*/
62
63         //drawpic(pos, "gfx/finale", '0 0 0', '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
64
65         //drawstring(pos, "END", hud_fontsize, '1 1 1', 1, DRAWFLAG_NORMAL);
66         MapVote_Draw();
67 }
68
69 void Cmd_HUD_SetFields(float argc);
70 void HUD_InitScores()
71 {
72         float i, f;
73
74         ps_primary = ps_secondary = ts_primary = ts_secondary = -1;
75         for(i = 0; i < MAX_SCORE; ++i)
76         {
77                 f = (scores_flags[i] & SFL_SORT_PRIO_MASK);
78                 if(f == SFL_SORT_PRIO_PRIMARY)
79                         ps_primary = i;
80                 if(f == SFL_SORT_PRIO_SECONDARY)
81                         ps_secondary = i;
82         }
83         if(ps_secondary == -1)
84                 ps_secondary = ps_primary;
85
86         for(i = 0; i < MAX_TEAMSCORE; ++i)
87         {
88                 f = (teamscores_flags[i] & SFL_SORT_PRIO_MASK);
89                 if(f == SFL_SORT_PRIO_PRIMARY)
90                         ts_primary = i;
91                 if(f == SFL_SORT_PRIO_SECONDARY)
92                         ts_secondary = i;
93         }
94         if(ts_secondary == -1)
95                 ts_secondary = ts_primary;
96
97         Cmd_HUD_SetFields(0);
98 }
99
100 void HUD_UpdatePlayerPos(entity pl);
101 float SetTeam(entity pl, float Team);
102 //float lastpnum;
103 void HUD_UpdatePlayerTeams()
104 {
105         float Team;
106         entity pl, tmp;
107         float num;
108
109         num = 0;
110         for(pl = players.sort_next; pl; pl = pl.sort_next)
111         {
112                 num += 1;
113                 Team = GetPlayerColor(pl.sv_entnum);
114                 if(SetTeam(pl, Team))
115                 {
116                         tmp = pl.sort_prev;
117                         HUD_UpdatePlayerPos(pl);
118                         if(tmp)
119                                 pl = tmp;
120                         else
121                                 pl = players.sort_next;
122                 }
123         }
124         /*
125         if(num != lastpnum)
126                 print(strcat("PNUM: ", ftos(num), "\n"));
127         lastpnum = num;
128         */
129 }
130
131 float HUD_ComparePlayerScores(entity left, entity right)
132 {
133         float vl, vr;
134         vl = GetPlayerColor(left.sv_entnum);
135         vr = GetPlayerColor(right.sv_entnum);
136
137         if(!left.gotscores)
138                 vl = COLOR_SPECTATOR;
139         if(!right.gotscores)
140                 vr = COLOR_SPECTATOR;
141
142         if(vl > vr)
143                 return true;
144         if(vl < vr)
145                 return false;
146
147         if(vl == COLOR_SPECTATOR)
148         {
149                 // FIRST the one with scores (spectators), THEN the ones without (downloaders)
150                 // no other sorting
151                 if(!left.gotscores && right.gotscores)
152                         return true;
153                 return false;
154         }
155
156         vl = left.scores[ps_primary];
157         vr = right.scores[ps_primary];
158         if(scores_flags[ps_primary] & SFL_ZERO_IS_WORST)
159         {
160                 if(vl == 0 && vr != 0)
161                         return 1;
162                 if(vl != 0 && vr == 0)
163                         return 0;
164         }
165         if(vl > vr)
166                 return IS_INCREASING(scores_flags[ps_primary]);
167         if(vl < vr)
168                 return IS_DECREASING(scores_flags[ps_primary]);
169
170         vl = left.scores[ps_secondary];
171         vr = right.scores[ps_secondary];
172         if(scores_flags[ps_secondary] & SFL_ZERO_IS_WORST)
173         {
174                 if(vl == 0 && vr != 0)
175                         return 1;
176                 if(vl != 0 && vr == 0)
177                         return 0;
178         }
179         if(vl > vr)
180                 return IS_INCREASING(scores_flags[ps_secondary]);
181         if(vl < vr)
182                 return IS_DECREASING(scores_flags[ps_secondary]);
183
184         return false;
185 }
186
187 void HUD_UpdatePlayerPos(entity player)
188 {
189         for(other = player.sort_next; other && HUD_ComparePlayerScores(player, other); other = player.sort_next)
190         {
191                 SORT_SWAP(player, other);
192         }
193         for(other = player.sort_prev; other != players && HUD_ComparePlayerScores(other, player); other = player.sort_prev)
194         {
195                 SORT_SWAP(other, player);
196         }
197 }
198
199 float HUD_CompareTeamScores(entity left, entity right)
200 {
201         float vl, vr;
202
203         if(left.team == COLOR_SPECTATOR)
204                 return 1;
205         if(right.team == COLOR_SPECTATOR)
206                 return 0;
207
208         vl = left.teamscores[ts_primary];
209         vr = right.teamscores[ts_primary];
210         if(vl > vr)
211                 return IS_INCREASING(teamscores_flags[ts_primary]);
212         if(vl < vr)
213                 return IS_DECREASING(teamscores_flags[ts_primary]);
214
215         vl = left.teamscores[ts_secondary];
216         vr = right.teamscores[ts_secondary];
217         if(vl > vr)
218                 return IS_INCREASING(teamscores_flags[ts_secondary]);
219         if(vl < vr)
220                 return IS_DECREASING(teamscores_flags[ts_secondary]);
221
222         return false;
223 }
224
225 void HUD_UpdateTeamPos(entity Team)
226 {
227         for(other = Team.sort_next; other && HUD_CompareTeamScores(Team, other); other = Team.sort_next)
228         {
229                 SORT_SWAP(Team, other);
230         }
231         for(other = Team.sort_prev; other != teams && HUD_CompareTeamScores(other, Team); other = Team.sort_prev)
232         {
233                 SORT_SWAP(other, Team);
234         }
235 }
236
237 void Cmd_HUD_Help(float argc)
238 {
239         print(_("You can modify the scoreboard using the ^2scoreboard_columns_set command.\n"));
240         print(_("^3|---------------------------------------------------------------|\n"));
241         print(_("Usage:\n"));
242         print(_("^2scoreboard_columns_set default\n"));
243         print(_("^2scoreboard_columns_set ^7field1 field2 ...\n"));
244         print(_("The following field names are recognized (case insensitive):\n"));
245         print(_("You can use a ^3|^7 to start the right-aligned fields.\n\n"));
246
247         print(_("^3name^7 or ^3nick^7             Name of a player\n"));
248         print(_("^3ping^7                     Ping time\n"));
249         print(_("^3pl^7                       Packet loss\n"));
250         print(_("^3kills^7                    Number of kills\n"));
251         print(_("^3deaths^7                   Number of deaths\n"));
252         print(_("^3suicides^7                 Number of suicides\n"));
253         print(_("^3frags^7                    kills - suicides\n"));
254         print(_("^3kd^7                       The kill-death ratio\n"));
255         print(_("^3caps^7                     How often a flag (CTF) or a key (KeyHunt) was captured\n"));
256         print(_("^3pickups^7                  How often a flag (CTF) or a key (KeyHunt) or a ball (Keepaway) was picked up\n"));
257         print(_("^3fckills^7                  Number of flag carrier kills\n"));
258         print(_("^3returns^7                  Number of flag returns\n"));
259         print(_("^3drops^7                    Number of flag drops\n"));
260         print(_("^3lives^7                    Number of lives (LMS)\n"));
261         print(_("^3rank^7                     Player rank\n"));
262         print(_("^3pushes^7                   Number of players pushed into void\n"));
263         print(_("^3destroyed^7                Number of keys destroyed by pushing them into void\n"));
264         print(_("^3kckills^7                  Number of keys carrier kills\n"));
265         print(_("^3losses^7                   Number of times a key was lost\n"));
266         print(_("^3laps^7                     Number of laps finished (race/cts)\n"));
267         print(_("^3time^7                     Total time raced (race/cts)\n"));
268         print(_("^3fastest^7                  Time of fastest lap (race/cts)\n"));
269         print(_("^3ticks^7                    Number of ticks (DOM)\n"));
270         print(_("^3takes^7                    Number of domination points taken (DOM)\n"));
271         print(_("^3bckills^7                  Number of ball carrier kills\n"));
272         print(_("^3bctime^7                   Total amount of time holding the ball in Keepaway\n"));
273         print(_("^3score^7                    Total score\n\n"));
274
275         print(_("Before a field you can put a + or - sign, then a comma separated list\n"
276                 "of game types, then a slash, to make the field show up only in these\n"
277                 "or in all but these game types. You can also specify 'all' as a\n"
278                 "field to show all fields available for the current game mode.\n\n"));
279
280         print(_("The special game type names 'teams' and 'noteams' can be used to\n"
281                 "include/exclude ALL teams/noteams game modes.\n\n"));
282
283         print(_("Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4\n"));
284         print(_("will display name, ping and pl aligned to the left, and the fields\n"
285                 "right of the vertical bar aligned to the right.\n"));
286         print(_("'field3' will only be shown in CTF, and 'field4' will be shown in all\n"
287                 "other gamemodes except DM.\n"));
288 }
289
290 string HUD_DefaultColumnLayout()
291 {
292         return strcat( // fteqcc sucks
293                 "ping pl name | ",
294                 "-teams,race,lms/kills +freezetag/kills -teams,lms/deaths +freezetag/deaths -teams,lms,race,ka/suicides +freezetag/suicides -race,dm,tdm,ka,freezetag/frags ", // tdm already has this in "score"
295                 "+ctf/caps +ctf/pickups +ctf/fckills +ctf/returns ",
296                 "+lms/lives +lms/rank ",
297                 "+kh/caps +kh/pushes +kh/destroyed ",
298                 "?+race/laps ?+race/time ?+race/fastest ",
299                 "+as/objectives +nexball/faults +nexball/goals +ka/pickups +ka/bckills +ka/bctime +freezetag/revivals ",
300                 "-lms,race,nexball/score");
301 }
302
303 void Cmd_HUD_SetFields(float argc)
304 {
305         float i, j, slash;
306         string str, pattern;
307         float have_name, have_primary, have_secondary, have_separator;
308         float missing;
309
310         // TODO: re enable with gametype dependant cvars?
311         if(argc < 2) // no arguments provided
312                 argc = tokenizebyseparator(strcat("x ", autocvar_scoreboard_columns), " ");
313
314         if(argc < 2)
315                 argc = tokenizebyseparator(strcat("x ", HUD_DefaultColumnLayout()), " ");
316
317         if(argc == 2)
318         {
319                 if(argv(1) == "default")
320                         argc = tokenizebyseparator(strcat("x ", HUD_DefaultColumnLayout()), " ");
321                 else if(argv(1) == "all")
322                 {
323                         string s;
324                         s = "ping pl color name |";
325                         for(i = 0; i < MAX_SCORE; ++i)
326                         {
327                                 if(i != ps_primary)
328                                 if(i != ps_secondary)
329                                 if(scores_label[i] != "")
330                                         s = strcat(s, " ", scores_label[i]);
331                         }
332                         if(ps_secondary != ps_primary)
333                                 s = strcat(s, " ", scores_label[ps_secondary]);
334                         s = strcat(s, " ", scores_label[ps_primary]);
335                         argc = tokenizebyseparator(strcat("x ", s), " ");
336                 }
337         }
338
339
340         hud_num_fields = 0;
341
342         hud_fontsize = HUD_GetFontsize("hud_fontsize"); 
343
344         for(i = 0; i < argc - 1; ++i)
345         {
346                 float nocomplain;
347                 str = argv(i+1);
348
349                 nocomplain = FALSE;
350                 if(substring(str, 0, 1) == "?")
351                 {
352                         nocomplain = TRUE;
353                         str = substring(str, 1, strlen(str) - 1);
354                 }
355
356                 slash = strstrofs(str, "/", 0);
357                 if(slash >= 0)
358                 {
359                         pattern = substring(str, 0, slash);
360                         str = substring(str, slash + 1, strlen(str) - (slash + 1));
361
362                         if not(isGametypeInFilter(gametype, teamplay, FALSE, pattern))
363                                 continue;
364                 }
365
366                 strunzone(hud_title[hud_num_fields]);
367                 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(str));
368                 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], FALSE, hud_fontsize);
369                 str = strtolower(str);
370
371                 if(str == "ping") {
372                         hud_field[hud_num_fields] = SP_PING;
373                 } else if(str == "pl") {
374                         hud_field[hud_num_fields] = SP_PL;
375                 } else if(str == "kd" || str == "kdr" || str == "kdratio" || str == "k/d") {
376                         hud_field[hud_num_fields] = SP_KDRATIO;
377                 } else if(str == "name" || str == "nick") {
378                         hud_field[hud_num_fields] = SP_NAME;
379                         have_name = 1;
380                 } else if(str == "|") {
381                         hud_field[hud_num_fields] = SP_SEPARATOR;
382                         have_separator = 1;
383                 } else {
384                         for(j = 0; j < MAX_SCORE; ++j)
385                                 if(str == strtolower(scores_label[j]))
386                                         goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
387 :notfound
388                         if(str == "frags")
389                         {
390                                 j = SP_FRAGS;
391                         }
392                         else
393                         {
394                                 if not(nocomplain)
395                                         print(sprintf("^1Error:^7 Unknown score field: '%s'\n", str));
396                                 continue;
397                         }
398 :found
399                         hud_field[hud_num_fields] = j;
400                         if(j == ps_primary)
401                                 have_primary = 1;
402                         if(j == ps_secondary)
403                                 have_secondary = 1;
404                 }
405                 ++hud_num_fields;
406                 if(hud_num_fields >= MAX_HUD_FIELDS)
407                         break;
408         }
409
410         if(scores_flags[ps_primary] & SFL_ALLOW_HIDE)
411                 have_primary = 1;
412         if(scores_flags[ps_secondary] & SFL_ALLOW_HIDE)
413                 have_secondary = 1;
414         if(ps_primary == ps_secondary)
415                 have_secondary = 1;
416         missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
417
418         if(hud_num_fields+missing < MAX_HUD_FIELDS)
419         {
420                 if(!have_name)
421                 {
422                         strunzone(hud_title[hud_num_fields]);
423                         for(i = hud_num_fields; i > 0; --i)
424                         {
425                                 hud_title[i] = hud_title[i-1];
426                                 hud_size[i] = hud_size[i-1];
427                                 hud_field[i] = hud_field[i-1];
428                         }
429                         hud_title[0] = strzone(TranslateScoresLabel("name"));
430                         hud_field[0] = SP_NAME;
431                         ++hud_num_fields;
432                         print(sprintf(_("fixed missing field '%s'\n"), "name"));
433
434                         if(!have_separator)
435                         {
436                                 strunzone(hud_title[hud_num_fields]);
437                                 for(i = hud_num_fields; i > 1; --i)
438                                 {
439                                         hud_title[i] = hud_title[i-1];
440                                         hud_size[i] = hud_size[i-1];
441                                         hud_field[i] = hud_field[i-1];
442                                 }
443                                 hud_title[1] = strzone("|");
444                                 hud_field[1] = SP_SEPARATOR;
445                                 hud_size[1] = stringwidth("|", FALSE, hud_fontsize);
446                                 ++hud_num_fields;
447                                 print(sprintf(_("fixed missing field '%s'\n"), "|"));
448                         }
449                 }
450                 else if(!have_separator)
451                 {
452                         strunzone(hud_title[hud_num_fields]);
453                         hud_title[hud_num_fields] = strzone("|");
454                         hud_size[hud_num_fields] = stringwidth("|", FALSE, hud_fontsize);
455                         hud_field[hud_num_fields] = SP_SEPARATOR;
456                         ++hud_num_fields;
457                         print(sprintf(_("fixed missing field '%s'\n"), "|"));
458                 }
459                 if(!have_secondary)
460                 {
461                         strunzone(hud_title[hud_num_fields]);
462                         hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_secondary]));
463                         hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], FALSE, hud_fontsize);
464                         hud_field[hud_num_fields] = ps_secondary;
465                         ++hud_num_fields;
466                         print(sprintf(_("fixed missing field '%s'\n"), scores_label[ps_secondary]));
467                 }
468                 if(!have_primary)
469                 {
470                         strunzone(hud_title[hud_num_fields]);
471                         hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_primary]));
472                         hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], FALSE, hud_fontsize);
473                         hud_field[hud_num_fields] = ps_primary;
474                         ++hud_num_fields;
475                         print(sprintf(_("fixed missing field '%s'\n"), scores_label[ps_primary]));
476                 }
477         }
478
479         hud_field[hud_num_fields] = SP_END;
480 }
481
482 // MOVEUP::
483 vector hud_field_rgb;
484 string hud_field_icon0;
485 string hud_field_icon1;
486 string hud_field_icon2;
487 vector hud_field_icon0_rgb;
488 vector hud_field_icon1_rgb;
489 vector hud_field_icon2_rgb;
490 float hud_field_icon0_alpha;
491 float hud_field_icon1_alpha;
492 float hud_field_icon2_alpha;
493 string HUD_GetField(entity pl, float field)
494 {
495         float tmp, num, denom, f;
496         string str;
497         hud_field_rgb = '1 1 1';
498         hud_field_icon0 = "";
499         hud_field_icon1 = "";
500         hud_field_icon2 = "";
501         hud_field_icon0_rgb = '1 1 1';
502         hud_field_icon1_rgb = '1 1 1';
503         hud_field_icon2_rgb = '1 1 1';
504         hud_field_icon0_alpha = 1;
505         hud_field_icon1_alpha = 1;
506         hud_field_icon2_alpha = 1;
507         switch(field)
508         {
509                 case SP_PING:
510                         if not(pl.gotscores)
511                                 return "\xEE\x82\x8D\xEE\x82\x8D\xEE\x82\x8D"; // >>> sign
512                         //str = getplayerkeyvalue(pl.sv_entnum, "ping");
513                         f = pl.ping;
514                         if(f == 0)
515                                 return _("N/A");
516                         tmp = max(0, min(220, f-80)) / 220;
517                         hud_field_rgb = '1 1 1' - '0 1 1'*tmp;
518                         return ftos(f);
519
520                 case SP_PL:
521                         if not(pl.gotscores)
522                                 return _("N/A");
523                         f = pl.ping_packetloss;
524                         tmp = pl.ping_movementloss;
525                         if(f == 0 && tmp == 0)
526                                 return "";
527                         str = ftos(ceil(f * 100));
528                         if(tmp != 0)
529                                 str = strcat(str, "~", ftos(ceil(tmp * 100)));
530                         tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
531                         hud_field_rgb = '1 0.5 0.5' - '0 0.5 0.5'*tmp;
532                         return str;
533
534                 case SP_NAME:
535                         if(ready_waiting && pl.ready)
536                         {
537                                 hud_field_icon0 = "gfx/scoreboard/player_ready";
538                         }
539                         else if(!teamplay)
540                         {
541                                 f = stof(getplayerkeyvalue(pl.sv_entnum, "colors"));
542                                 {
543                                         hud_field_icon0 = "gfx/scoreboard/playercolor_base";
544                                         hud_field_icon1 = "gfx/scoreboard/playercolor_shirt";
545                                         hud_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
546                                         hud_field_icon2 = "gfx/scoreboard/playercolor_pants";
547                                         hud_field_icon2_rgb = colormapPaletteColor(mod(f, 16), 1);
548                                 }
549                         }
550                         return GetPlayerName(pl.sv_entnum);
551
552                 case SP_FRAGS:
553                         f = pl.(scores[SP_KILLS]);
554                         f -= pl.(scores[SP_SUICIDES]);
555                         return ftos(f);
556
557                 case SP_KDRATIO:
558                         num = pl.(scores[SP_KILLS]);
559                         denom = pl.(scores[SP_DEATHS]);
560
561                         if(denom == 0) {
562                                 hud_field_rgb = '0 1 0';
563                                 str = sprintf("%d", num);
564                         } else if(num <= 0) {
565                                 hud_field_rgb = '1 0 0';
566                                 str = sprintf("%.1f", num/denom);
567                         } else
568                                 str = sprintf("%.1f", num/denom);
569                         return str;
570
571                 default:
572                         tmp = pl.(scores[field]);
573                         f = scores_flags[field];
574                         if(field == ps_primary)
575                                 hud_field_rgb = '1 1 0';
576                         else if(field == ps_secondary)
577                                 hud_field_rgb = '0 1 1';
578                         else
579                                 hud_field_rgb = '1 1 1';
580                         return ScoreString(f, tmp);
581         }
582         //return "error";
583 }
584
585 float xmin, xmax, ymin, ymax, sbwidth;
586 float hud_fixscoreboardcolumnwidth_len;
587 float hud_fixscoreboardcolumnwidth_iconlen;
588 float hud_fixscoreboardcolumnwidth_marginlen;
589
590 string HUD_FixScoreboardColumnWidth(float i, string str)
591 {
592         float field, f;
593         vector sz;
594         field = hud_field[i];
595
596         hud_fixscoreboardcolumnwidth_iconlen = 0;
597
598         if(hud_field_icon0 != "")
599         {
600                 sz = draw_getimagesize(hud_field_icon0);
601                 f = sz_x / sz_y;
602                 if(hud_fixscoreboardcolumnwidth_iconlen < f)
603                         hud_fixscoreboardcolumnwidth_iconlen = f;
604         }
605
606         if(hud_field_icon1 != "")
607         {
608                 sz = draw_getimagesize(hud_field_icon1);
609                 f = sz_x / sz_y;
610                 if(hud_fixscoreboardcolumnwidth_iconlen < f)
611                         hud_fixscoreboardcolumnwidth_iconlen = f;
612         }
613
614         if(hud_field_icon2 != "")
615         {
616                 sz = draw_getimagesize(hud_field_icon2);
617                 f = sz_x / sz_y;
618                 if(hud_fixscoreboardcolumnwidth_iconlen < f)
619                         hud_fixscoreboardcolumnwidth_iconlen = f;
620         }
621
622         hud_fixscoreboardcolumnwidth_iconlen *= hud_fontsize_y / hud_fontsize_x; // fix icon aspect
623
624         if(hud_fixscoreboardcolumnwidth_iconlen != 0)
625                 hud_fixscoreboardcolumnwidth_marginlen = stringwidth(" ", FALSE, hud_fontsize);
626         else
627                 hud_fixscoreboardcolumnwidth_marginlen = 0;
628
629         if(field == SP_NAME) // name gets all remaining space
630         {
631                 float namesize, j;
632                 namesize = sbwidth;// / hud_fontsize_x;
633                 for(j = 0; j < hud_num_fields; ++j)
634                         if(j != i)
635                                 if (hud_field[i] != SP_SEPARATOR)
636                                         namesize -= hud_size[j] + hud_fontsize_x;
637                 namesize += hud_fontsize_x;
638                 hud_size[i] = namesize;
639
640                 if (hud_fixscoreboardcolumnwidth_iconlen != 0)
641                         namesize -= hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
642                 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
643                 hud_fixscoreboardcolumnwidth_len = stringwidth(str, TRUE, hud_fontsize);
644         }
645         else
646                 hud_fixscoreboardcolumnwidth_len = stringwidth(str, FALSE, hud_fontsize);
647
648         f = hud_fixscoreboardcolumnwidth_len + hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
649         if(hud_size[i] < f)
650                 hud_size[i] = f;
651
652         return str;
653 }
654
655 void HUD_PrintScoreboardItem(vector pos, entity pl, float is_self, float pl_number)
656 {
657         vector tmp, rgb;
658         rgb = GetTeamRGB(pl.team);
659         string str;
660         float i, field;
661         float is_spec;
662         is_spec = (GetPlayerColor(pl.sv_entnum) == COLOR_SPECTATOR);
663
664         if((rgb == '1 1 1') && (!is_spec)) {
665                 rgb_x = autocvar_scoreboard_color_bg_r + 0.5;
666                 rgb_y = autocvar_scoreboard_color_bg_g + 0.5;
667                 rgb_z = autocvar_scoreboard_color_bg_b + 0.5; }
668
669         // Layout:
670         tmp_x = sbwidth;
671         tmp_y = hud_fontsize_y * 1.25;
672         tmp_z = 0;
673
674         // alternated rows highlighting
675         if(is_self)
676                 drawfill(pos - '1 1 0', tmp + '2 0 0', rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
677         else if((scoreboard_highlight) && (!mod(pl_number,2)))
678                 drawfill(pos - '1 1 0', tmp + '2 0 0', rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
679
680         tmp_y = 0;
681
682         for(i = 0; i < hud_num_fields; ++i)
683         {
684                 field = hud_field[i];
685                 if(field == SP_SEPARATOR)
686                         break;
687
688                 if(is_spec && field != SP_NAME && field != SP_PING) {
689                         pos_x += hud_size[i] + hud_fontsize_x;
690                         continue;
691                 }
692                 str = HUD_GetField(pl, field);
693                 str = HUD_FixScoreboardColumnWidth(i, str);
694
695                 pos_x += hud_size[i] + hud_fontsize_x;
696
697                 if(field == SP_NAME) {
698                         tmp_x = hud_size[i] - hud_fontsize_x*hud_fixscoreboardcolumnwidth_iconlen - hud_fixscoreboardcolumnwidth_marginlen + hud_fontsize_x;
699                         if (is_self)
700                                 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
701                         else
702                                 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
703                 } else {
704                         tmp_x = hud_fixscoreboardcolumnwidth_len + hud_fontsize_x;
705                         if (is_self)
706                                 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
707                         else
708                                 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
709                 }
710
711                 tmp_x = hud_size[i] + hud_fontsize_x;
712                 if(hud_field_icon0 != "")
713                         if (is_self)
714                                 drawpic(pos - tmp, hud_field_icon0, '0 1 0' * hud_fontsize_y + '1 0 0' * hud_fontsize_x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon0_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
715                         else
716                                 drawpic(pos - tmp, hud_field_icon0, '0 1 0' * hud_fontsize_y + '1 0 0' * hud_fontsize_x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon0_alpha * scoreboard_alpha_name, DRAWFLAG_NORMAL);
717                 if(hud_field_icon1 != "")
718                         if (is_self)
719                                 drawpic(pos - tmp, hud_field_icon1, '0 1 0' * hud_fontsize_y + '1 0 0' * hud_fontsize_x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon1_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
720                         else
721                                 drawpic(pos - tmp, hud_field_icon1, '0 1 0' * hud_fontsize_y + '1 0 0' * hud_fontsize_x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon1_alpha * scoreboard_alpha_name, DRAWFLAG_NORMAL);
722                 if(hud_field_icon2 != "")
723                         if (is_self)
724                                 drawpic(pos - tmp, hud_field_icon2, '0 1 0' * hud_fontsize_y + '1 0 0' * hud_fontsize_x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon2_rgb, hud_field_icon2_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
725                         else
726                                 drawpic(pos - tmp, hud_field_icon2, '0 1 0' * hud_fontsize_y + '1 0 0' * hud_fontsize_x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon2_rgb, hud_field_icon2_alpha * scoreboard_alpha_name, DRAWFLAG_NORMAL);
727         }
728
729         if(hud_field[i] == SP_SEPARATOR)
730         {
731                 pos_x = xmax;
732                 for(i = hud_num_fields-1; i > 0; --i)
733                 {
734                         field = hud_field[i];
735                         if(field == SP_SEPARATOR)
736                                 break;
737
738                         if(is_spec && field != SP_NAME && field != SP_PING) {
739                                 pos_x -= hud_size[i] + hud_fontsize_x;
740                                 continue;
741                         }
742
743                         str = HUD_GetField(pl, field);
744                         str = HUD_FixScoreboardColumnWidth(i, str);
745
746                         if(field == SP_NAME) {
747                                 tmp_x = hud_fixscoreboardcolumnwidth_len; // left or right aligned? let's put it right...
748                                 if(is_self)
749                                         drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
750                                 else
751                                         drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
752                         } else {
753                                 tmp_x = hud_fixscoreboardcolumnwidth_len;
754                                 if(is_self)
755                                         drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
756                                 else
757                                         drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
758                         }
759
760                         tmp_x = hud_size[i];
761                         if(hud_field_icon0 != "")
762                                 if (is_self)
763                                         drawpic(pos - tmp, hud_field_icon0, '0 1 0' * hud_fontsize_y + '1 0 0' * hud_fontsize_x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon0_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
764                                 else
765                                         drawpic(pos - tmp, hud_field_icon0, '0 1 0' * hud_fontsize_y + '1 0 0' * hud_fontsize_x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon0_alpha * scoreboard_alpha_name, DRAWFLAG_NORMAL);
766                         if(hud_field_icon1 != "")
767                                 if (is_self)
768                                         drawpic(pos - tmp, hud_field_icon1, '0 1 0' * hud_fontsize_y + '1 0 0' * hud_fontsize_x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon1_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
769                                 else
770                                         drawpic(pos - tmp, hud_field_icon1, '0 1 0' * hud_fontsize_y + '1 0 0' * hud_fontsize_x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon1_alpha * scoreboard_alpha_name, DRAWFLAG_NORMAL);
771                         if(hud_field_icon2 != "")
772                                 if (is_self)
773                                         drawpic(pos - tmp, hud_field_icon2, '0 1 0' * hud_fontsize_y + '1 0 0' * hud_fontsize_x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon2_rgb, hud_field_icon2_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
774                                 else
775                                         drawpic(pos - tmp, hud_field_icon2, '0 1 0' * hud_fontsize_y + '1 0 0' * hud_fontsize_x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon2_rgb, hud_field_icon2_alpha * scoreboard_alpha_name, DRAWFLAG_NORMAL);
776                         pos_x -= hud_size[i] + hud_fontsize_x;
777                 }
778         }
779 }
780
781 /*
782  * HUD_Scoreboard_MakeTable
783  *
784  * Makes a table for a team (for all playing players in DM) and fills it
785  */
786
787 vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
788 {
789         float body_table_height, i;
790         vector tmp, column_dim;
791         entity pl;
792
793         body_table_height = 1.25 * hud_fontsize_y * max(1, tm.team_size); // no player? show 1 empty line
794
795         pos_y += autocvar_scoreboard_border_thickness;
796         pos -= '1 1 0';
797
798         tmp_x = sbwidth + 2;
799         tmp_y = 1.25 * hud_fontsize_y;
800
801         // rounded header
802         if (teamplay)
803                 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, (rgb * autocvar_scoreboard_color_bg_team) + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
804         else
805                 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
806
807         // table border
808         tmp_y += autocvar_scoreboard_border_thickness;
809         tmp_y += body_table_height;
810         drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL); // more transparency for the scoreboard
811
812         // separator header/table
813         pos_y += 1.25 * hud_fontsize_y;
814         tmp_y = autocvar_scoreboard_border_thickness;
815         drawfill(pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
816
817         pos_y += autocvar_scoreboard_border_thickness;
818
819         // table background
820         tmp_y = body_table_height;
821         if (teamplay)
822                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
823         else
824                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
825
826         // anyway, apply some color
827         //drawfill(pos, tmp + '2 0 0', rgb, 0.1, DRAWFLAG_NORMAL);
828
829         // go back to the top to make alternated columns highlighting and to print the strings
830         pos_y -= 1.25 * hud_fontsize_y;
831         pos_y -= autocvar_scoreboard_border_thickness;
832
833         pos += '1 1 0';
834
835         if (scoreboard_highlight)
836         {
837                 column_dim_y = 1.25 * hud_fontsize_y; // header
838                 column_dim_y += autocvar_scoreboard_border_thickness;
839                 column_dim_y += body_table_height;
840         }
841
842         // print the strings of the columns headers and draw the columns
843         for(i = 0; i < hud_num_fields; ++i)
844         {
845                 if(hud_field[i] == SP_SEPARATOR)
846                         break;
847                 column_dim_x = hud_size[i] + hud_fontsize_x;
848                 if (scoreboard_highlight)
849                 {
850                         if (mod(i,2))
851                                 drawfill(pos - '0 1 0' - hud_fontsize_x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
852                 }
853                 drawstring(pos, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
854                 pos_x += column_dim_x;
855         }
856         if(hud_field[i] == SP_SEPARATOR)
857         {
858                 pos_x = xmax;
859                 tmp_y = 0;
860                 for(i = hud_num_fields-1; i > 0; --i)
861                 {
862                         if(hud_field[i] == SP_SEPARATOR)
863                                 break;
864
865                         pos_x -= hud_size[i];
866
867                         if (scoreboard_highlight)
868                         {
869                                 if (!mod(i,2))
870                                 {
871                                         if (i == hud_num_fields-1)
872                                                 column_dim_x = hud_size[i] + hud_fontsize_x / 2 + 1;
873                                         else
874                                                 column_dim_x = hud_size[i] + hud_fontsize_x;
875                                         drawfill(pos - '0 1 0' - hud_fontsize_x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
876                                 }
877                         }
878
879                         tmp_x = stringwidth(hud_title[i], FALSE, hud_fontsize);
880                         tmp_x = (hud_size[i] - tmp_x);
881                         drawstring(pos + tmp, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
882                         pos_x -= hud_fontsize_x;
883                 }
884         }
885
886         pos_x = xmin;
887         pos_y += 1.25 * hud_fontsize_y; // skip the header
888         pos_y += autocvar_scoreboard_border_thickness;
889
890         // fill the table and draw the rows
891         i = 0;
892         if (teamplay)
893                 for(pl = players.sort_next; pl; pl = pl.sort_next)
894                 {
895                         if(pl.team != tm.team)
896                                 continue;
897                         HUD_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localnum), i);
898                         pos_y += 1.25 * hud_fontsize_y;
899                         ++i;
900                 }
901         else
902                 for(pl = players.sort_next; pl; pl = pl.sort_next)
903                 {
904                         if(pl.team == COLOR_SPECTATOR)
905                                 continue;
906                         HUD_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localnum), i);
907                         pos_y += 1.25 * hud_fontsize_y;
908                         ++i;
909                 }
910
911         if (i == 0)
912                 pos_y += 1.25 * hud_fontsize_y; // move to the end of the table
913         pos_y += 1.25 * hud_fontsize_y; // move empty row (out of the table)
914
915         return pos;
916 }
917
918 float HUD_WouldDrawScoreboard() {
919         if (autocvar__hud_configure)
920                 return 0;
921         else if (scoreboard_showscores)
922                 return 1;
923         else if (intermission == 1)
924                 return 1;
925         else if (intermission == 2)
926                 return 0;
927         else if (spectatee_status != -1 && getstati(STAT_HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS)
928                 return 1;
929         else if (scoreboard_showscores_force)
930                 return 1;
931         return 0;
932 }
933
934 float g_minstagib;
935 float average_accuracy;
936 vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
937 {
938         float i;
939         float weapon_cnt = WEP_COUNT - 3; // either minstanex/nex are hidden, no port-o-launch, no tuba
940         float rows;
941         if(autocvar_scoreboard_accuracy_doublerows)
942                 rows = 2;
943         else
944                 rows = 1;
945         float height = 40;
946         float fontsize = height * 1/3;
947         float weapon_height = height * 2/3;
948         float weapon_width = sbwidth / weapon_cnt;
949
950         drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
951         pos_y += 1.25 * hud_fontsize_y + autocvar_scoreboard_border_thickness;
952         vector tmp;
953         tmp_x = sbwidth;
954         tmp_y = height * rows;
955
956         if (teamplay)
957                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
958         else
959                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
960         drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
961
962         // column highlighting
963         for(i = 0; i < weapon_cnt/rows; ++i)
964         {
965                 if(!mod(i, 2))
966                         drawfill(pos + '1 0 0' * weapon_width * rows * i, '0 1 0' * height * rows + '1 0 0' * weapon_width * rows, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
967         }
968
969         // row highlighting
970         for(i = 0; i < rows; ++i)
971         {
972                 drawfill(pos + '0 1 0' * weapon_height + '0 1 0' * height * i, '1 0 0' * sbwidth + '0 1 0' * fontsize, '1 1 1', scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
973         }
974
975         average_accuracy = 0;
976         float weapons_with_stats;
977         weapons_with_stats = 0;
978         if(rows == 2)
979                 pos_x += weapon_width / 2;
980
981         if(switchweapon == WEP_MINSTANEX)
982                 g_minstagib = 1; // TODO: real detection for minstagib?
983
984         float weapon_stats;
985
986         if (!acc_levels)
987                 rgb = '1 1 1';
988         else if (acc_col_x[0] == -1)
989                 for (i = 0; i < acc_levels; ++i)
990                         acc_col[i] = stov(cvar_string(strcat("accuracy_color", ftos(i))));
991
992         for(i = WEP_FIRST; i <= WEP_LAST; ++i)
993         {
994                 self = get_weaponinfo(i);
995                 if not(self.weapons)
996                         continue;
997                 if ((i == WEP_NEX && g_minstagib) || i == WEP_PORTO || (i == WEP_MINSTANEX && !g_minstagib) || i == WEP_TUBA) // skip port-o-launch, nex || minstanex and tuba
998                         continue;
999                 weapon_stats = weapon_accuracy[i-WEP_FIRST];
1000
1001                 float weapon_alpha;
1002                 if(weapon_stats >= 0)
1003                         weapon_alpha = scoreboard_alpha_fg;
1004                 else
1005                         weapon_alpha = 0.2 * scoreboard_alpha_fg;
1006
1007                 // weapon icon
1008                 drawpic_aspect_skin(pos, strcat("weapon", self.netname), '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
1009                 // the accuracy
1010                 if(weapon_stats >= 0) {
1011                         weapons_with_stats += 1;
1012                         average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1013
1014                         string s;
1015                         s = sprintf(_("%d%%"), weapon_stats*100);
1016
1017                         float padding;
1018                         padding = (weapon_width - stringwidth(s, FALSE, '1 0 0' * fontsize)) / 2; // center the accuracy value
1019
1020                         if (acc_levels)
1021                         {
1022                                 // find the max level lower than weapon_stats
1023                                 float j;
1024                                 j = acc_levels-1;
1025                                 while ( j && weapon_stats < acc_lev[j] )
1026                                         --j;
1027
1028                                 // inject color j+1 in color j, how much depending on how much weapon_stats is higher than level j
1029                                 float factor;
1030                                 factor = (weapon_stats - acc_lev[j]) / (acc_lev[j+1] - acc_lev[j]);
1031                                 rgb = acc_col[j];
1032                                 rgb = rgb + factor * (acc_col[j+1] - rgb);
1033                         }
1034
1035                         drawstring(pos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1036                 }
1037                 pos_x += weapon_width * rows;
1038                 if(rows == 2 && i == 6) {
1039                         pos_x -= sbwidth;
1040                         pos_y += height;
1041                 }
1042         }
1043
1044         if(weapons_with_stats)
1045                 average_accuracy = floor(average_accuracy / weapons_with_stats);
1046
1047         if(rows == 2)
1048                 pos_x -= weapon_width / 2;
1049         pos_x -= sbwidth;
1050         pos_y += height;
1051
1052         pos_y +=  1.25 * hud_fontsize_y;
1053         return pos;
1054 }
1055
1056 vector HUD_DrawKeyValue(vector pos, string key, string value) {
1057         float px = pos_x;
1058         pos_x += hud_fontsize_x * 0.25;
1059         drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1060         pos_x = xmax - stringwidth(value, FALSE, hud_fontsize) - hud_fontsize_x * 0.25;
1061         drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1062         pos_x = px;
1063         pos_y+= hud_fontsize_y;
1064         
1065         return pos;
1066 }
1067
1068 vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
1069         float stat_secrets_found, stat_secrets_total;
1070         float rows;
1071         string val;
1072
1073         // get secrets stats
1074         stat_secrets_found = getstatf(STAT_SECRETS_FOUND);
1075         stat_secrets_total = getstatf(STAT_SECRETS_TOTAL);
1076
1077         // get number of rows
1078         rows = (stat_secrets_total ? 1 : 0);
1079
1080         // if no rows, return
1081         if not(rows)
1082                 return pos;
1083
1084         //  draw table header
1085         drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1086         pos_y += 1.25 * hud_fontsize_y + autocvar_scoreboard_border_thickness;
1087         
1088         // draw table   
1089         vector tmp;
1090         tmp_x = sbwidth;
1091         tmp_y = hud_fontsize_y * rows;
1092
1093         if (teamplay)
1094                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1095         else
1096                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1097         drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1098
1099         // draw secrets
1100         val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1101         pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
1102         
1103         // update position
1104         pos_y += 1.25 * hud_fontsize_y;
1105         return pos;
1106 }
1107
1108
1109 vector HUD_DrawScoreboardRankings(vector pos, entity pl,  vector rgb, vector bg_size)
1110 {
1111         float i;
1112         RANKINGS_RECEIVED_CNT = 0;
1113         for (i=RANKINGS_CNT-1; i>=0; --i)
1114                 if (grecordtime[i])
1115                         ++RANKINGS_RECEIVED_CNT;
1116
1117         if (RANKINGS_RECEIVED_CNT == 0)
1118                 return pos;
1119
1120         float is_spec;
1121         is_spec = (GetPlayerColor(pl.sv_entnum) == COLOR_SPECTATOR);
1122         vector hl_rgb;
1123         hl_rgb_x = autocvar_scoreboard_color_bg_r + 0.5;
1124         hl_rgb_y = autocvar_scoreboard_color_bg_g + 0.5;
1125         hl_rgb_z = autocvar_scoreboard_color_bg_b + 0.5;
1126
1127         pos_y += hud_fontsize_y;
1128         drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1129         pos_y += hud_fontsize_y + autocvar_scoreboard_border_thickness;
1130         vector tmp;
1131         tmp_x = sbwidth;
1132         tmp_y = 1.25 * hud_fontsize_y * RANKINGS_RECEIVED_CNT;
1133
1134         if (teamplay)
1135                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1136         else
1137                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1138         drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1139
1140         // row highlighting
1141         for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1142         {
1143                 string n, p;
1144                 float t;
1145                 t = grecordtime[i];
1146                 if (t == 0)
1147                         continue;
1148                 n = grecordholder[i];
1149                 p = race_PlaceName(i+1);
1150                 if(grecordholder[i] == GetPlayerName(player_localnum))
1151                         drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize_y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1152                 else if(!mod(i, 2) && scoreboard_highlight)
1153                         drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize_y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1154                 drawstring(pos, p, '1 1 0' * hud_fontsize_y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1155                 drawstring(pos + '3 0 0' * hud_fontsize_y, TIME_ENCODED_TOSTRING(t), '1 1 0' * hud_fontsize_y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1156                 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize_y, n, '1 1 0' * hud_fontsize_y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1157                 pos_y += 1.25 * hud_fontsize_y;
1158         }
1159         pos_y += autocvar_scoreboard_border_thickness;
1160
1161         return pos;
1162 }
1163
1164 float hud_woulddrawscoreboard_prev;
1165 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1166 void HUD_DrawScoreboard()
1167 {
1168         float hud_woulddrawscoreboard;
1169         hud_woulddrawscoreboard = scoreboard_active;
1170         if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1171                 hud_woulddrawscoreboard_change = time;
1172                 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1173         }
1174
1175         if(hud_woulddrawscoreboard) {
1176                 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1177                 if (scoreboard_fadeinspeed)
1178                         scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1179                 else
1180                         scoreboard_fade_alpha = 1;
1181         }
1182         else {
1183                 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1184                 if (scoreboard_fadeoutspeed)
1185                         scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1186                 else
1187                         scoreboard_fade_alpha = 0;
1188         }
1189
1190         if not(scoreboard_fade_alpha)
1191                 return;
1192
1193         HUD_UpdatePlayerTeams();
1194
1195         scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1196         scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1197         scoreboard_highlight = autocvar_scoreboard_highlight;
1198         scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1199         scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1200         scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1201         scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1202
1203         vector rgb, pos, tmp;
1204         entity pl, tm;
1205         string str;
1206
1207         xmin = (autocvar_scoreboard_offset_left * vid_conwidth);
1208         ymin = max((autocvar_con_notify * autocvar_con_notifysize), (autocvar_scoreboard_offset_vertical * vid_conwidth));
1209
1210         xmax = ((1 - autocvar_scoreboard_offset_right) * vid_conwidth);
1211         ymax = (vid_conheight - ymin);
1212
1213         sbwidth = xmax - xmin;
1214
1215         // Initializes position
1216         pos_x = xmin;
1217         pos_y = ymin;
1218         pos_z = 0;
1219
1220         // Heading
1221         vector sb_heading_fontsize;
1222         sb_heading_fontsize = hud_fontsize * 2;
1223         drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1224
1225         pos_y += sb_heading_fontsize_y + hud_fontsize_y * 0.25;
1226
1227         // Draw the scoreboard
1228         vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * autocvar_scoreboard_bg_scale;
1229
1230         if(teamplay)
1231         {
1232                 vector team_score_baseoffset;
1233                 team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize_y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize_x * 0.25);
1234                 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1235                 {
1236                         if(tm.team == COLOR_SPECTATOR)
1237                                 continue;
1238
1239                         rgb = GetTeamRGB(tm.team);
1240                         str = ftos(tm.(teamscores[ts_primary]));
1241                         drawstring(pos + team_score_baseoffset - eX * stringwidth(str, FALSE, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1242
1243                         if(ts_primary != ts_secondary)
1244                         {
1245                                 str = ftos(tm.(teamscores[ts_secondary]));
1246                                 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, FALSE, hud_fontsize) + eY * hud_fontsize_y * 1.5, str, hud_fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1247                         }
1248                         pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1249                 }
1250         }
1251         else
1252         {
1253                 rgb_x = autocvar_scoreboard_color_bg_r;
1254                 rgb_y = autocvar_scoreboard_color_bg_g;
1255                 rgb_z = autocvar_scoreboard_color_bg_b;
1256
1257                 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1258                 {
1259                         if(tm.team == COLOR_SPECTATOR)
1260                                 continue;
1261
1262                         pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1263                 }
1264         }
1265
1266         if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1267                 if(race_speedaward) {
1268                         drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1269                         pos_y += 1.25 * hud_fontsize_y;
1270                 }
1271                 if(race_speedaward_alltimebest) {
1272                         drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1273                         pos_y += 1.25 * hud_fontsize_y;
1274                 }
1275                 pos = HUD_DrawScoreboardRankings(pos, pl, rgb, bg_size);
1276         }
1277         else if(autocvar_scoreboard_accuracy && spectatee_status != -1 && !warmup_stage) {
1278                 if(teamplay)
1279                         pos = HUD_DrawScoreboardAccuracyStats(pos, GetTeamRGB(myteam), bg_size);
1280                 else
1281                         pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1282         }
1283
1284                 
1285         if(teamplay)
1286                 pos = HUD_DrawMapStats(pos, GetTeamRGB(myteam), bg_size);
1287         else
1288                 pos = HUD_DrawMapStats(pos, rgb, bg_size);
1289
1290         // List spectators
1291         float specs;
1292         specs = 0;
1293         tmp = pos;
1294         for(pl = players.sort_next; pl; pl = pl.sort_next)
1295         {
1296                 if(pl.team != COLOR_SPECTATOR)
1297                         continue;
1298                 pos_y += 1.25 * hud_fontsize_y;
1299                 HUD_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localnum), specs);
1300                 ++specs;
1301         }
1302
1303         if(specs)
1304         {
1305                 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1306                 pos_y += 1.25 * hud_fontsize_y;
1307         }
1308
1309         // Print info string
1310         float tl, fl, ll;
1311         str = sprintf(_("playing on ^2%s^7"), shortmapname);
1312         tl = getstatf(STAT_TIMELIMIT);
1313         fl = getstatf(STAT_FRAGLIMIT);
1314         ll = getstatf(STAT_LEADLIMIT);
1315         if(gametype == MAPINFO_TYPE_LMS)
1316         {
1317                 if(tl > 0)
1318                         str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1319         }
1320         else
1321         {
1322                 if(tl > 0)
1323                         str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1324                 if(fl > 0)
1325                 {
1326                         if(tl > 0)
1327                                 str = strcat(str, _(" or"));
1328                         if(teamplay)
1329                         {
1330                                 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl), 
1331                                         (teamscores_label[ts_primary] == "score")   ? CTX(_("SCO^points")) :
1332                                         (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1333                                         TranslateScoresLabel(teamscores_label[ts_primary])));
1334                         }
1335                         else
1336                         {
1337                                 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl), 
1338                                         (scores_label[ps_primary] == "score")   ? CTX(_("SCO^points")) :
1339                                         (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1340                                         TranslateScoresLabel(scores_label[ps_primary])));
1341                         }
1342                 }
1343                 if(ll > 0)
1344                 {
1345                         if(tl > 0 || fl > 0)
1346                                 str = strcat(str, _(" or"));
1347                         if(teamplay)
1348                         {
1349                                 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll), 
1350                                         (teamscores_label[ts_primary] == "score")   ? CTX(_("SCO^points")) :
1351                                         (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1352                                         TranslateScoresLabel(teamscores_label[ts_primary])));
1353                         }
1354                         else
1355                         {
1356                                 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll), 
1357                                         (scores_label[ps_primary] == "score")   ? CTX(_("SCO^points")) :
1358                                         (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1359                                         TranslateScoresLabel(scores_label[ps_primary])));
1360                         }
1361                 }
1362         }
1363
1364
1365         pos_y += 1.2 * hud_fontsize_y;
1366         drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, TRUE, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1367
1368         scoreboard_bottom = pos_y + 2 * hud_fontsize_y;
1369 }