]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/client/scoreboard.qc
added a 'net' column to the tdm scoreboard (enabled by default)
[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 "net": return CTX(_("SCO^net"));
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(_("^3net^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/frags +tdm/deaths +tdm/net ",
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 == "net" || str == "net" || str == "net" || str == "f-d") {
394                         hud_field[hud_num_fields] = SP_NET;
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(sprintf(_("fixed missing field '%s'\n"), "name"));
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(sprintf(_("fixed missing field '%s'\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(sprintf(_("fixed missing field '%s'\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, num1;
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_NET:
591                         num1 = pl.(scores[SP_KILLS - SP_SUICIDES +2]);
592                         denom = pl.(scores[SP_DEATHS]);
593
594                         if((num1 - denom) > 0) {
595                                 hud_field_rgb = '0 1 0';
596                                 str = sprintf("%d", (num1 - denom));
597                         } else if((num1 - denom) == 0) {
598                                 hud_field_rgb = '1 1 1';
599                                 str = sprintf("%d", (num1 - denom));
600                         } else {
601                                 hud_field_rgb = '1 0 0';
602                                 str = sprintf("%d", (num1 - denom));
603                         }
604                         return str;
605
606                 default:
607                         tmp = pl.(scores[field]);
608                         f = scores_flags[field];
609                         if(field == ps_primary)
610                                 hud_field_rgb = '1 1 0';
611                         else if(field == ps_secondary)
612                                 hud_field_rgb = '0 1 1';
613                         else
614                                 hud_field_rgb = '1 1 1';
615                         return ScoreString(f, tmp);
616         }
617         //return "error";
618 }
619
620 float xmin, xmax, ymin, ymax, sbwidth;
621 float hud_fixscoreboardcolumnwidth_len;
622 float hud_fixscoreboardcolumnwidth_iconlen;
623 float hud_fixscoreboardcolumnwidth_marginlen;
624
625 string HUD_FixScoreboardColumnWidth(float i, string str)
626 {
627         float field, f;
628         vector sz;
629         field = hud_field[i];
630
631         hud_fixscoreboardcolumnwidth_iconlen = 0;
632
633         if(hud_field_icon0 != "")
634         {
635                 sz = draw_getimagesize(hud_field_icon0);
636                 f = sz_x / sz_y;
637                 if(hud_fixscoreboardcolumnwidth_iconlen < f)
638                         hud_fixscoreboardcolumnwidth_iconlen = f;
639         }
640
641         if(hud_field_icon1 != "")
642         {
643                 sz = draw_getimagesize(hud_field_icon1);
644                 f = sz_x / sz_y;
645                 if(hud_fixscoreboardcolumnwidth_iconlen < f)
646                         hud_fixscoreboardcolumnwidth_iconlen = f;
647         }
648
649         if(hud_field_icon2 != "")
650         {
651                 sz = draw_getimagesize(hud_field_icon2);
652                 f = sz_x / sz_y;
653                 if(hud_fixscoreboardcolumnwidth_iconlen < f)
654                         hud_fixscoreboardcolumnwidth_iconlen = f;
655         }
656
657         hud_fixscoreboardcolumnwidth_iconlen *= hud_fontsize_y / hud_fontsize_x; // fix icon aspect
658
659         if(hud_fixscoreboardcolumnwidth_iconlen != 0)
660                 hud_fixscoreboardcolumnwidth_marginlen = stringwidth(" ", FALSE, hud_fontsize);
661         else
662                 hud_fixscoreboardcolumnwidth_marginlen = 0;
663
664         if(field == SP_NAME) // name gets all remaining space
665         {
666                 float namesize, j;
667                 namesize = sbwidth;// / hud_fontsize_x;
668                 for(j = 0; j < hud_num_fields; ++j)
669                         if(j != i)
670                                 if (hud_field[i] != SP_SEPARATOR)
671                                         namesize -= hud_size[j] + hud_fontsize_x;
672                 namesize += hud_fontsize_x;
673                 hud_size[i] = namesize;
674
675                 if (hud_fixscoreboardcolumnwidth_iconlen != 0)
676                         namesize -= hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
677                 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
678                 hud_fixscoreboardcolumnwidth_len = stringwidth(str, TRUE, hud_fontsize);
679         }
680         else
681                 hud_fixscoreboardcolumnwidth_len = stringwidth(str, FALSE, hud_fontsize);
682
683         f = hud_fixscoreboardcolumnwidth_len + hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
684         if(hud_size[i] < f)
685                 hud_size[i] = f;
686
687         return str;
688 }
689
690 void HUD_PrintScoreboardItem(vector pos, entity pl, float is_self, float pl_number)
691 {
692         vector tmp, rgb;
693         rgb = Team_ColorRGB(pl.team);
694         string str;
695         float i, field;
696         float is_spec;
697         is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
698
699         if((rgb == '1 1 1') && (!is_spec)) {
700                 rgb_x = autocvar_scoreboard_color_bg_r + 0.5;
701                 rgb_y = autocvar_scoreboard_color_bg_g + 0.5;
702                 rgb_z = autocvar_scoreboard_color_bg_b + 0.5; }
703
704         // Layout:
705         tmp_x = sbwidth;
706         tmp_y = hud_fontsize_y * 1.25;
707         tmp_z = 0;
708
709         // alternated rows highlighting
710         if(is_self)
711                 drawfill(pos - '1 1 0', tmp + '2 0 0', rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
712         else if((scoreboard_highlight) && (!mod(pl_number,2)))
713                 drawfill(pos - '1 1 0', tmp + '2 0 0', rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
714
715         tmp_y = 0;
716
717         for(i = 0; i < hud_num_fields; ++i)
718         {
719                 field = hud_field[i];
720                 if(field == SP_SEPARATOR)
721                         break;
722
723                 if(is_spec && field != SP_NAME && field != SP_PING) {
724                         pos_x += hud_size[i] + hud_fontsize_x;
725                         continue;
726                 }
727                 str = HUD_GetField(pl, field);
728                 str = HUD_FixScoreboardColumnWidth(i, str);
729
730                 pos_x += hud_size[i] + hud_fontsize_x;
731
732                 if(field == SP_NAME) {
733                         tmp_x = hud_size[i] - hud_fontsize_x*hud_fixscoreboardcolumnwidth_iconlen - hud_fixscoreboardcolumnwidth_marginlen + hud_fontsize_x;
734                         if (is_self)
735                                 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
736                         else
737                                 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
738                 } else {
739                         tmp_x = hud_fixscoreboardcolumnwidth_len + hud_fontsize_x;
740                         if (is_self)
741                                 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
742                         else
743                                 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
744                 }
745
746                 tmp_x = hud_size[i] + hud_fontsize_x;
747                 if(hud_field_icon0 != "")
748                         if (is_self)
749                                 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);
750                         else
751                                 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);
752                 if(hud_field_icon1 != "")
753                         if (is_self)
754                                 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);
755                         else
756                                 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);
757                 if(hud_field_icon2 != "")
758                         if (is_self)
759                                 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);
760                         else
761                                 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);
762         }
763
764         if(hud_field[i] == SP_SEPARATOR)
765         {
766                 pos_x = xmax;
767                 for(i = hud_num_fields-1; i > 0; --i)
768                 {
769                         field = hud_field[i];
770                         if(field == SP_SEPARATOR)
771                                 break;
772
773                         if(is_spec && field != SP_NAME && field != SP_PING) {
774                                 pos_x -= hud_size[i] + hud_fontsize_x;
775                                 continue;
776                         }
777
778                         str = HUD_GetField(pl, field);
779                         str = HUD_FixScoreboardColumnWidth(i, str);
780
781                         if(field == SP_NAME) {
782                                 tmp_x = hud_fixscoreboardcolumnwidth_len; // left or right aligned? let's put it right...
783                                 if(is_self)
784                                         drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
785                                 else
786                                         drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
787                         } else {
788                                 tmp_x = hud_fixscoreboardcolumnwidth_len;
789                                 if(is_self)
790                                         drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
791                                 else
792                                         drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
793                         }
794
795                         tmp_x = hud_size[i];
796                         if(hud_field_icon0 != "")
797                                 if (is_self)
798                                         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);
799                                 else
800                                         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);
801                         if(hud_field_icon1 != "")
802                                 if (is_self)
803                                         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);
804                                 else
805                                         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);
806                         if(hud_field_icon2 != "")
807                                 if (is_self)
808                                         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);
809                                 else
810                                         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);
811                         pos_x -= hud_size[i] + hud_fontsize_x;
812                 }
813         }
814 }
815
816 /*
817  * HUD_Scoreboard_MakeTable
818  *
819  * Makes a table for a team (for all playing players in DM) and fills it
820  */
821
822 vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
823 {
824         float body_table_height, i;
825         vector tmp = '0 0 0', column_dim = '0 0 0';
826         entity pl;
827
828         body_table_height = 1.25 * hud_fontsize_y * max(1, tm.team_size); // no player? show 1 empty line
829
830         pos_y += autocvar_scoreboard_border_thickness;
831         pos -= '1 1 0';
832
833         tmp_x = sbwidth + 2;
834         tmp_y = 1.25 * hud_fontsize_y;
835
836         // rounded header
837         if (teamplay)
838                 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, (rgb * autocvar_scoreboard_color_bg_team) + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
839         else
840                 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
841
842         // table border
843         tmp_y += autocvar_scoreboard_border_thickness;
844         tmp_y += body_table_height;
845         drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL); // more transparency for the scoreboard
846
847         // separator header/table
848         pos_y += 1.25 * hud_fontsize_y;
849         tmp_y = autocvar_scoreboard_border_thickness;
850         drawfill(pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
851
852         pos_y += autocvar_scoreboard_border_thickness;
853
854         // table background
855         tmp_y = body_table_height;
856         if (teamplay)
857                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
858         else
859                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
860
861         // anyway, apply some color
862         //drawfill(pos, tmp + '2 0 0', rgb, 0.1, DRAWFLAG_NORMAL);
863
864         // go back to the top to make alternated columns highlighting and to print the strings
865         pos_y -= 1.25 * hud_fontsize_y;
866         pos_y -= autocvar_scoreboard_border_thickness;
867
868         pos += '1 1 0';
869
870         if (scoreboard_highlight)
871         {
872                 column_dim_y = 1.25 * hud_fontsize_y; // header
873                 column_dim_y += autocvar_scoreboard_border_thickness;
874                 column_dim_y += body_table_height;
875         }
876
877         // print the strings of the columns headers and draw the columns
878         draw_beginBoldFont();
879         for(i = 0; i < hud_num_fields; ++i)
880         {
881                 if(hud_field[i] == SP_SEPARATOR)
882                         break;
883                 column_dim_x = hud_size[i] + hud_fontsize_x;
884                 if (scoreboard_highlight)
885                 {
886                         if (mod(i,2))
887                                 drawfill(pos - '0 1 0' - hud_fontsize_x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
888                 }
889                 drawstring(pos, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
890                 pos_x += column_dim_x;
891         }
892         if(hud_field[i] == SP_SEPARATOR)
893         {
894                 pos_x = xmax;
895                 tmp_y = 0;
896                 for(i = hud_num_fields-1; i > 0; --i)
897                 {
898                         if(hud_field[i] == SP_SEPARATOR)
899                                 break;
900
901                         pos_x -= hud_size[i];
902
903                         if (scoreboard_highlight)
904                         {
905                                 if (!mod(i,2))
906                                 {
907                                         if (i == hud_num_fields-1)
908                                                 column_dim_x = hud_size[i] + hud_fontsize_x / 2 + 1;
909                                         else
910                                                 column_dim_x = hud_size[i] + hud_fontsize_x;
911                                         drawfill(pos - '0 1 0' - hud_fontsize_x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
912                                 }
913                         }
914
915                         tmp_x = stringwidth(hud_title[i], FALSE, hud_fontsize);
916                         tmp_x = (hud_size[i] - tmp_x);
917                         drawstring(pos + tmp, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
918                         pos_x -= hud_fontsize_x;
919                 }
920         }
921         draw_endBoldFont();
922
923         pos_x = xmin;
924         pos_y += 1.25 * hud_fontsize_y; // skip the header
925         pos_y += autocvar_scoreboard_border_thickness;
926
927         // fill the table and draw the rows
928         i = 0;
929         if (teamplay)
930                 for(pl = players.sort_next; pl; pl = pl.sort_next)
931                 {
932                         if(pl.team != tm.team)
933                                 continue;
934                         HUD_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localnum), i);
935                         pos_y += 1.25 * hud_fontsize_y;
936                         ++i;
937                 }
938         else
939                 for(pl = players.sort_next; pl; pl = pl.sort_next)
940                 {
941                         if(pl.team == NUM_SPECTATOR)
942                                 continue;
943                         HUD_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localnum), i);
944                         pos_y += 1.25 * hud_fontsize_y;
945                         ++i;
946                 }
947
948         if (i == 0)
949                 pos_y += 1.25 * hud_fontsize_y; // move to the end of the table
950         pos_y += 1.25 * hud_fontsize_y; // move empty row (out of the table)
951
952         return pos;
953 }
954
955 float HUD_WouldDrawScoreboard() {
956         if (autocvar__hud_configure)
957                 return 0;
958         else if (scoreboard_showscores)
959                 return 1;
960         else if (intermission == 1)
961                 return 1;
962         else if (intermission == 2)
963                 return 0;
964         else if (spectatee_status != -1 && getstati(STAT_HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS)
965                 return 1;
966         else if (scoreboard_showscores_force)
967                 return 1;
968         return 0;
969 }
970
971 float g_minstagib;
972 float average_accuracy;
973 vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
974 {
975         float i;
976         float weapon_cnt = WEP_COUNT - 3; // either minstanex/nex are hidden, no port-o-launch, no tuba
977         float rows;
978         if(autocvar_scoreboard_accuracy_doublerows)
979                 rows = 2;
980         else
981                 rows = 1;
982         float height = 40;
983         float fontsize = height * 1/3;
984         float weapon_height = height * 2/3;
985         float weapon_width = sbwidth / weapon_cnt;
986
987         drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
988         pos_y += 1.25 * hud_fontsize_y + autocvar_scoreboard_border_thickness;
989         vector tmp = '0 0 0';
990         tmp_x = sbwidth;
991         tmp_y = height * rows;
992
993         if (teamplay)
994                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
995         else
996                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
997         drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
998
999         // column highlighting
1000         for(i = 0; i < weapon_cnt/rows; ++i)
1001         {
1002                 if(!mod(i, 2))
1003                         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);
1004         }
1005
1006         // row highlighting
1007         for(i = 0; i < rows; ++i)
1008         {
1009                 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);
1010         }
1011
1012         average_accuracy = 0;
1013         float weapons_with_stats;
1014         weapons_with_stats = 0;
1015         if(rows == 2)
1016                 pos_x += weapon_width / 2;
1017
1018         if(switchweapon == WEP_MINSTANEX)
1019                 g_minstagib = 1; // TODO: real detection for minstagib?
1020
1021         float weapon_stats;
1022         if(autocvar_scoreboard_accuracy_nocolors)
1023                 rgb = '1 1 1';
1024         else
1025                 Accuracy_LoadColors();
1026
1027         for(i = WEP_FIRST; i <= WEP_LAST; ++i)
1028         {
1029                 self = get_weaponinfo(i);
1030                 if not(self.weapon)
1031                         continue;
1032                 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
1033                         continue;
1034                 weapon_stats = weapon_accuracy[i-WEP_FIRST];
1035
1036                 float weapon_alpha;
1037                 if(weapon_stats >= 0)
1038                         weapon_alpha = scoreboard_alpha_fg;
1039                 else
1040                         weapon_alpha = 0.2 * scoreboard_alpha_fg;
1041
1042                 // weapon icon
1043                 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);
1044                 // the accuracy
1045                 if(weapon_stats >= 0) {
1046                         weapons_with_stats += 1;
1047                         average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1048
1049                         string s;
1050                         s = sprintf(_("%d%%"), weapon_stats*100);
1051
1052                         float padding;
1053                         padding = (weapon_width - stringwidth(s, FALSE, '1 0 0' * fontsize)) / 2; // center the accuracy value
1054
1055                         if(!autocvar_scoreboard_accuracy_nocolors)
1056                                 rgb = Accuracy_GetColor(weapon_stats);
1057
1058                         drawstring(pos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1059                 }
1060                 pos_x += weapon_width * rows;
1061                 if(rows == 2 && i == 6) {
1062                         pos_x -= sbwidth;
1063                         pos_y += height;
1064                 }
1065         }
1066
1067         if(weapons_with_stats)
1068                 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1069
1070         if(rows == 2)
1071                 pos_x -= weapon_width / 2;
1072         pos_x -= sbwidth;
1073         pos_y += height;
1074
1075         pos_y +=  1.25 * hud_fontsize_y;
1076         return pos;
1077 }
1078
1079 vector HUD_DrawKeyValue(vector pos, string key, string value) {
1080         float px = pos_x;
1081         pos_x += hud_fontsize_x * 0.25;
1082         drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1083         pos_x = xmax - stringwidth(value, FALSE, hud_fontsize) - hud_fontsize_x * 0.25;
1084         drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1085         pos_x = px;
1086         pos_y+= hud_fontsize_y;
1087         
1088         return pos;
1089 }
1090
1091 vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
1092         float stat_secrets_found, stat_secrets_total;
1093         float rows;
1094         string val;
1095
1096         // get secrets stats
1097         stat_secrets_found = getstatf(STAT_SECRETS_FOUND);
1098         stat_secrets_total = getstatf(STAT_SECRETS_TOTAL);
1099
1100         // get number of rows
1101         rows = (stat_secrets_total ? 1 : 0);
1102
1103         // if no rows, return
1104         if not(rows)
1105                 return pos;
1106
1107         //  draw table header
1108         drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1109         pos_y += 1.25 * hud_fontsize_y + autocvar_scoreboard_border_thickness;
1110         
1111         // draw table   
1112         vector tmp = '0 0 0';
1113         tmp_x = sbwidth;
1114         tmp_y = hud_fontsize_y * rows;
1115
1116         if (teamplay)
1117                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1118         else
1119                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1120         drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1121
1122         // draw secrets
1123         val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1124         pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
1125         
1126         // update position
1127         pos_y += 1.25 * hud_fontsize_y;
1128         return pos;
1129 }
1130
1131
1132 vector HUD_DrawScoreboardRankings(vector pos, entity pl,  vector rgb, vector bg_size)
1133 {
1134         float i;
1135         RANKINGS_RECEIVED_CNT = 0;
1136         for (i=RANKINGS_CNT-1; i>=0; --i)
1137                 if (grecordtime[i])
1138                         ++RANKINGS_RECEIVED_CNT;
1139
1140         if (RANKINGS_RECEIVED_CNT == 0)
1141                 return pos;
1142
1143         float is_spec;
1144         is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
1145         vector hl_rgb;
1146         hl_rgb_x = autocvar_scoreboard_color_bg_r + 0.5;
1147         hl_rgb_y = autocvar_scoreboard_color_bg_g + 0.5;
1148         hl_rgb_z = autocvar_scoreboard_color_bg_b + 0.5;
1149
1150         pos_y += hud_fontsize_y;
1151         drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1152         pos_y += hud_fontsize_y + autocvar_scoreboard_border_thickness;
1153         vector tmp = '0 0 0';
1154         tmp_x = sbwidth;
1155         tmp_y = 1.25 * hud_fontsize_y * RANKINGS_RECEIVED_CNT;
1156
1157         if (teamplay)
1158                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1159         else
1160                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1161         drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1162
1163         // row highlighting
1164         for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1165         {
1166                 string n, p;
1167                 float t;
1168                 t = grecordtime[i];
1169                 if (t == 0)
1170                         continue;
1171                 n = grecordholder[i];
1172                 p = count_ordinal(i+1);
1173                 if(grecordholder[i] == GetPlayerName(player_localnum))
1174                         drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize_y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1175                 else if(!mod(i, 2) && scoreboard_highlight)
1176                         drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize_y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1177                 drawstring(pos, p, '1 1 0' * hud_fontsize_y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1178                 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);
1179                 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize_y, n, '1 1 0' * hud_fontsize_y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1180                 pos_y += 1.25 * hud_fontsize_y;
1181         }
1182         pos_y += autocvar_scoreboard_border_thickness;
1183
1184         return pos;
1185 }
1186
1187 float hud_woulddrawscoreboard_prev;
1188 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1189 void HUD_DrawScoreboard()
1190 {
1191         float hud_woulddrawscoreboard;
1192         hud_woulddrawscoreboard = scoreboard_active;
1193         if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1194                 hud_woulddrawscoreboard_change = time;
1195                 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1196         }
1197
1198         if(hud_woulddrawscoreboard) {
1199                 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1200                 if (scoreboard_fadeinspeed)
1201                         scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1202                 else
1203                         scoreboard_fade_alpha = 1;
1204         }
1205         else {
1206                 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1207                 if (scoreboard_fadeoutspeed)
1208                         scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1209                 else
1210                         scoreboard_fade_alpha = 0;
1211         }
1212
1213         if not(scoreboard_fade_alpha)
1214                 return;
1215
1216         HUD_UpdatePlayerTeams();
1217
1218         scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1219         scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1220         scoreboard_highlight = autocvar_scoreboard_highlight;
1221         scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1222         scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1223         scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1224         scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1225
1226         vector rgb, pos, tmp;
1227         entity pl, tm;
1228         string str;
1229
1230         xmin = (autocvar_scoreboard_offset_left * vid_conwidth);
1231         ymin = max((autocvar_con_notify * autocvar_con_notifysize), (autocvar_scoreboard_offset_vertical * vid_conwidth));
1232
1233         xmax = ((1 - autocvar_scoreboard_offset_right) * vid_conwidth);
1234         ymax = (vid_conheight - ymin);
1235
1236         sbwidth = xmax - xmin;
1237
1238         // Initializes position
1239         pos_x = xmin;
1240         pos_y = ymin;
1241         pos_z = 0;
1242
1243         // Heading
1244         vector sb_heading_fontsize;
1245         sb_heading_fontsize = hud_fontsize * 2;
1246         draw_beginBoldFont();
1247         drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1248         draw_endBoldFont();
1249
1250         pos_y += sb_heading_fontsize_y + hud_fontsize_y * 0.25;
1251
1252         // Draw the scoreboard
1253         vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * autocvar_scoreboard_bg_scale;
1254
1255         if(teamplay)
1256         {
1257                 vector team_score_baseoffset;
1258                 team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize_y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize_x * 0.25);
1259                 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1260                 {
1261                         if(tm.team == NUM_SPECTATOR)
1262                                 continue;
1263
1264                         draw_beginBoldFont();
1265                         rgb = Team_ColorRGB(tm.team);
1266                         str = ftos(tm.(teamscores[ts_primary]));
1267                         drawstring(pos + team_score_baseoffset - eX * stringwidth(str, FALSE, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1268
1269                         if(ts_primary != ts_secondary)
1270                         {
1271                                 str = ftos(tm.(teamscores[ts_secondary]));
1272                                 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);
1273                         }
1274                         draw_endBoldFont();
1275
1276                         pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1277                 }
1278
1279 #ifdef GMQCC
1280                 rgb = '0 0 0';
1281 #endif
1282                 rgb_x = autocvar_scoreboard_color_bg_r;
1283                 rgb_y = autocvar_scoreboard_color_bg_g;
1284                 rgb_z = autocvar_scoreboard_color_bg_b;
1285         }
1286         else
1287         {
1288 #ifdef GMQCC
1289                 rgb = '0 0 0';
1290 #endif
1291                 rgb_x = autocvar_scoreboard_color_bg_r;
1292                 rgb_y = autocvar_scoreboard_color_bg_g;
1293                 rgb_z = autocvar_scoreboard_color_bg_b;
1294
1295                 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1296                 {
1297                         if(tm.team == NUM_SPECTATOR)
1298                                 continue;
1299
1300                         pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1301                 }
1302         }
1303
1304         if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1305                 if(race_speedaward) {
1306                         drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1307                         pos_y += 1.25 * hud_fontsize_y;
1308                 }
1309                 if(race_speedaward_alltimebest) {
1310                         drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1311                         pos_y += 1.25 * hud_fontsize_y;
1312                 }
1313                 pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
1314         }
1315         else if(autocvar_scoreboard_accuracy && spectatee_status != -1 && !warmup_stage) {
1316                 if(teamplay)
1317                         pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
1318                 else
1319                         pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1320         }
1321
1322                 
1323         if(teamplay)
1324                 pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
1325         else
1326                 pos = HUD_DrawMapStats(pos, rgb, bg_size);
1327
1328         // List spectators
1329         float specs;
1330         specs = 0;
1331         tmp = pos;
1332         for(pl = players.sort_next; pl; pl = pl.sort_next)
1333         {
1334                 if(pl.team != NUM_SPECTATOR)
1335                         continue;
1336                 pos_y += 1.25 * hud_fontsize_y;
1337                 HUD_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localnum), specs);
1338                 ++specs;
1339         }
1340
1341         if(specs)
1342         {
1343                 draw_beginBoldFont();
1344                 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1345                 draw_endBoldFont();
1346                 pos_y += 1.25 * hud_fontsize_y;
1347         }
1348
1349         // Print info string
1350         float tl, fl, ll;
1351         str = sprintf(_("playing on ^2%s^7"), shortmapname);
1352         tl = getstatf(STAT_TIMELIMIT);
1353         fl = getstatf(STAT_FRAGLIMIT);
1354         ll = getstatf(STAT_LEADLIMIT);
1355         if(gametype == MAPINFO_TYPE_LMS)
1356         {
1357                 if(tl > 0)
1358                         str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1359         }
1360         else
1361         {
1362                 if(tl > 0)
1363                         str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1364                 if(fl > 0)
1365                 {
1366                         if(tl > 0)
1367                                 str = strcat(str, _(" or"));
1368                         if(teamplay)
1369                         {
1370                                 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl), 
1371                                         (teamscores_label[ts_primary] == "score")   ? CTX(_("SCO^points")) :
1372                                         (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1373                                         TranslateScoresLabel(teamscores_label[ts_primary])));
1374                         }
1375                         else
1376                         {
1377                                 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl), 
1378                                         (scores_label[ps_primary] == "score")   ? CTX(_("SCO^points")) :
1379                                         (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1380                                         TranslateScoresLabel(scores_label[ps_primary])));
1381                         }
1382                 }
1383                 if(ll > 0)
1384                 {
1385                         if(tl > 0 || fl > 0)
1386                                 str = strcat(str, _(" or"));
1387                         if(teamplay)
1388                         {
1389                                 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll), 
1390                                         (teamscores_label[ts_primary] == "score")   ? CTX(_("SCO^points")) :
1391                                         (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1392                                         TranslateScoresLabel(teamscores_label[ts_primary])));
1393                         }
1394                         else
1395                         {
1396                                 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll), 
1397                                         (scores_label[ps_primary] == "score")   ? CTX(_("SCO^points")) :
1398                                         (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1399                                         TranslateScoresLabel(scores_label[ps_primary])));
1400                         }
1401                 }
1402         }
1403
1404         pos_y += 1.2 * hud_fontsize_y;
1405         drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, TRUE, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1406
1407         // print information about respawn status
1408         float respawn_time = getstatf(STAT_RESPAWN_TIME);
1409         if(respawn_time)
1410         {
1411                 if(respawn_time < 0)
1412                 {
1413                         // a negative number means we are awaiting respawn, time value is still the same
1414                         respawn_time *= -1; // remove mark now that we checked it
1415                         respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
1416                         
1417                         str = sprintf(_("^1Respawning in ^3%s^1..."),
1418                                 (autocvar_scoreboard_respawntime_decimals ?
1419                                         count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1420                                         :
1421                                         count_seconds(respawn_time - time)
1422                                 )
1423                         );
1424                 }
1425                 else if(time < respawn_time)
1426                 {
1427                         str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
1428                                 (autocvar_scoreboard_respawntime_decimals ?
1429                                         count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1430                                         :
1431                                         count_seconds(respawn_time - time)
1432                                 )
1433                         );
1434                 }
1435                 else if(time >= respawn_time)
1436                         str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
1437
1438                 pos_y += 1.2 * hud_fontsize_y;
1439                 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, TRUE, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1440         }
1441
1442         scoreboard_bottom = pos_y + 2 * hud_fontsize_y;
1443 }