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