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