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