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