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