]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/client/scoreboard.qc
df3e490d9d9ee5402a0dfc7daca271e793e485b9
[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 (getstati(STAT_HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != GAME_CTS)
884                 return 1;
885         else if (spectatee_status == -1)
886                 return 1;
887         else if (scoreboard_showscores_force)
888                 return 1;
889         return 0;
890 }
891
892 float g_minstagib;
893 float average_accuracy;
894 vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
895 {
896         float i;
897         float weapon_cnt = WEP_COUNT - 3; // either minstanex/nex are hidden, no port-o-launch, no tuba
898         float rows;
899         if(autocvar_scoreboard_accuracy_doublerows)
900                 rows = 2;
901         else
902                 rows = 1;
903         float height = 40;
904         float fontsize = height * 1/3;
905         float weapon_height = height * 2/3;
906         float weapon_width = sbwidth / weapon_cnt;
907
908         drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
909         pos_y += 1.25 * hud_fontsize_y;
910         vector tmp;
911         tmp_x = sbwidth;
912         tmp_y = height * rows;
913
914         if (teamplay)
915                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
916         else
917                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
918         drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
919
920         // column highlighting
921         for(i = 0; i < weapon_cnt/rows; ++i)
922         {
923                 if(!mod(i, 2))
924                         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);
925         }
926
927         // row highlighting
928         for(i = 0; i < rows; ++i)
929         {
930                 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);
931         }
932
933         average_accuracy = 0;
934         float weapons_with_stats;
935         weapons_with_stats = 0;
936         if(rows == 2)
937                 pos_x += weapon_width / 2;
938
939         if(getstati(STAT_SWITCHWEAPON) == WEP_MINSTANEX)
940                 g_minstagib = 1; // TODO: real detection for minstagib?
941
942         float weapon_stats, weapon_number;
943
944         if (!acc_levels)
945                 rgb = '1 1 1';
946         else if (acc_col_x[0] == -1)
947                 for (i = 0; i < acc_levels; ++i)
948                         acc_col[i] = stov(cvar_string(strcat("accuracy_color", ftos(i))));
949
950         for(i = WEP_FIRST; i <= WEP_LAST; ++i)
951         {
952                 self = get_weaponinfo(i);
953                 if not(self.weapons)
954                         continue;
955                 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
956                         continue;
957                 weapon_stats = weapon_accuracy[i-WEP_FIRST];
958
959                 float weapon_alpha;
960                 if(weapon_stats >= 0)
961                         weapon_alpha = scoreboard_alpha_fg;
962                 else
963                         weapon_alpha = 0.2 * scoreboard_alpha_fg;
964
965                 // weapon icon
966                 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);
967                 // the accuracy
968                 if(weapon_stats >= 0) {
969                         weapons_with_stats += 1;
970                         average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
971
972                         string s;
973                         s = sprintf(_("%d%%"), weapon_stats*100);
974
975                         float padding;
976                         padding = (weapon_width - stringwidth(s, FALSE, '1 0 0' * fontsize)) / 2; // center the accuracy value
977
978                         if (acc_levels)
979                         {
980                                 // find the max level lower than weapon_stats
981                                 float j;
982                                 j = acc_levels-1;
983                                 while ( j && weapon_stats < acc_lev[j] )
984                                         --j;
985
986                                 // inject color j+1 in color j, how much depending on how much weapon_stats is higher than level j
987                                 float factor;
988                                 factor = (weapon_stats - acc_lev[j]) / (acc_lev[j+1] - acc_lev[j]);
989                                 rgb = acc_col[j];
990                                 rgb = rgb + factor * (acc_col[j+1] - rgb);
991                         }
992
993                         drawstring(pos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
994                 }
995                 pos_x += weapon_width * rows;
996                 if(rows == 2 && i == 6) {
997                         pos_x -= sbwidth;
998                         pos_y += height;
999                 }
1000         }
1001
1002         if(weapons_with_stats)
1003                 average_accuracy = floor(average_accuracy / weapons_with_stats);
1004
1005         if(rows == 2)
1006                 pos_x -= weapon_width / 2;
1007         pos_x -= sbwidth;
1008         pos_y += height;
1009
1010         pos_y +=  1.25 * hud_fontsize_y;
1011         return pos;
1012 }
1013
1014 vector HUD_DrawScoreboardRankings(vector pos, entity pl,  vector rgb, vector bg_size)
1015 {
1016         float i;
1017         RANKINGS_RECEIVED_CNT = 0;
1018         for (i=RANKINGS_CNT-1; i>=0; --i)
1019                 if (grecordtime[i])
1020                         ++RANKINGS_RECEIVED_CNT;
1021
1022         if (RANKINGS_RECEIVED_CNT == 0)
1023                 return pos;
1024
1025         float is_spec;
1026         is_spec = (GetPlayerColor(pl.sv_entnum) == COLOR_SPECTATOR);
1027         vector hl_rgb;
1028         hl_rgb_x = autocvar_scoreboard_color_bg_r + 0.5;
1029         hl_rgb_y = autocvar_scoreboard_color_bg_g + 0.5;
1030         hl_rgb_z = autocvar_scoreboard_color_bg_b + 0.5;
1031
1032         pos_y += hud_fontsize_y;
1033         drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1034         pos_y += hud_fontsize_y;
1035         vector tmp;
1036         tmp_x = sbwidth;
1037         tmp_y = 1.25 * hud_fontsize_y * RANKINGS_RECEIVED_CNT;
1038
1039         if (teamplay)
1040                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1041         else
1042                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1043         drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1044
1045         // row highlighting
1046         for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1047         {
1048                 string n, p;
1049                 float t;
1050                 t = grecordtime[i];
1051                 if (t == 0)
1052                         continue;
1053                 n = grecordholder[i];
1054                 p = race_PlaceName(i+1);
1055                 if(grecordholder[i] == GetPlayerName(player_localentnum - 1))
1056                         drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize_y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1057                 else if(!mod(i, 2) && scoreboard_highlight)
1058                         drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize_y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1059                 drawstring(pos, p, '1 1 0' * hud_fontsize_y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1060                 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);
1061                 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize_y, n, '1 1 0' * hud_fontsize_y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1062                 pos_y += 1.25 * hud_fontsize_y;
1063         }
1064
1065         return pos;
1066 }
1067
1068 float hud_woulddrawscoreboard_prev;
1069 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1070 void HUD_DrawScoreboard()
1071 {
1072         float hud_woulddrawscoreboard;
1073         hud_woulddrawscoreboard = scoreboard_active;
1074         if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1075                 hud_woulddrawscoreboard_change = time;
1076                 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1077         }
1078
1079         if(hud_woulddrawscoreboard) {
1080                 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1081                 if (scoreboard_fadeinspeed)
1082                         scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1083                 else
1084                         scoreboard_fade_alpha = 1;
1085         }
1086         else {
1087                 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1088                 if (scoreboard_fadeoutspeed)
1089                         scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1090                 else
1091                         scoreboard_fade_alpha = 0;
1092         }
1093
1094         if not(scoreboard_fade_alpha)
1095                 return;
1096
1097         HUD_UpdatePlayerTeams();
1098
1099         scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1100         scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1101         scoreboard_highlight = autocvar_scoreboard_highlight;
1102         scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1103         scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1104         scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1105         scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1106
1107         vector rgb, pos, tmp;
1108         entity pl, tm;
1109
1110         xmin = autocvar_scoreboard_offset_left * vid_conwidth;
1111         ymin = autocvar_con_notify * autocvar_con_notifysize;
1112
1113         xmax = (1 - autocvar_scoreboard_offset_right) * vid_conwidth;
1114         ymax = vid_conheight - ymin;
1115
1116         sbwidth = xmax - xmin;
1117
1118         // Initializes position
1119         pos_x = xmin;
1120         pos_y = ymin;
1121         pos_z = 0;
1122
1123         // Heading
1124         drawstring(pos, _("Scoreboard"), '24 24 0', '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1125         
1126         centerprint_start_x = vid_conwidth - 0.5 * (pos_x + stringwidth(_("Scoreboard"), FALSE, '24 24 0'));
1127         centerprint_start_y = pos_y;
1128
1129         pos_y += 24;
1130
1131         // Draw the scoreboard
1132         vector bg_size = drawgetimagesize("gfx/scoreboard/scoreboard_bg") * autocvar_scoreboard_bg_scale;
1133
1134         if(teamplay)
1135         {
1136                 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1137                 {
1138                         if(tm.team == COLOR_SPECTATOR)
1139                                 continue;
1140
1141                         rgb = GetTeamRGB(tm.team);
1142                         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);
1143
1144                         if(ts_primary != ts_secondary)
1145                                 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);
1146
1147                         pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1148                 }
1149         }
1150         else
1151         {
1152                 rgb_x = autocvar_scoreboard_color_bg_r;
1153                 rgb_y = autocvar_scoreboard_color_bg_g;
1154                 rgb_z = autocvar_scoreboard_color_bg_b;
1155
1156                 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1157                 {
1158                         if(tm.team == COLOR_SPECTATOR)
1159                                 continue;
1160
1161                         pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1162                 }
1163         }
1164
1165         if(gametype == GAME_CTS || gametype == GAME_RACE) {
1166                 if(race_speedaward) {
1167                         drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1168                         pos_y += 1.25 * hud_fontsize_y;
1169                 }
1170                 if(race_speedaward_alltimebest) {
1171                         drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1172                         pos_y += 1.25 * hud_fontsize_y;
1173                 }
1174                 pos = HUD_DrawScoreboardRankings(pos, pl, rgb, bg_size);
1175         }
1176         else if(autocvar_scoreboard_accuracy && spectatee_status != -1 && !warmup_stage) {
1177                 if(teamplay)
1178                         pos = HUD_DrawScoreboardAccuracyStats(pos, GetTeamRGB(myteam), bg_size);
1179                 else
1180                         pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1181         }
1182
1183         // List spectators
1184         float specs;
1185         specs = 0;
1186         tmp = pos;
1187         for(pl = players.sort_next; pl; pl = pl.sort_next)
1188         {
1189                 if(pl.team != COLOR_SPECTATOR)
1190                         continue;
1191                 pos_y += 1.25 * hud_fontsize_y;
1192                 HUD_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localentnum - 1), specs);
1193                 ++specs;
1194         }
1195
1196         if(specs)
1197         {
1198                 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1199                 pos_y += 1.25 * hud_fontsize_y;
1200         }
1201
1202         // Print info string
1203         string str;
1204         float tl, fl, ll;
1205         str = sprintf(_("playing on ^2%s^7"), shortmapname);
1206         tl = getstatf(STAT_TIMELIMIT);
1207         fl = getstatf(STAT_FRAGLIMIT);
1208         ll = getstatf(STAT_LEADLIMIT);
1209         if(gametype == GAME_LMS)
1210         {
1211                 if(tl > 0)
1212                         str = strcat(str, sprintf(_(" for up to ^1%.1g minutes^7"), tl));
1213         }
1214         else
1215         {
1216                 if(tl > 0)
1217                         str = strcat(str, sprintf(_(" for up to ^1%.1g minutes^7"), tl));
1218                 if(fl > 0)
1219                 {
1220                         if(tl > 0)
1221                                 str = strcat(str, _(" or"));
1222                         if(teamplay)
1223                         {
1224                                 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl), 
1225                                         (teamscores_label[ts_primary] == "score")   ? _("points") :
1226                                         (teamscores_label[ts_primary] == "fastest") ? _("is beaten") :
1227                                         teamscores_label[ts_primary]));
1228                         }
1229                         else
1230                         {
1231                                 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl), 
1232                                         (teamscores_label[ts_primary] == "score")   ? _("points") :
1233                                         (teamscores_label[ts_primary] == "fastest") ? _("is beaten") :
1234                                         teamscores_label[ts_primary]));
1235                         }
1236                 }
1237                 if(ll > 0)
1238                 {
1239                         if(tl > 0 || fl > 0)
1240                                 str = strcat(str, _(" or"));
1241                         if(teamplay)
1242                         {
1243                                 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll), 
1244                                         (teamscores_label[ts_primary] == "score")   ? _("points") :
1245                                         (teamscores_label[ts_primary] == "fastest") ? _("is beaten") :
1246                                         teamscores_label[ts_primary]));
1247                         }
1248                         else
1249                         {
1250                                 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll), 
1251                                         (teamscores_label[ts_primary] == "score")   ? _("points") :
1252                                         (teamscores_label[ts_primary] == "fastest") ? _("is beaten") :
1253                                         teamscores_label[ts_primary]));
1254                         }
1255                 }
1256         }
1257
1258
1259         pos_y += 1.2 * hud_fontsize_y;
1260         drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, TRUE, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1261
1262         scoreboard_bottom = pos_y + 2 * hud_fontsize_y;
1263 }