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