]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/client/scoreboard.qc
0d2cd934ead26dcaa799f65ff1310267059d0da8
[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 (!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 (!nocomplain)
413                                         printf("^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                         printf("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                         printf("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 (!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 (!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 (HUD_QuickMenu_IsOpened())
956                 return 0;
957         else if (scoreboard_showscores)
958                 return 1;
959         else if (intermission == 1)
960                 return 1;
961         else if (intermission == 2)
962                 return 0;
963         else if (spectatee_status != -1 && getstati(STAT_HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS)
964                 return 1;
965         else if (scoreboard_showscores_force)
966                 return 1;
967         return 0;
968 }
969
970 float average_accuracy;
971 vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
972 {
973         float i;
974         float weapon_cnt = WEP_COUNT - 3; // either minstanex/nex are hidden, no port-o-launch, no tuba
975         float rows;
976         if(autocvar_scoreboard_accuracy_doublerows)
977                 rows = 2;
978         else
979                 rows = 1;
980         float height = 40;
981         float fontsize = height * 1/3;
982         float weapon_height = height * 2/3;
983         float weapon_width = sbwidth / weapon_cnt;
984         float g_minstagib = 0;
985
986         drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
987         pos_y += 1.25 * hud_fontsize_y + autocvar_scoreboard_border_thickness;
988         vector tmp = '0 0 0';
989         tmp_x = sbwidth;
990         tmp_y = height * rows;
991
992         if (teamplay)
993                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
994         else
995                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
996         drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
997
998         // column highlighting
999         for(i = 0; i < weapon_cnt/rows; ++i)
1000         {
1001                 if(!mod(i, 2))
1002                         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);
1003         }
1004
1005         // row highlighting
1006         for(i = 0; i < rows; ++i)
1007         {
1008                 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);
1009         }
1010
1011         average_accuracy = 0;
1012         float weapons_with_stats;
1013         weapons_with_stats = 0;
1014         if(rows == 2)
1015                 pos_x += weapon_width / 2;
1016
1017         if(switchweapon == WEP_MINSTANEX)
1018                 g_minstagib = 1; // TODO: real detection for minstagib?
1019
1020         float weapon_stats;
1021         if(autocvar_scoreboard_accuracy_nocolors)
1022                 rgb = '1 1 1';
1023         else
1024                 Accuracy_LoadColors();
1025
1026         for(i = WEP_FIRST; i <= WEP_LAST; ++i)
1027         {
1028                 self = get_weaponinfo(i);
1029                 if (!self.weapon)
1030                         continue;
1031                 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
1032                         continue;
1033                 weapon_stats = weapon_accuracy[i-WEP_FIRST];
1034
1035                 float weapon_alpha;
1036                 if(weapon_stats >= 0)
1037                         weapon_alpha = scoreboard_alpha_fg;
1038                 else
1039                         weapon_alpha = 0.2 * scoreboard_alpha_fg;
1040
1041                 // weapon icon
1042                 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);
1043                 // the accuracy
1044                 if(weapon_stats >= 0) {
1045                         weapons_with_stats += 1;
1046                         average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1047
1048                         string s;
1049                         s = sprintf(_("%d%%"), weapon_stats*100);
1050
1051                         float padding;
1052                         padding = (weapon_width - stringwidth(s, FALSE, '1 0 0' * fontsize)) / 2; // center the accuracy value
1053
1054                         if(!autocvar_scoreboard_accuracy_nocolors)
1055                                 rgb = Accuracy_GetColor(weapon_stats);
1056
1057                         drawstring(pos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1058                 }
1059                 pos_x += weapon_width * rows;
1060                 if(rows == 2 && i == 6) {
1061                         pos_x -= sbwidth;
1062                         pos_y += height;
1063                 }
1064         }
1065
1066         if(weapons_with_stats)
1067                 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1068
1069         if(rows == 2)
1070                 pos_x -= weapon_width / 2;
1071         pos_x -= sbwidth;
1072         pos_y += height;
1073
1074         pos_y +=  1.25 * hud_fontsize_y;
1075         return pos;
1076 }
1077
1078 vector HUD_DrawKeyValue(vector pos, string key, string value) {
1079         float px = pos_x;
1080         pos_x += hud_fontsize_x * 0.25;
1081         drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1082         pos_x = xmax - stringwidth(value, FALSE, hud_fontsize) - hud_fontsize_x * 0.25;
1083         drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1084         pos_x = px;
1085         pos_y+= hud_fontsize_y;
1086
1087         return pos;
1088 }
1089
1090 vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
1091         float stat_secrets_found, stat_secrets_total;
1092         float rows;
1093         string val;
1094
1095         // get secrets stats
1096         stat_secrets_found = getstatf(STAT_SECRETS_FOUND);
1097         stat_secrets_total = getstatf(STAT_SECRETS_TOTAL);
1098
1099         // get number of rows
1100         rows = (stat_secrets_total ? 1 : 0);
1101
1102         // if no rows, return
1103         if (!rows)
1104                 return pos;
1105
1106         //  draw table header
1107         drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1108         pos_y += 1.25 * hud_fontsize_y + autocvar_scoreboard_border_thickness;
1109
1110         // draw table
1111         vector tmp = '0 0 0';
1112         tmp_x = sbwidth;
1113         tmp_y = hud_fontsize_y * rows;
1114
1115         if (teamplay)
1116                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1117         else
1118                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1119         drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1120
1121         // draw secrets
1122         val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1123         pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
1124
1125         // update position
1126         pos_y += 1.25 * hud_fontsize_y;
1127         return pos;
1128 }
1129
1130
1131 vector HUD_DrawScoreboardRankings(vector pos, entity pl,  vector rgb, vector bg_size)
1132 {
1133         float i;
1134         RANKINGS_RECEIVED_CNT = 0;
1135         for (i=RANKINGS_CNT-1; i>=0; --i)
1136                 if (grecordtime[i])
1137                         ++RANKINGS_RECEIVED_CNT;
1138
1139         if (RANKINGS_RECEIVED_CNT == 0)
1140                 return pos;
1141
1142         float is_spec;
1143         is_spec = (GetPlayerColor(pl.sv_entnum) == NUM_SPECTATOR);
1144         vector hl_rgb;
1145         hl_rgb_x = autocvar_scoreboard_color_bg_r + 0.5;
1146         hl_rgb_y = autocvar_scoreboard_color_bg_g + 0.5;
1147         hl_rgb_z = autocvar_scoreboard_color_bg_b + 0.5;
1148
1149         pos_y += hud_fontsize_y;
1150         drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1151         pos_y += hud_fontsize_y + autocvar_scoreboard_border_thickness;
1152         vector tmp = '0 0 0';
1153         tmp_x = sbwidth;
1154         tmp_y = 1.25 * hud_fontsize_y * RANKINGS_RECEIVED_CNT;
1155
1156         if (teamplay)
1157                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1158         else
1159                 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1160         drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1161
1162         // row highlighting
1163         for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1164         {
1165                 string n, p;
1166                 float t;
1167                 t = grecordtime[i];
1168                 if (t == 0)
1169                         continue;
1170                 n = grecordholder[i];
1171                 p = count_ordinal(i+1);
1172                 if(grecordholder[i] == GetPlayerName(player_localnum))
1173                         drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize_y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1174                 else if(!mod(i, 2) && scoreboard_highlight)
1175                         drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize_y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1176                 drawstring(pos, p, '1 1 0' * hud_fontsize_y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1177                 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);
1178                 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize_y, n, '1 1 0' * hud_fontsize_y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1179                 pos_y += 1.25 * hud_fontsize_y;
1180         }
1181         pos_y += autocvar_scoreboard_border_thickness;
1182
1183         return pos;
1184 }
1185
1186 float hud_woulddrawscoreboard_prev;
1187 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1188 void HUD_DrawScoreboard()
1189 {
1190         float hud_woulddrawscoreboard;
1191         hud_woulddrawscoreboard = scoreboard_active;
1192         if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1193                 hud_woulddrawscoreboard_change = time;
1194                 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1195         }
1196
1197         if(hud_woulddrawscoreboard) {
1198                 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1199                 if (scoreboard_fadeinspeed)
1200                         scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1201                 else
1202                         scoreboard_fade_alpha = 1;
1203         }
1204         else {
1205                 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1206                 if (scoreboard_fadeoutspeed)
1207                         scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1208                 else
1209                         scoreboard_fade_alpha = 0;
1210         }
1211
1212         if (!scoreboard_fade_alpha)
1213                 return;
1214
1215         HUD_UpdatePlayerTeams();
1216
1217         scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1218         scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1219         scoreboard_highlight = autocvar_scoreboard_highlight;
1220         scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * scoreboard_alpha_fg;
1221         scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * scoreboard_alpha_fg;
1222         scoreboard_alpha_name = autocvar_scoreboard_alpha_name * scoreboard_alpha_fg;
1223         scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * scoreboard_alpha_fg;
1224
1225         vector rgb, pos, tmp;
1226         entity pl, tm;
1227         string str;
1228
1229         xmin = (autocvar_scoreboard_offset_left * vid_conwidth);
1230         ymin = max((autocvar_con_notify * autocvar_con_notifysize), (autocvar_scoreboard_offset_vertical * vid_conwidth));
1231
1232         xmax = ((1 - autocvar_scoreboard_offset_right) * vid_conwidth);
1233         ymax = (vid_conheight - ymin);
1234
1235         sbwidth = xmax - xmin;
1236
1237         // Initializes position
1238         pos_x = xmin;
1239         pos_y = ymin;
1240         pos_z = 0;
1241
1242         // Heading
1243         vector sb_heading_fontsize;
1244         sb_heading_fontsize = hud_fontsize * 2;
1245         draw_beginBoldFont();
1246         drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1247         draw_endBoldFont();
1248
1249         pos_y += sb_heading_fontsize_y + hud_fontsize_y * 0.25;
1250
1251         // Draw the scoreboard
1252         vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * autocvar_scoreboard_bg_scale;
1253
1254         if(teamplay)
1255         {
1256                 vector team_score_baseoffset;
1257                 team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize_y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize_x * 0.25);
1258                 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1259                 {
1260                         if(tm.team == NUM_SPECTATOR)
1261                                 continue;
1262
1263                         draw_beginBoldFont();
1264                         rgb = Team_ColorRGB(tm.team);
1265                         str = ftos(tm.(teamscores[ts_primary]));
1266                         drawstring(pos + team_score_baseoffset - eX * stringwidth(str, FALSE, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1267
1268                         if(ts_primary != ts_secondary)
1269                         {
1270                                 str = ftos(tm.(teamscores[ts_secondary]));
1271                                 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);
1272                         }
1273                         draw_endBoldFont();
1274
1275                         pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1276                 }
1277
1278 #ifdef GMQCC
1279                 rgb = '0 0 0';
1280 #endif
1281                 rgb_x = autocvar_scoreboard_color_bg_r;
1282                 rgb_y = autocvar_scoreboard_color_bg_g;
1283                 rgb_z = autocvar_scoreboard_color_bg_b;
1284         }
1285         else
1286         {
1287 #ifdef GMQCC
1288                 rgb = '0 0 0';
1289 #endif
1290                 rgb_x = autocvar_scoreboard_color_bg_r;
1291                 rgb_y = autocvar_scoreboard_color_bg_g;
1292                 rgb_z = autocvar_scoreboard_color_bg_b;
1293
1294                 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1295                 {
1296                         if(tm.team == NUM_SPECTATOR)
1297                                 continue;
1298
1299                         pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1300                 }
1301         }
1302
1303         if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1304                 if(race_speedaward) {
1305                         drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1306                         pos_y += 1.25 * hud_fontsize_y;
1307                 }
1308                 if(race_speedaward_alltimebest) {
1309                         drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1310                         pos_y += 1.25 * hud_fontsize_y;
1311                 }
1312                 pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
1313         }
1314         else if(autocvar_scoreboard_accuracy && spectatee_status != -1 && !warmup_stage) {
1315                 if(teamplay)
1316                         pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
1317                 else
1318                         pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1319         }
1320
1321
1322         if(teamplay)
1323                 pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
1324         else
1325                 pos = HUD_DrawMapStats(pos, rgb, bg_size);
1326
1327         // List spectators
1328         float specs;
1329         specs = 0;
1330         tmp = pos;
1331         for(pl = players.sort_next; pl; pl = pl.sort_next)
1332         {
1333                 if(pl.team != NUM_SPECTATOR)
1334                         continue;
1335                 pos_y += 1.25 * hud_fontsize_y;
1336                 HUD_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localnum), specs);
1337                 ++specs;
1338         }
1339
1340         if(specs)
1341         {
1342                 draw_beginBoldFont();
1343                 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1344                 draw_endBoldFont();
1345                 pos_y += 1.25 * hud_fontsize_y;
1346         }
1347
1348         // Print info string
1349         float tl, fl, ll;
1350         str = sprintf(_("playing on ^2%s^7"), shortmapname);
1351         tl = getstatf(STAT_TIMELIMIT);
1352         fl = getstatf(STAT_FRAGLIMIT);
1353         ll = getstatf(STAT_LEADLIMIT);
1354         if(gametype == MAPINFO_TYPE_LMS)
1355         {
1356                 if(tl > 0)
1357                         str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1358         }
1359         else
1360         {
1361                 if(tl > 0)
1362                         str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1363                 if(fl > 0)
1364                 {
1365                         if(tl > 0)
1366                                 str = strcat(str, _(" or"));
1367                         if(teamplay)
1368                         {
1369                                 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl),
1370                                         (teamscores_label[ts_primary] == "score")   ? CTX(_("SCO^points")) :
1371                                         (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1372                                         TranslateScoresLabel(teamscores_label[ts_primary])));
1373                         }
1374                         else
1375                         {
1376                                 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl),
1377                                         (scores_label[ps_primary] == "score")   ? CTX(_("SCO^points")) :
1378                                         (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1379                                         TranslateScoresLabel(scores_label[ps_primary])));
1380                         }
1381                 }
1382                 if(ll > 0)
1383                 {
1384                         if(tl > 0 || fl > 0)
1385                                 str = strcat(str, _(" or"));
1386                         if(teamplay)
1387                         {
1388                                 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll),
1389                                         (teamscores_label[ts_primary] == "score")   ? CTX(_("SCO^points")) :
1390                                         (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1391                                         TranslateScoresLabel(teamscores_label[ts_primary])));
1392                         }
1393                         else
1394                         {
1395                                 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll),
1396                                         (scores_label[ps_primary] == "score")   ? CTX(_("SCO^points")) :
1397                                         (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1398                                         TranslateScoresLabel(scores_label[ps_primary])));
1399                         }
1400                 }
1401         }
1402
1403         pos_y += 1.2 * hud_fontsize_y;
1404         drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, TRUE, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1405
1406         // print information about respawn status
1407         float respawn_time = getstatf(STAT_RESPAWN_TIME);
1408         if(respawn_time)
1409         {
1410                 if(respawn_time < 0)
1411                 {
1412                         // a negative number means we are awaiting respawn, time value is still the same
1413                         respawn_time *= -1; // remove mark now that we checked it
1414                         respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
1415
1416                         str = sprintf(_("^1Respawning in ^3%s^1..."),
1417                                 (autocvar_scoreboard_respawntime_decimals ?
1418                                         count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1419                                         :
1420                                         count_seconds(respawn_time - time)
1421                                 )
1422                         );
1423                 }
1424                 else if(time < respawn_time)
1425                 {
1426                         str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
1427                                 (autocvar_scoreboard_respawntime_decimals ?
1428                                         count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1429                                         :
1430                                         count_seconds(respawn_time - time)
1431                                 )
1432                         );
1433                 }
1434                 else if(time >= respawn_time)
1435                         str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
1436
1437                 pos_y += 1.2 * hud_fontsize_y;
1438                 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, TRUE, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1439         }
1440
1441         scoreboard_bottom = pos_y + 2 * hud_fontsize_y;
1442 }