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