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