]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/client/scoreboard.qc
Remove drops from keepaway - they're redundant with pickups
[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 ^7filed1 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) 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(_("^3score^7                    Total score\n\n"));
272
273         print(_("Before a field you can put a + or - sign, then a comma separated list\n"
274                 "of game types, then a slash, to make the field show up only in these\n"
275                 "or in all but these game types. You can also specify 'all' as a\n"
276                 "field to show all fields available for the current game mode.\n\n"));
277
278         print(_("The special game type names 'teams' and 'noteams' can be used to\n"
279                 "include/exclude ALL teams/noteams game modes.\n\n"));
280
281         print(_("Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4\n"));
282         print(_("will display name, ping and pl aligned to the left, and the fields\n"
283                 "right of the vertical bar aligned to the right.\n"));
284         print(_("'field3' will only be shown in CTF, and 'field4' will be shown in all\n"
285                 "other gamemodes except DM.\n"));
286 }
287
288 string HUD_DefaultColumnLayout()
289 {
290         return strcat( // fteqcc sucks
291                 "ping pl name | ",
292                 "-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"
293                 "+ctf/caps +ctf/pickups +ctf/fckills +ctf/returns ",
294                 "+lms/lives +lms/rank ",
295                 "+kh/caps +kh/pushes +kh/destroyed ",
296                 "?+race/laps ?+race/time ?+race/fastest ",
297                 "+as/objectives +nexball/faults +nexball/goals +ka/pickups +ka/bckills +ka/bctime +freezetag/revivals ",
298                 "-lms,race,nexball/score");
299 }
300
301 void Cmd_HUD_SetFields(float argc)
302 {
303         float i, j, slash;
304         string str, pattern;
305         float have_name, have_primary, have_secondary, have_separator;
306         float missing;
307
308         // TODO: re enable with gametype dependant cvars?
309         if(argc < 2) // no arguments provided
310                 argc = tokenizebyseparator(strcat("x ", autocvar_scoreboard_columns), " ");
311
312         if(argc < 2)
313                 argc = tokenizebyseparator(strcat("x ", HUD_DefaultColumnLayout()), " ");
314
315         if(argc == 2)
316         {
317                 if(argv(1) == "default")
318                         argc = tokenizebyseparator(strcat("x ", HUD_DefaultColumnLayout()), " ");
319                 else if(argv(1) == "all")
320                 {
321                         string s;
322                         s = "ping pl color name |";
323                         for(i = 0; i < MAX_SCORE; ++i)
324                         {
325                                 if(i != ps_primary)
326                                 if(i != ps_secondary)
327                                 if(scores_label[i] != "")
328                                         s = strcat(s, " ", scores_label[i]);
329                         }
330                         if(ps_secondary != ps_primary)
331                                 s = strcat(s, " ", scores_label[ps_secondary]);
332                         s = strcat(s, " ", scores_label[ps_primary]);
333                         argc = tokenizebyseparator(strcat("x ", s), " ");
334                 }
335         }
336
337
338         hud_num_fields = 0;
339
340         hud_fontsize = HUD_GetFontsize("hud_fontsize"); 
341
342         for(i = 0; i < argc - 1; ++i)
343         {
344                 float nocomplain;
345                 str = argv(i+1);
346
347                 nocomplain = FALSE;
348                 if(substring(str, 0, 1) == "?")
349                 {
350                         nocomplain = TRUE;
351                         str = substring(str, 1, strlen(str) - 1);
352                 }
353
354                 slash = strstrofs(str, "/", 0);
355                 if(slash >= 0)
356                 {
357                         pattern = substring(str, 0, slash);
358                         str = substring(str, slash + 1, strlen(str) - (slash + 1));
359
360                         if not(isGametypeInFilter(gametype, teamplay, FALSE, pattern))
361                                 continue;
362                 }
363
364                 strunzone(hud_title[hud_num_fields]);
365                 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(str));
366                 hud_size[hud_num_fields] = stringwidth(str, FALSE, hud_fontsize);
367                 str = strtolower(str);
368
369                 if(str == "ping") {
370                         hud_field[hud_num_fields] = SP_PING;
371                 } else if(str == "pl") {
372                         hud_field[hud_num_fields] = SP_PL;
373                 } else if(str == "kd" || str == "kdr" || str == "kdratio" || str == "k/d") {
374                         hud_field[hud_num_fields] = SP_KDRATIO;
375                 } else if(str == "name" || str == "nick") {
376                         hud_field[hud_num_fields] = SP_NAME;
377                         have_name = 1;
378                 } else if(str == "|") {
379                         hud_field[hud_num_fields] = SP_SEPARATOR;
380                         have_separator = 1;
381                 } else {
382                         for(j = 0; j < MAX_SCORE; ++j)
383                                 if(str == strtolower(scores_label[j]))
384                                         goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
385 :notfound
386                         if(str == "frags")
387                         {
388                                 j = SP_FRAGS;
389                         }
390                         else
391                         {
392                                 if not(nocomplain)
393                                         print(sprintf("^1Error:^7 Unknown score field: '%s'\n", str));
394                                 continue;
395                         }
396 :found
397                         hud_field[hud_num_fields] = j;
398                         if(j == ps_primary)
399                                 have_primary = 1;
400                         if(j == ps_secondary)
401                                 have_secondary = 1;
402                 }
403                 ++hud_num_fields;
404                 if(hud_num_fields >= MAX_HUD_FIELDS)
405                         break;
406         }
407
408         if(scores_flags[ps_primary] & SFL_ALLOW_HIDE)
409                 have_primary = 1;
410         if(scores_flags[ps_secondary] & SFL_ALLOW_HIDE)
411                 have_secondary = 1;
412         if(ps_primary == ps_secondary)
413                 have_secondary = 1;
414         missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
415
416         if(hud_num_fields+missing < MAX_HUD_FIELDS)
417         {
418                 if(!have_name)
419                 {
420                         strunzone(hud_title[hud_num_fields]);
421                         for(i = hud_num_fields; i > 0; --i)
422                         {
423                                 hud_title[i] = hud_title[i-1];
424                                 hud_size[i] = hud_size[i-1];
425                                 hud_field[i] = hud_field[i-1];
426                         }
427                         hud_title[0] = strzone(TranslateScoresLabel("name"));
428                         hud_field[0] = SP_NAME;
429                         ++hud_num_fields;
430                         print(sprintf(_("fixed missing field '%s'\n"), "name"));
431
432                         if(!have_separator)
433                         {
434                                 strunzone(hud_title[hud_num_fields]);
435                                 for(i = hud_num_fields; i > 1; --i)
436                                 {
437                                         hud_title[i] = hud_title[i-1];
438                                         hud_size[i] = hud_size[i-1];
439                                         hud_field[i] = hud_field[i-1];
440                                 }
441                                 hud_title[1] = strzone("|");
442                                 hud_field[1] = SP_SEPARATOR;
443                                 hud_size[1] = stringwidth("|", FALSE, hud_fontsize);
444                                 ++hud_num_fields;
445                                 print(sprintf(_("fixed missing field '%s'\n"), "|"));
446                         }
447                 }
448                 else if(!have_separator)
449                 {
450                         strunzone(hud_title[hud_num_fields]);
451                         hud_title[hud_num_fields] = strzone("|");
452                         hud_size[hud_num_fields] = stringwidth("|", FALSE, hud_fontsize);
453                         hud_field[hud_num_fields] = SP_SEPARATOR;
454                         ++hud_num_fields;
455                         print(sprintf(_("fixed missing field '%s'\n"), "|"));
456                 }
457                 if(!have_secondary)
458                 {
459                         strunzone(hud_title[hud_num_fields]);
460                         hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_secondary]));
461                         hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], FALSE, hud_fontsize);
462                         hud_field[hud_num_fields] = ps_secondary;
463                         ++hud_num_fields;
464                         print(sprintf(_("fixed missing field '%s'\n"), scores_label[ps_secondary]));
465                 }
466                 if(!have_primary)
467                 {
468                         strunzone(hud_title[hud_num_fields]);
469                         hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_primary]));
470                         hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], FALSE, hud_fontsize);
471                         hud_field[hud_num_fields] = ps_primary;
472                         ++hud_num_fields;
473                         print(sprintf(_("fixed missing field '%s'\n"), scores_label[ps_primary]));
474                 }
475         }
476
477         hud_field[hud_num_fields] = SP_END;
478 }
479
480 // MOVEUP::
481 vector hud_field_rgb;
482 string hud_field_icon0;
483 string hud_field_icon1;
484 string hud_field_icon2;
485 vector hud_field_icon0_rgb;
486 vector hud_field_icon1_rgb;
487 vector hud_field_icon2_rgb;
488 float hud_field_icon0_alpha;
489 float hud_field_icon1_alpha;
490 float hud_field_icon2_alpha;
491 string HUD_GetField(entity pl, float field)
492 {
493         float tmp, num, denom, f;
494         string str;
495         hud_field_rgb = '1 1 1';
496         hud_field_icon0 = "";
497         hud_field_icon1 = "";
498         hud_field_icon2 = "";
499         hud_field_icon0_rgb = '1 1 1';
500         hud_field_icon1_rgb = '1 1 1';
501         hud_field_icon2_rgb = '1 1 1';
502         hud_field_icon0_alpha = 1;
503         hud_field_icon1_alpha = 1;
504         hud_field_icon2_alpha = 1;
505         switch(field)
506         {
507                 case SP_PING:
508                         if not(pl.gotscores)
509                                 return "\xEE\x82\x8D\xEE\x82\x8D\xEE\x82\x8D"; // >>> sign
510                         //str = getplayerkey(pl.sv_entnum, "ping");
511                         f = pl.ping;
512                         if(f == 0)
513                                 return _("N/A");
514                         tmp = max(0, min(220, f-80)) / 220;
515                         hud_field_rgb = '1 1 1' - '0 1 1'*tmp;
516                         return ftos(f);
517
518                 case SP_PL:
519                         if not(pl.gotscores)
520                                 return _("N/A");
521                         f = pl.ping_packetloss;
522                         tmp = pl.ping_movementloss;
523                         if(f == 0 && tmp == 0)
524                                 return "";
525                         str = ftos(ceil(f * 100));
526                         if(tmp != 0)
527                                 str = strcat(str, "~", ftos(ceil(tmp * 100)));
528                         tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
529                         hud_field_rgb = '1 0.5 0.5' - '0 0.5 0.5'*tmp;
530                         return str;
531
532                 case SP_NAME:
533                         if(ready_waiting && pl.ready)
534                         {
535                                 hud_field_icon0 = "gfx/scoreboard/player_ready";
536                         }
537                         else if(!teamplay)
538                         {
539                                 f = stof(getplayerkey(pl.sv_entnum, "colors"));
540                                 {
541                                         hud_field_icon0 = "gfx/scoreboard/playercolor_base";
542                                         hud_field_icon1 = "gfx/scoreboard/playercolor_shirt";
543                                         hud_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
544                                         hud_field_icon2 = "gfx/scoreboard/playercolor_pants";
545                                         hud_field_icon2_rgb = colormapPaletteColor(mod(f, 16), 1);
546                                 }
547                         }
548                         return GetPlayerName(pl.sv_entnum);
549
550                 case SP_FRAGS:
551                         f = pl.(scores[SP_KILLS]);
552                         f -= pl.(scores[SP_SUICIDES]);
553                         return ftos(f);
554
555                 case SP_KDRATIO:
556                         num = pl.(scores[SP_KILLS]);
557                         denom = pl.(scores[SP_DEATHS]);
558
559                         if(denom == 0) {
560                                 hud_field_rgb = '0 1 0';
561                                 str = sprintf("%d", num);
562                         } else if(num <= 0) {
563                                 hud_field_rgb = '1 0 0';
564                                 str = sprintf("%.1f", num/denom);
565                         } else
566                                 str = sprintf("%.1f", num/denom);
567                         return str;
568
569                 default:
570                         tmp = pl.(scores[field]);
571                         f = scores_flags[field];
572                         if(field == ps_primary)
573                                 hud_field_rgb = '1 1 0';
574                         else if(field == ps_secondary)
575                                 hud_field_rgb = '0 1 1';
576                         else
577                                 hud_field_rgb = '1 1 1';
578                         return ScoreString(f, tmp);
579         }
580         //return "error";
581 }
582
583 float xmin, xmax, ymin, ymax, sbwidth;
584 float hud_fixscoreboardcolumnwidth_len;
585 float hud_fixscoreboardcolumnwidth_iconlen;
586 float hud_fixscoreboardcolumnwidth_marginlen;
587
588 string HUD_FixScoreboardColumnWidth(float i, string str)
589 {
590         float field, f;
591         vector sz;
592         field = hud_field[i];
593
594         hud_fixscoreboardcolumnwidth_iconlen = 0;
595
596         if(hud_field_icon0 != "")
597         {
598                 sz = drawgetimagesize(hud_field_icon0);
599                 f = sz_x / sz_y;
600                 if(hud_fixscoreboardcolumnwidth_iconlen < f)
601                         hud_fixscoreboardcolumnwidth_iconlen = f;
602         }
603
604         if(hud_field_icon1 != "")
605         {
606                 sz = drawgetimagesize(hud_field_icon1);
607                 f = sz_x / sz_y;
608                 if(hud_fixscoreboardcolumnwidth_iconlen < f)
609                         hud_fixscoreboardcolumnwidth_iconlen = f;
610         }
611
612         if(hud_field_icon2 != "")
613         {
614                 sz = drawgetimagesize(hud_field_icon2);
615                 f = sz_x / sz_y;
616                 if(hud_fixscoreboardcolumnwidth_iconlen < f)
617                         hud_fixscoreboardcolumnwidth_iconlen = f;
618         }
619
620         hud_fixscoreboardcolumnwidth_iconlen *= hud_fontsize_y / hud_fontsize_x; // fix icon aspect
621
622         if(hud_fixscoreboardcolumnwidth_iconlen != 0)
623                 hud_fixscoreboardcolumnwidth_marginlen = stringwidth(" ", FALSE, hud_fontsize);
624         else
625                 hud_fixscoreboardcolumnwidth_marginlen = 0;
626
627         if(field == SP_NAME) // name gets all remaining space
628         {
629                 float namesize, j;
630                 namesize = sbwidth;// / hud_fontsize_x;
631                 for(j = 0; j < hud_num_fields; ++j)
632                         if(j != i)
633                                 if (hud_field[i] != SP_SEPARATOR)
634                                         namesize -= hud_size[j] + hud_fontsize_x;
635                 namesize += hud_fontsize_x;
636                 hud_size[i] = namesize;
637
638                 if (hud_fixscoreboardcolumnwidth_iconlen != 0)
639                         namesize -= hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
640                 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
641                 hud_fixscoreboardcolumnwidth_len = stringwidth(str, TRUE, hud_fontsize);
642         }
643         else
644                 hud_fixscoreboardcolumnwidth_len = stringwidth(str, FALSE, hud_fontsize);
645
646         f = hud_fixscoreboardcolumnwidth_len + hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
647         if(hud_size[i] < f)
648                 hud_size[i] = f;
649
650         return str;
651 }
652
653 void HUD_PrintScoreboardItem(vector pos, entity pl, float is_self, float pl_number)
654 {
655         vector tmp, rgb;
656         rgb = GetTeamRGB(pl.team);
657         string str;
658         float i, field;
659         float is_spec;
660         is_spec = (GetPlayerColor(pl.sv_entnum) == COLOR_SPECTATOR);
661
662         if((rgb == '1 1 1') && (!is_spec)) {
663                 rgb_x = autocvar_scoreboard_color_bg_r + 0.5;
664                 rgb_y = autocvar_scoreboard_color_bg_g + 0.5;
665                 rgb_z = autocvar_scoreboard_color_bg_b + 0.5; }
666
667         // Layout:
668         tmp_x = sbwidth;
669         tmp_y = hud_fontsize_y * 1.25;
670         tmp_z = 0;
671
672         // alternated rows highlighting
673         if(is_self)
674                 drawfill(pos - '1 1 0', tmp + '2 0 0', rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
675         else if((scoreboard_highlight) && (!mod(pl_number,2)))
676                 drawfill(pos - '1 1 0', tmp + '2 0 0', rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
677
678         tmp_y = 0;
679
680         for(i = 0; i < hud_num_fields; ++i)
681         {
682                 field = hud_field[i];
683                 if(field == SP_SEPARATOR)
684                         break;
685
686                 if(is_spec && field != SP_NAME && field != SP_PING) {
687                         pos_x += hud_size[i] + hud_fontsize_x;
688                         continue;
689                 }
690                 str = HUD_GetField(pl, field);
691                 str = HUD_FixScoreboardColumnWidth(i, str);
692
693                 pos_x += hud_size[i] + hud_fontsize_x;
694
695                 if(field == SP_NAME) {
696                         tmp_x = hud_size[i] - hud_fontsize_x*hud_fixscoreboardcolumnwidth_iconlen - hud_fixscoreboardcolumnwidth_marginlen + hud_fontsize_x;
697                         if (is_self)
698                                 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
699                         else
700                                 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
701                 } else {
702                         tmp_x = hud_fixscoreboardcolumnwidth_len + hud_fontsize_x;
703                         if (is_self)
704                                 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
705                         else
706                                 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
707                 }
708
709                 tmp_x = hud_size[i] + hud_fontsize_x;
710                 if(hud_field_icon0 != "")
711                         if (is_self)
712                                 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);
713                         else
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, DRAWFLAG_NORMAL);
715                 if(hud_field_icon1 != "")
716                         if (is_self)
717                                 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);
718                         else
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, DRAWFLAG_NORMAL);
720                 if(hud_field_icon2 != "")
721                         if (is_self)
722                                 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);
723                         else
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, DRAWFLAG_NORMAL);
725         }
726
727         if(hud_field[i] == SP_SEPARATOR)
728         {
729                 pos_x = xmax;
730                 for(i = hud_num_fields-1; i > 0; --i)
731                 {
732                         field = hud_field[i];
733                         if(field == SP_SEPARATOR)
734                                 break;
735
736                         if(is_spec && field != SP_NAME && field != SP_PING) {
737                                 pos_x -= hud_size[i] + hud_fontsize_x;
738                                 continue;
739                         }
740
741                         str = HUD_GetField(pl, field);
742                         str = HUD_FixScoreboardColumnWidth(i, str);
743
744                         if(field == SP_NAME) {
745                                 tmp_x = hud_fixscoreboardcolumnwidth_len; // left or right aligned? let's put it right...
746                                 if(is_self)
747                                         drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
748                                 else
749                                         drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
750                         } else {
751                                 tmp_x = hud_fixscoreboardcolumnwidth_len;
752                                 if(is_self)
753                                         drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
754                                 else
755                                         drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
756                         }
757
758                         tmp_x = hud_size[i];
759                         if(hud_field_icon0 != "")
760                                 if (is_self)
761                                         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);
762                                 else
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, DRAWFLAG_NORMAL);
764                         if(hud_field_icon1 != "")
765                                 if (is_self)
766                                         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);
767                                 else
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, DRAWFLAG_NORMAL);
769                         if(hud_field_icon2 != "")
770                                 if (is_self)
771                                         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);
772                                 else
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, DRAWFLAG_NORMAL);
774                         pos_x -= hud_size[i] + hud_fontsize_x;
775                 }
776         }
777 }
778
779 /*
780  * HUD_Scoreboard_MakeTable
781  *
782  * Makes a table for a team (for all playing players in DM) and fills it
783  */
784
785 vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
786 {
787         float body_table_height, i;
788         vector tmp, column_dim;
789         entity pl;
790
791         body_table_height = 1.25 * hud_fontsize_y * max(1, tm.team_size); // no player? show 1 empty line
792
793         pos -= '1 1 0';
794
795         tmp_x = sbwidth + 2;
796         tmp_y = 1.25 * hud_fontsize_y;
797
798         // rounded header
799         if (teamplay)
800                 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, (rgb * autocvar_scoreboard_color_bg_team) + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
801         else
802                 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
803
804         // table border
805         tmp_y += autocvar_scoreboard_border_thickness;
806         tmp_y += body_table_height;
807         drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL); // more transparency for the scoreboard
808
809         // separator header/table
810         pos_y += 1.25 * hud_fontsize_y;
811         tmp_y = autocvar_scoreboard_border_thickness;
812         drawfill(pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
813
814         pos_y += autocvar_scoreboard_border_thickness;
815
816         // table background
817         tmp_y = body_table_height;
818         if (teamplay)
819                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
820         else
821                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
822
823         // anyway, apply some color
824         //drawfill(pos, tmp + '2 0 0', rgb, 0.1, DRAWFLAG_NORMAL);
825
826         // go back to the top to make alternated columns highlighting and to print the strings
827         pos_y -= 1.25 * hud_fontsize_y;
828         pos_y -= autocvar_scoreboard_border_thickness;
829
830         pos += '1 1 0';
831
832         if (scoreboard_highlight)
833         {
834                 column_dim_y = 1.25 * hud_fontsize_y; // header
835                 column_dim_y += autocvar_scoreboard_border_thickness;
836                 column_dim_y += body_table_height;
837         }
838
839         // print the strings of the columns headers and draw the columns
840         for(i = 0; i < hud_num_fields; ++i)
841         {
842                 if(hud_field[i] == SP_SEPARATOR)
843                         break;
844                 column_dim_x = hud_size[i] + hud_fontsize_x;
845                 if (scoreboard_highlight)
846                 {
847                         if (mod(i,2))
848                                 drawfill(pos - '0 1 0' - hud_fontsize_x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
849                 }
850                 drawstring(pos, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
851                 pos_x += column_dim_x;
852         }
853         if(hud_field[i] == SP_SEPARATOR)
854         {
855                 pos_x = xmax;
856                 tmp_y = 0;
857                 for(i = hud_num_fields-1; i > 0; --i)
858                 {
859                         if(hud_field[i] == SP_SEPARATOR)
860                                 break;
861
862                         pos_x -= hud_size[i];
863
864                         if (scoreboard_highlight)
865                         {
866                                 if (!mod(i,2))
867                                 {
868                                         if (i == hud_num_fields-1)
869                                                 column_dim_x = hud_size[i] + hud_fontsize_x / 2 + 1;
870                                         else
871                                                 column_dim_x = hud_size[i] + hud_fontsize_x;
872                                         drawfill(pos - '0 1 0' - hud_fontsize_x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
873                                 }
874                         }
875
876                         tmp_x = stringwidth(hud_title[i], FALSE, hud_fontsize);
877                         tmp_x = (hud_size[i] - tmp_x);
878                         drawstring(pos + tmp, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
879                         pos_x -= hud_fontsize_x;
880                 }
881         }
882
883         pos_x = xmin;
884         pos_y += 1.25 * hud_fontsize_y; // skip the header
885         pos_y += autocvar_scoreboard_border_thickness;
886
887         // fill the table and draw the rows
888         i = 0;
889         if (teamplay)
890                 for(pl = players.sort_next; pl; pl = pl.sort_next)
891                 {
892                         if(pl.team != tm.team)
893                                 continue;
894                         HUD_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localentnum - 1), i);
895                         pos_y += 1.25 * hud_fontsize_y;
896                         ++i;
897                 }
898         else
899                 for(pl = players.sort_next; pl; pl = pl.sort_next)
900                 {
901                         if(pl.team == COLOR_SPECTATOR)
902                                 continue;
903                         HUD_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localentnum - 1), i);
904                         pos_y += 1.25 * hud_fontsize_y;
905                         ++i;
906                 }
907
908         if (i == 0)
909                 pos_y += 1.25 * hud_fontsize_y; // move to the end of the table
910         pos_y += 1.25 * hud_fontsize_y; // move empty row (out of the table)
911
912         return pos;
913 }
914
915 float HUD_WouldDrawScoreboard() {
916         if (autocvar__hud_configure)
917                 return 0;
918         else if (scoreboard_showscores)
919                 return 1;
920         else if (intermission == 1)
921                 return 1;
922         else if (intermission == 2)
923                 return 0;
924         else if (getstati(STAT_HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != GAME_CTS)
925                 return 1;
926         else if (spectatee_status == -1)
927                 return 1;
928         else if (scoreboard_showscores_force)
929                 return 1;
930         return 0;
931 }
932
933 float g_minstagib;
934 float average_accuracy;
935 vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
936 {
937         float i;
938         float weapon_cnt = WEP_COUNT - 3; // either minstanex/nex are hidden, no port-o-launch, no tuba
939         float rows;
940         if(autocvar_scoreboard_accuracy_doublerows)
941                 rows = 2;
942         else
943                 rows = 1;
944         float height = 40;
945         float fontsize = height * 1/3;
946         float weapon_height = height * 2/3;
947         float weapon_width = sbwidth / weapon_cnt;
948
949         drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
950         pos_y += 1.25 * hud_fontsize_y;
951         vector tmp;
952         tmp_x = sbwidth;
953         tmp_y = height * rows;
954
955         if (teamplay)
956                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
957         else
958                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
959         drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
960
961         // column highlighting
962         for(i = 0; i < weapon_cnt/rows; ++i)
963         {
964                 if(!mod(i, 2))
965                         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);
966         }
967
968         // row highlighting
969         for(i = 0; i < rows; ++i)
970         {
971                 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);
972         }
973
974         average_accuracy = 0;
975         float weapons_with_stats;
976         weapons_with_stats = 0;
977         if(rows == 2)
978                 pos_x += weapon_width / 2;
979
980         if(getstati(STAT_SWITCHWEAPON) == WEP_MINSTANEX)
981                 g_minstagib = 1; // TODO: real detection for minstagib?
982
983         float weapon_stats, weapon_number;
984
985         if (!acc_levels)
986                 rgb = '1 1 1';
987         else if (acc_col_x[0] == -1)
988                 for (i = 0; i < acc_levels; ++i)
989                         acc_col[i] = stov(cvar_string(strcat("accuracy_color", ftos(i))));
990
991         for(i = WEP_FIRST; i <= WEP_LAST; ++i)
992         {
993                 self = get_weaponinfo(i);
994                 if not(self.weapons)
995                         continue;
996                 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
997                         continue;
998                 weapon_stats = weapon_accuracy[i-WEP_FIRST];
999
1000                 float weapon_alpha;
1001                 if(weapon_stats >= 0)
1002                         weapon_alpha = scoreboard_alpha_fg;
1003                 else
1004                         weapon_alpha = 0.2 * scoreboard_alpha_fg;
1005
1006                 // weapon icon
1007                 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);
1008                 // the accuracy
1009                 if(weapon_stats >= 0) {
1010                         weapons_with_stats += 1;
1011                         average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1012
1013                         string s;
1014                         s = sprintf(_("%d%%"), weapon_stats*100);
1015
1016                         float padding;
1017                         padding = (weapon_width - stringwidth(s, FALSE, '1 0 0' * fontsize)) / 2; // center the accuracy value
1018
1019                         if (acc_levels)
1020                         {
1021                                 // find the max level lower than weapon_stats
1022                                 float j;
1023                                 j = acc_levels-1;
1024                                 while ( j && weapon_stats < acc_lev[j] )
1025                                         --j;
1026
1027                                 // inject color j+1 in color j, how much depending on how much weapon_stats is higher than level j
1028                                 float factor;
1029                                 factor = (weapon_stats - acc_lev[j]) / (acc_lev[j+1] - acc_lev[j]);
1030                                 rgb = acc_col[j];
1031                                 rgb = rgb + factor * (acc_col[j+1] - rgb);
1032                         }
1033
1034                         drawstring(pos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1035                 }
1036                 pos_x += weapon_width * rows;
1037                 if(rows == 2 && i == 6) {
1038                         pos_x -= sbwidth;
1039                         pos_y += height;
1040                 }
1041         }
1042
1043         if(weapons_with_stats)
1044                 average_accuracy = floor(average_accuracy / weapons_with_stats);
1045
1046         if(rows == 2)
1047                 pos_x -= weapon_width / 2;
1048         pos_x -= sbwidth;
1049         pos_y += height;
1050
1051         pos_y +=  1.25 * hud_fontsize_y;
1052         return pos;
1053 }
1054
1055 vector HUD_DrawScoreboardRankings(vector pos, entity pl,  vector rgb, vector bg_size)
1056 {
1057         float i;
1058         RANKINGS_RECEIVED_CNT = 0;
1059         for (i=RANKINGS_CNT-1; i>=0; --i)
1060                 if (grecordtime[i])
1061                         ++RANKINGS_RECEIVED_CNT;
1062
1063         if (RANKINGS_RECEIVED_CNT == 0)
1064                 return pos;
1065
1066         float is_spec;
1067         is_spec = (GetPlayerColor(pl.sv_entnum) == COLOR_SPECTATOR);
1068         vector hl_rgb;
1069         hl_rgb_x = autocvar_scoreboard_color_bg_r + 0.5;
1070         hl_rgb_y = autocvar_scoreboard_color_bg_g + 0.5;
1071         hl_rgb_z = autocvar_scoreboard_color_bg_b + 0.5;
1072
1073         pos_y += hud_fontsize_y;
1074         drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1075         pos_y += hud_fontsize_y;
1076         vector tmp;
1077         tmp_x = sbwidth;
1078         tmp_y = 1.25 * hud_fontsize_y * RANKINGS_RECEIVED_CNT;
1079
1080         if (teamplay)
1081                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1082         else
1083                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1084         drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1085
1086         // row highlighting
1087         for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1088         {
1089                 string n, p;
1090                 float t;
1091                 t = grecordtime[i];
1092                 if (t == 0)
1093                         continue;
1094                 n = grecordholder[i];
1095                 p = race_PlaceName(i+1);
1096                 if(grecordholder[i] == GetPlayerName(player_localentnum - 1))
1097                         drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize_y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1098                 else if(!mod(i, 2) && scoreboard_highlight)
1099                         drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize_y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1100                 drawstring(pos, p, '1 1 0' * hud_fontsize_y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1101                 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);
1102                 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize_y, n, '1 1 0' * hud_fontsize_y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1103                 pos_y += 1.25 * hud_fontsize_y;
1104         }
1105
1106         return pos;
1107 }
1108
1109 float hud_woulddrawscoreboard_prev;
1110 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1111 void HUD_DrawScoreboard()
1112 {
1113         float hud_woulddrawscoreboard;
1114         hud_woulddrawscoreboard = scoreboard_active;
1115         if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1116                 hud_woulddrawscoreboard_change = time;
1117                 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1118         }
1119
1120         if(hud_woulddrawscoreboard) {
1121                 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1122                 if (scoreboard_fadeinspeed)
1123                         scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1124                 else
1125                         scoreboard_fade_alpha = 1;
1126         }
1127         else {
1128                 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1129                 if (scoreboard_fadeoutspeed)
1130                         scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1131                 else
1132                         scoreboard_fade_alpha = 0;
1133         }
1134
1135         if not(scoreboard_fade_alpha)
1136                 return;
1137
1138         HUD_UpdatePlayerTeams();
1139
1140         scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1141         scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1142         scoreboard_highlight = autocvar_scoreboard_highlight;
1143         scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1144         scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1145         scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1146         scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1147
1148         vector rgb, pos, tmp;
1149         entity pl, tm;
1150
1151         xmin = autocvar_scoreboard_offset_left * vid_conwidth;
1152         ymin = autocvar_con_notify * autocvar_con_notifysize;
1153
1154         xmax = (1 - autocvar_scoreboard_offset_right) * vid_conwidth;
1155         ymax = vid_conheight - ymin;
1156
1157         sbwidth = xmax - xmin;
1158
1159         // Initializes position
1160         pos_x = xmin;
1161         pos_y = ymin;
1162         pos_z = 0;
1163
1164         // Heading
1165         drawstring(pos, _("Scoreboard"), '24 24 0', '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1166         
1167         centerprint_start_x = vid_conwidth - 0.5 * (pos_x + stringwidth(_("Scoreboard"), FALSE, '24 24 0'));
1168         centerprint_start_y = pos_y;
1169
1170         pos_y += 24;
1171
1172         // Draw the scoreboard
1173         vector bg_size = drawgetimagesize("gfx/scoreboard/scoreboard_bg") * autocvar_scoreboard_bg_scale;
1174
1175         if(teamplay)
1176         {
1177                 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1178                 {
1179                         if(tm.team == COLOR_SPECTATOR)
1180                                 continue;
1181
1182                         rgb = GetTeamRGB(tm.team);
1183                         drawstring(pos - '2 0 0' * hud_fontsize_x + '0 1 0' * hud_fontsize_y, ftos(tm.(teamscores[ts_primary])), '1 1 0' * hud_fontsize_y * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1184
1185                         if(ts_primary != ts_secondary)
1186                                 drawstring(pos - '2 0 0' * hud_fontsize_x + '0 2.5 0' * hud_fontsize_y, ftos(tm.(teamscores[ts_secondary])), '1 1 0' * hud_fontsize_y * 1, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1187
1188                         pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1189                 }
1190         }
1191         else
1192         {
1193                 rgb_x = autocvar_scoreboard_color_bg_r;
1194                 rgb_y = autocvar_scoreboard_color_bg_g;
1195                 rgb_z = autocvar_scoreboard_color_bg_b;
1196
1197                 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1198                 {
1199                         if(tm.team == COLOR_SPECTATOR)
1200                                 continue;
1201
1202                         pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1203                 }
1204         }
1205
1206         if(gametype == GAME_CTS || gametype == GAME_RACE) {
1207                 if(race_speedaward) {
1208                         drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1209                         pos_y += 1.25 * hud_fontsize_y;
1210                 }
1211                 if(race_speedaward_alltimebest) {
1212                         drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1213                         pos_y += 1.25 * hud_fontsize_y;
1214                 }
1215                 pos = HUD_DrawScoreboardRankings(pos, pl, rgb, bg_size);
1216         }
1217         else if(autocvar_scoreboard_accuracy && spectatee_status != -1 && !warmup_stage) {
1218                 if(teamplay)
1219                         pos = HUD_DrawScoreboardAccuracyStats(pos, GetTeamRGB(myteam), bg_size);
1220                 else
1221                         pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1222         }
1223
1224         // List spectators
1225         float specs;
1226         specs = 0;
1227         tmp = pos;
1228         for(pl = players.sort_next; pl; pl = pl.sort_next)
1229         {
1230                 if(pl.team != COLOR_SPECTATOR)
1231                         continue;
1232                 pos_y += 1.25 * hud_fontsize_y;
1233                 HUD_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localentnum - 1), specs);
1234                 ++specs;
1235         }
1236
1237         if(specs)
1238         {
1239                 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1240                 pos_y += 1.25 * hud_fontsize_y;
1241         }
1242
1243         // Print info string
1244         string str;
1245         float tl, fl, ll;
1246         str = sprintf(_("playing on ^2%s^7"), shortmapname);
1247         tl = getstatf(STAT_TIMELIMIT);
1248         fl = getstatf(STAT_FRAGLIMIT);
1249         ll = getstatf(STAT_LEADLIMIT);
1250         if(gametype == GAME_LMS)
1251         {
1252                 if(tl > 0)
1253                         str = strcat(str, sprintf(_(" for up to ^1%.1f minutes^7"), tl));
1254         }
1255         else
1256         {
1257                 if(tl > 0)
1258                         str = strcat(str, sprintf(_(" for up to ^1%.1f minutes^7"), tl));
1259                 if(fl > 0)
1260                 {
1261                         if(tl > 0)
1262                                 str = strcat(str, _(" or"));
1263                         if(teamplay)
1264                         {
1265                                 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl), 
1266                                         (teamscores_label[ts_primary] == "score")   ? CTX(_("SCO^points")) :
1267                                         (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1268                                         TranslateScoresLabel(teamscores_label[ts_primary])));
1269                         }
1270                         else
1271                         {
1272                                 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl), 
1273                                         (scores_label[ps_primary] == "score")   ? CTX(_("SCO^points")) :
1274                                         (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1275                                         TranslateScoresLabel(scores_label[ps_primary])));
1276                         }
1277                 }
1278                 if(ll > 0)
1279                 {
1280                         if(tl > 0 || fl > 0)
1281                                 str = strcat(str, _(" or"));
1282                         if(teamplay)
1283                         {
1284                                 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll), 
1285                                         (teamscores_label[ts_primary] == "score")   ? CTX(_("SCO^points")) :
1286                                         (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1287                                         TranslateScoresLabel(teamscores_label[ts_primary])));
1288                         }
1289                         else
1290                         {
1291                                 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll), 
1292                                         (scores_label[ps_primary] == "score")   ? CTX(_("SCO^points")) :
1293                                         (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1294                                         TranslateScoresLabel(scores_label[ps_primary])));
1295                         }
1296                 }
1297         }
1298
1299
1300         pos_y += 1.2 * hud_fontsize_y;
1301         drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, TRUE, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1302
1303         scoreboard_bottom = pos_y + 2 * hud_fontsize_y;
1304 }