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