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