1 #include "scoreboard.qh"
3 #include "quickmenu.qh"
4 #include <common/ent_cs.qh>
5 #include <common/constants.qh>
6 #include <common/mapinfo.qh>
7 #include <common/minigames/cl_minigames.qh>
8 #include <common/stats.qh>
9 #include <common/teams.qh>
11 float scoreboard_alpha_bg;
12 float scoreboard_alpha_fg;
13 float scoreboard_highlight;
14 float scoreboard_highlight_alpha;
15 float scoreboard_highlight_alpha_self;
16 float scoreboard_alpha_name;
17 float scoreboard_alpha_name_self;
19 void drawstringright(vector, string, vector, vector, float, float);
20 void drawstringcenter(vector, string, vector, vector, float, float);
22 const float SCOREBOARD_OFFSET = 50;
24 // wrapper to put all possible scores titles through gettext
25 string TranslateScoresLabel(string l)
29 case "bckills": return CTX(_("SCO^bckills"));
30 case "bctime": return CTX(_("SCO^bctime"));
31 case "caps": return CTX(_("SCO^caps"));
32 case "captime": return CTX(_("SCO^captime"));
33 case "deaths": return CTX(_("SCO^deaths"));
34 case "destroyed": return CTX(_("SCO^destroyed"));
35 case "dmg": return CTX(_("SCO^dmg"));
36 case "dmgtaken": return CTX(_("SCO^dmgtaken"));
37 case "drops": return CTX(_("SCO^drops"));
38 case "faults": return CTX(_("SCO^faults"));
39 case "fckills": return CTX(_("SCO^fckills"));
40 case "goals": return CTX(_("SCO^goals"));
41 case "kckills": return CTX(_("SCO^kckills"));
42 case "kdratio": return CTX(_("SCO^kdratio"));
43 case "k/d": return CTX(_("SCO^k/d"));
44 case "kd": return CTX(_("SCO^kd"));
45 case "kdr": return CTX(_("SCO^kdr"));
46 case "kills": return CTX(_("SCO^kills"));
47 case "laps": return CTX(_("SCO^laps"));
48 case "lives": return CTX(_("SCO^lives"));
49 case "losses": return CTX(_("SCO^losses"));
50 case "name": return CTX(_("SCO^name"));
51 case "sum": return CTX(_("SCO^sum"));
52 case "nick": return CTX(_("SCO^nick"));
53 case "objectives": return CTX(_("SCO^objectives"));
54 case "pickups": return CTX(_("SCO^pickups"));
55 case "ping": return CTX(_("SCO^ping"));
56 case "pl": return CTX(_("SCO^pl"));
57 case "pushes": return CTX(_("SCO^pushes"));
58 case "rank": return CTX(_("SCO^rank"));
59 case "returns": return CTX(_("SCO^returns"));
60 case "revivals": return CTX(_("SCO^revivals"));
61 case "score": return CTX(_("SCO^score"));
62 case "suicides": return CTX(_("SCO^suicides"));
63 case "takes": return CTX(_("SCO^takes"));
64 case "ticks": return CTX(_("SCO^ticks"));
73 ps_primary = ps_secondary = ts_primary = ts_secondary = -1;
74 for(i = 0; i < MAX_SCORE; ++i)
76 f = (scores_flags[i] & SFL_SORT_PRIO_MASK);
77 if(f == SFL_SORT_PRIO_PRIMARY)
79 if(f == SFL_SORT_PRIO_SECONDARY)
82 if(ps_secondary == -1)
83 ps_secondary = ps_primary;
85 for(i = 0; i < MAX_TEAMSCORE; ++i)
87 f = (teamscores_flags[i] & SFL_SORT_PRIO_MASK);
88 if(f == SFL_SORT_PRIO_PRIMARY)
90 if(f == SFL_SORT_PRIO_SECONDARY)
93 if(ts_secondary == -1)
94 ts_secondary = ts_primary;
99 float SetTeam(entity pl, float Team);
101 void HUD_UpdatePlayerTeams()
108 for(pl = players.sort_next; pl; pl = pl.sort_next)
111 Team = entcs_GetScoreTeam(pl.sv_entnum);
112 if(SetTeam(pl, Team))
115 HUD_UpdatePlayerPos(pl);
119 pl = players.sort_next;
124 print(strcat("PNUM: ", ftos(num), "\n"));
129 int HUD_CompareScore(int vl, int vr, int f)
131 TC(int, vl); TC(int, vr); TC(int, f);
132 if(f & SFL_ZERO_IS_WORST)
134 if(vl == 0 && vr != 0)
136 if(vl != 0 && vr == 0)
140 return IS_INCREASING(f);
142 return IS_DECREASING(f);
146 float HUD_ComparePlayerScores(entity left, entity right)
149 vl = entcs_GetTeam(left.sv_entnum);
150 vr = entcs_GetTeam(right.sv_entnum);
162 if(vl == NUM_SPECTATOR)
164 // FIRST the one with scores (spectators), THEN the ones without (downloaders)
166 if(!left.gotscores && right.gotscores)
171 r = HUD_CompareScore(left.scores[ps_primary], right.scores[ps_primary], scores_flags[ps_primary]);
175 r = HUD_CompareScore(left.scores[ps_secondary], right.scores[ps_secondary], scores_flags[ps_secondary]);
180 for(i = 0; i < MAX_SCORE; ++i)
182 r = HUD_CompareScore(left.scores[i], right.scores[i], scores_flags[i]);
187 if (left.sv_entnum < right.sv_entnum)
193 void HUD_UpdatePlayerPos(entity player)
196 for(ent = player.sort_next; ent && HUD_ComparePlayerScores(player, ent); ent = player.sort_next)
198 SORT_SWAP(player, ent);
200 for(ent = player.sort_prev; ent != players && HUD_ComparePlayerScores(ent, player); ent = player.sort_prev)
202 SORT_SWAP(ent, player);
206 float HUD_CompareTeamScores(entity left, entity right)
210 if(left.team == NUM_SPECTATOR)
212 if(right.team == NUM_SPECTATOR)
215 r = HUD_CompareScore(left.teamscores[ts_primary], right.teamscores[ts_primary], teamscores_flags[ts_primary]);
219 r = HUD_CompareScore(left.teamscores[ts_secondary], right.teamscores[ts_secondary], teamscores_flags[ts_secondary]);
223 for(i = 0; i < MAX_SCORE; ++i)
225 r = HUD_CompareScore(left.teamscores[i], right.teamscores[i], teamscores_flags[i]);
230 if (left.team < right.team)
236 void HUD_UpdateTeamPos(entity Team)
239 for(ent = Team.sort_next; ent && HUD_CompareTeamScores(Team, ent); ent = Team.sort_next)
241 SORT_SWAP(Team, ent);
243 for(ent = Team.sort_prev; ent != teams && HUD_CompareTeamScores(ent, Team); ent = Team.sort_prev)
245 SORT_SWAP(ent, Team);
251 LOG_INFO(_("You can modify the scoreboard using the ^2scoreboard_columns_set command.\n"));
252 LOG_INFO(_("^3|---------------------------------------------------------------|\n"));
253 LOG_INFO(_("Usage:\n"));
254 LOG_INFO(_("^2scoreboard_columns_set default\n"));
255 LOG_INFO(_("^2scoreboard_columns_set ^7field1 field2 ...\n"));
256 LOG_INFO(_("The following field names are recognized (case insensitive):\n"));
257 LOG_INFO(_("You can use a ^3|^7 to start the right-aligned fields.\n\n"));
259 LOG_INFO(_("^3name^7 or ^3nick^7 Name of a player\n"));
260 LOG_INFO(_("^3ping^7 Ping time\n"));
261 LOG_INFO(_("^3pl^7 Packet loss\n"));
262 LOG_INFO(_("^3kills^7 Number of kills\n"));
263 LOG_INFO(_("^3deaths^7 Number of deaths\n"));
264 LOG_INFO(_("^3suicides^7 Number of suicides\n"));
265 LOG_INFO(_("^3frags^7 kills - suicides\n"));
266 LOG_INFO(_("^3kd^7 The kill-death ratio\n"));
267 LOG_INFO(_("^3dmg^7 The total damage done\n"));
268 LOG_INFO(_("^3dmgtaken^7 The total damage taken\n"));
269 LOG_INFO(_("^3sum^7 frags - deaths\n"));
270 LOG_INFO(_("^3caps^7 How often a flag (CTF) or a key (KeyHunt) was captured\n"));
271 LOG_INFO(_("^3pickups^7 How often a flag (CTF) or a key (KeyHunt) or a ball (Keepaway) was picked up\n"));
272 LOG_INFO(_("^3captime^7 Time of fastest cap (CTF)\n"));
273 LOG_INFO(_("^3fckills^7 Number of flag carrier kills\n"));
274 LOG_INFO(_("^3returns^7 Number of flag returns\n"));
275 LOG_INFO(_("^3drops^7 Number of flag drops\n"));
276 LOG_INFO(_("^3lives^7 Number of lives (LMS)\n"));
277 LOG_INFO(_("^3rank^7 Player rank\n"));
278 LOG_INFO(_("^3pushes^7 Number of players pushed into void\n"));
279 LOG_INFO(_("^3destroyed^7 Number of keys destroyed by pushing them into void\n"));
280 LOG_INFO(_("^3kckills^7 Number of keys carrier kills\n"));
281 LOG_INFO(_("^3losses^7 Number of times a key was lost\n"));
282 LOG_INFO(_("^3laps^7 Number of laps finished (race/cts)\n"));
283 LOG_INFO(_("^3time^7 Total time raced (race/cts)\n"));
284 LOG_INFO(_("^3fastest^7 Time of fastest lap (race/cts)\n"));
285 LOG_INFO(_("^3ticks^7 Number of ticks (DOM)\n"));
286 LOG_INFO(_("^3takes^7 Number of domination points taken (DOM)\n"));
287 LOG_INFO(_("^3bckills^7 Number of ball carrier kills\n"));
288 LOG_INFO(_("^3bctime^7 Total amount of time holding the ball in Keepaway\n"));
289 LOG_INFO(_("^3score^7 Total score\n\n"));
291 LOG_INFO(_("Before a field you can put a + or - sign, then a comma separated list\n"
292 "of game types, then a slash, to make the field show up only in these\n"
293 "or in all but these game types. You can also specify 'all' as a\n"
294 "field to show all fields available for the current game mode.\n\n"));
296 LOG_INFO(_("The special game type names 'teams' and 'noteams' can be used to\n"
297 "include/exclude ALL teams/noteams game modes.\n\n"));
299 LOG_INFO(_("Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4\n"));
300 LOG_INFO(_("will display name, ping and pl aligned to the left, and the fields\n"
301 "right of the vertical bar aligned to the right.\n"));
302 LOG_INFO(_("'field3' will only be shown in CTF, and 'field4' will be shown in all\n"
303 "other gamemodes except DM.\n"));
306 // NOTE: adding a gametype with ? to not warn for an optional field
307 // make sure it's excluded in a previous exclusive rule, if any
308 // otherwise the previous exclusive rule warns anyway
309 // e.g. -teams,rc,cts,lms/kills ?+rc/kills
310 #define SCOREBOARD_DEFAULT_COLUMNS \
312 " -teams,rc,cts,inv,lms/kills +ft,tdm/kills ?+rc,inv/kills" \
313 " -teams,lms/deaths +ft,tdm/deaths" \
314 " -teams,lms,rc,cts,inv,ka/suicides +ft,tdm/suicides ?+rc,inv/suicides" \
315 " -cts,dm,tdm,ka,ft/frags" /* tdm already has this in "score" */ \
316 " -rc,cts,nb/dmg -rc,cts,nb/dmgtaken" \
317 " +ctf/caps +ctf/pickups +ctf/fckills +ctf/returns +ons/caps +ons/takes" \
318 " +lms/lives +lms/rank" \
319 " +kh/caps +kh/pushes +kh/destroyed" \
320 " ?+rc/laps ?+rc/time +rc,cts/fastest" \
321 " +as/objectives +nb/faults +nb/goals" \
322 " +ka/pickups +ka/bckills +ka/bctime +ft/revivals" \
323 " -lms,rc,cts,inv,nb/score"
325 void Cmd_HUD_SetFields(int argc)
330 float have_name = 0, have_primary = 0, have_secondary = 0, have_separator = 0;
335 // set up a temporary scoreboard layout
336 // no layout can be properly set up until score_info data haven't been received
337 argc = tokenizebyseparator("0 1 ping pl name | score", " ");
339 scores_label[ps_primary] = strzone("score");
340 scores_flags[ps_primary] = SFL_ALLOW_HIDE;
343 // TODO: re enable with gametype dependant cvars?
344 if(argc < 3) // no arguments provided
345 argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " ");
348 argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " ");
352 if(argv(2) == "default")
353 argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " ");
354 else if(argv(2) == "all")
357 s = "ping pl name |";
358 for(i = 0; i < MAX_SCORE; ++i)
361 if(i != ps_secondary)
362 if(scores_label[i] != "")
363 s = strcat(s, " ", scores_label[i]);
365 if(ps_secondary != ps_primary)
366 s = strcat(s, " ", scores_label[ps_secondary]);
367 s = strcat(s, " ", scores_label[ps_primary]);
368 argc = tokenizebyseparator(strcat("0 1 ", s), " ");
375 hud_fontsize = HUD_GetFontsize("hud_fontsize");
377 for(i = 1; i < argc - 1; ++i)
383 if(substring(str, 0, 1) == "?")
386 str = substring(str, 1, strlen(str) - 1);
389 slash = strstrofs(str, "/", 0);
392 pattern = substring(str, 0, slash);
393 str = substring(str, slash + 1, strlen(str) - (slash + 1));
395 if (!isGametypeInFilter(gametype, teamplay, false, pattern))
399 strunzone(hud_title[hud_num_fields]);
400 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(str));
401 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
402 str = strtolower(str);
406 case "ping": hud_field[hud_num_fields] = SP_PING; break;
407 case "pl": hud_field[hud_num_fields] = SP_PL; break;
408 case "kd": case "kdr": case "kdratio": case "k/d": hud_field[hud_num_fields] = SP_KDRATIO; break;
409 case "sum": case "diff": case "k-d": hud_field[hud_num_fields] = SP_SUM; break;
410 case "name": case "nick": hud_field[hud_num_fields] = SP_NAME; have_name = true; break;
411 case "|": hud_field[hud_num_fields] = SP_SEPARATOR; have_separator = true; break;
412 case "dmg": hud_field[hud_num_fields] = SP_DMG; break;
413 case "dmgtaken": hud_field[hud_num_fields] = SP_DMGTAKEN; break;
416 for(j = 0; j < MAX_SCORE; ++j)
417 if(str == strtolower(scores_label[j]))
418 goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
426 LOG_INFOF("^1Error:^7 Unknown score field: '%s'\n", str);
430 hud_field[hud_num_fields] = j;
433 if(j == ps_secondary)
439 if(hud_num_fields >= MAX_HUD_FIELDS)
443 if(scores_flags[ps_primary] & SFL_ALLOW_HIDE)
445 if(scores_flags[ps_secondary] & SFL_ALLOW_HIDE)
447 if(ps_primary == ps_secondary)
449 missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
451 if(hud_num_fields+missing < MAX_HUD_FIELDS)
455 strunzone(hud_title[hud_num_fields]);
456 for(i = hud_num_fields; i > 0; --i)
458 hud_title[i] = hud_title[i-1];
459 hud_size[i] = hud_size[i-1];
460 hud_field[i] = hud_field[i-1];
462 hud_title[0] = strzone(TranslateScoresLabel("name"));
463 hud_field[0] = SP_NAME;
465 LOG_INFO("fixed missing field 'name'\n");
469 strunzone(hud_title[hud_num_fields]);
470 for(i = hud_num_fields; i > 1; --i)
472 hud_title[i] = hud_title[i-1];
473 hud_size[i] = hud_size[i-1];
474 hud_field[i] = hud_field[i-1];
476 hud_title[1] = strzone("|");
477 hud_field[1] = SP_SEPARATOR;
478 hud_size[1] = stringwidth("|", false, hud_fontsize);
480 LOG_INFO("fixed missing field '|'\n");
483 else if(!have_separator)
485 strunzone(hud_title[hud_num_fields]);
486 hud_title[hud_num_fields] = strzone("|");
487 hud_size[hud_num_fields] = stringwidth("|", false, hud_fontsize);
488 hud_field[hud_num_fields] = SP_SEPARATOR;
490 LOG_INFO("fixed missing field '|'\n");
494 strunzone(hud_title[hud_num_fields]);
495 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_secondary]));
496 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
497 hud_field[hud_num_fields] = ps_secondary;
499 LOG_INFOF("fixed missing field '%s'\n", scores_label[ps_secondary]);
503 strunzone(hud_title[hud_num_fields]);
504 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_primary]));
505 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
506 hud_field[hud_num_fields] = ps_primary;
508 LOG_INFOF("fixed missing field '%s'\n", scores_label[ps_primary]);
512 hud_field[hud_num_fields] = SP_END;
516 vector hud_field_rgb;
517 string hud_field_icon0;
518 string hud_field_icon1;
519 string hud_field_icon2;
520 vector hud_field_icon0_rgb;
521 vector hud_field_icon1_rgb;
522 vector hud_field_icon2_rgb;
523 float hud_field_icon0_alpha;
524 float hud_field_icon1_alpha;
525 float hud_field_icon2_alpha;
526 string HUD_GetField(entity pl, int field)
529 float tmp, num, denom;
532 hud_field_rgb = '1 1 1';
533 hud_field_icon0 = "";
534 hud_field_icon1 = "";
535 hud_field_icon2 = "";
536 hud_field_icon0_rgb = '1 1 1';
537 hud_field_icon1_rgb = '1 1 1';
538 hud_field_icon2_rgb = '1 1 1';
539 hud_field_icon0_alpha = 1;
540 hud_field_icon1_alpha = 1;
541 hud_field_icon2_alpha = 1;
546 return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6
547 //str = getplayerkeyvalue(pl.sv_entnum, "ping");
551 tmp = max(0, min(220, f-80)) / 220;
552 hud_field_rgb = '1 1 1' - '0 1 1'*tmp;
558 f = pl.ping_packetloss;
559 tmp = pl.ping_movementloss;
560 if(f == 0 && tmp == 0)
562 str = ftos(ceil(f * 100));
564 str = strcat(str, "~", ftos(ceil(tmp * 100)));
565 tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
566 hud_field_rgb = '1 0.5 0.5' - '0 0.5 0.5'*tmp;
570 if(ready_waiting && pl.ready)
572 hud_field_icon0 = "gfx/scoreboard/player_ready";
576 f = stof(getplayerkeyvalue(pl.sv_entnum, "colors"));
578 hud_field_icon0 = "gfx/scoreboard/playercolor_base";
579 hud_field_icon1 = "gfx/scoreboard/playercolor_shirt";
580 hud_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
581 hud_field_icon2 = "gfx/scoreboard/playercolor_pants";
582 hud_field_icon2_rgb = colormapPaletteColor(f % 16, 1);
585 return entcs_GetName(pl.sv_entnum);
588 f = pl.(scores[SP_KILLS]);
589 f -= pl.(scores[SP_SUICIDES]);
593 num = pl.(scores[SP_KILLS]);
594 denom = pl.(scores[SP_DEATHS]);
597 hud_field_rgb = '0 1 0';
598 str = sprintf("%d", num);
599 } else if(num <= 0) {
600 hud_field_rgb = '1 0 0';
601 str = sprintf("%.1f", num/denom);
603 str = sprintf("%.1f", num/denom);
607 f = pl.(scores[SP_KILLS]);
608 f -= pl.(scores[SP_DEATHS]);
611 hud_field_rgb = '0 1 0';
613 hud_field_rgb = '1 1 1';
615 hud_field_rgb = '1 0 0';
620 num = pl.(scores[SP_DMG]);
623 str = sprintf("%.1f k", num/denom);
627 num = pl.(scores[SP_DMGTAKEN]);
630 str = sprintf("%.1f k", num/denom);
634 tmp = pl.(scores[field]);
635 f = scores_flags[field];
636 if(field == ps_primary)
637 hud_field_rgb = '1 1 0';
638 else if(field == ps_secondary)
639 hud_field_rgb = '0 1 1';
641 hud_field_rgb = '1 1 1';
642 return ScoreString(f, tmp);
647 float hud_fixscoreboardcolumnwidth_len;
648 float hud_fixscoreboardcolumnwidth_iconlen;
649 float hud_fixscoreboardcolumnwidth_marginlen;
651 string HUD_FixScoreboardColumnWidth(int i, string str)
656 field = hud_field[i];
658 hud_fixscoreboardcolumnwidth_iconlen = 0;
660 if(hud_field_icon0 != "")
662 sz = draw_getimagesize(hud_field_icon0);
664 if(hud_fixscoreboardcolumnwidth_iconlen < f)
665 hud_fixscoreboardcolumnwidth_iconlen = f;
668 if(hud_field_icon1 != "")
670 sz = draw_getimagesize(hud_field_icon1);
672 if(hud_fixscoreboardcolumnwidth_iconlen < f)
673 hud_fixscoreboardcolumnwidth_iconlen = f;
676 if(hud_field_icon2 != "")
678 sz = draw_getimagesize(hud_field_icon2);
680 if(hud_fixscoreboardcolumnwidth_iconlen < f)
681 hud_fixscoreboardcolumnwidth_iconlen = f;
684 hud_fixscoreboardcolumnwidth_iconlen *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect
686 if(hud_fixscoreboardcolumnwidth_iconlen != 0)
687 hud_fixscoreboardcolumnwidth_marginlen = stringwidth(" ", false, hud_fontsize);
689 hud_fixscoreboardcolumnwidth_marginlen = 0;
691 if(field == SP_NAME) // name gets all remaining space
695 namesize = sbwidth;// / hud_fontsize_x;
696 for(j = 0; j < hud_num_fields; ++j)
698 if (hud_field[i] != SP_SEPARATOR)
699 namesize -= hud_size[j] + hud_fontsize.x;
700 namesize += hud_fontsize.x;
701 hud_size[i] = namesize;
703 if (hud_fixscoreboardcolumnwidth_iconlen != 0)
704 namesize -= hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
705 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
706 hud_fixscoreboardcolumnwidth_len = stringwidth(str, true, hud_fontsize);
709 hud_fixscoreboardcolumnwidth_len = stringwidth(str, false, hud_fontsize);
711 f = hud_fixscoreboardcolumnwidth_len + hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
718 void HUD_PrintScoreboardItem(vector pos, vector item_size, entity pl, bool is_self, int pl_number)
720 TC(bool, is_self); TC(int, pl_number);
722 rgb = Team_ColorRGB(pl.team);
726 is_spec = (entcs_GetTeam(pl.sv_entnum) == NUM_SPECTATOR);
728 if((rgb == '1 1 1') && (!is_spec)) {
729 rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
730 rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
731 rgb.z = autocvar_scoreboard_color_bg_b + 0.5; }
733 vector h_pos = pos - '1 1 0';
734 vector h_size = item_size + '2 0 0';
735 // alternated rows highlighting
737 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
738 else if((scoreboard_highlight) && (!(pl_number % 2)))
739 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
745 for(i = 0; i < hud_num_fields; ++i)
747 field = hud_field[i];
748 if(field == SP_SEPARATOR)
751 if(is_spec && field != SP_NAME && field != SP_PING) {
752 pos.x += hud_size[i] + hud_fontsize.x;
755 str = HUD_GetField(pl, field);
756 str = HUD_FixScoreboardColumnWidth(i, str);
758 pos.x += hud_size[i] + hud_fontsize.x;
760 if(field == SP_NAME) {
761 tmp.x = hud_size[i] - hud_fontsize.x*hud_fixscoreboardcolumnwidth_iconlen - hud_fixscoreboardcolumnwidth_marginlen + hud_fontsize.x;
763 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
765 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
767 tmp.x = hud_fixscoreboardcolumnwidth_len + hud_fontsize.x;
769 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
771 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
774 tmp.x = hud_size[i] + hud_fontsize.x;
775 if(hud_field_icon0 != "")
777 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);
779 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);
780 if(hud_field_icon1 != "")
782 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);
784 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);
785 if(hud_field_icon2 != "")
787 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);
789 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);
792 if(hud_field[i] == SP_SEPARATOR)
795 for(i = hud_num_fields-1; i > 0; --i)
797 field = hud_field[i];
798 if(field == SP_SEPARATOR)
801 if(is_spec && field != SP_NAME && field != SP_PING) {
802 pos.x -= hud_size[i] + hud_fontsize.x;
806 str = HUD_GetField(pl, field);
807 str = HUD_FixScoreboardColumnWidth(i, str);
809 if(field == SP_NAME) {
810 tmp.x = hud_fixscoreboardcolumnwidth_len; // left or right aligned? let's put it right...
812 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
814 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
816 tmp.x = hud_fixscoreboardcolumnwidth_len;
818 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
820 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
824 if(hud_field_icon0 != "")
826 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);
828 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);
829 if(hud_field_icon1 != "")
831 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);
833 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);
834 if(hud_field_icon2 != "")
836 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);
838 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);
839 pos.x -= hud_size[i] + hud_fontsize.x;
844 drawfill(h_pos, h_size, '0 0 0', 0.5, DRAWFLAG_NORMAL);
848 * HUD_Scoreboard_MakeTable
850 * Makes a table for a team (for all playing players in DM) and fills it
853 vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
855 float body_table_height;
856 vector tmp = '0 0 0', column_dim = '0 0 0';
859 body_table_height = 1.25 * hud_fontsize.y * max(1, tm.team_size); // no player? show 1 empty line
861 pos.y += autocvar_scoreboard_border_thickness;
865 tmp.y = 1.25 * hud_fontsize.y;
869 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, (rgb * autocvar_scoreboard_color_bg_team) + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
871 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
874 tmp.y += autocvar_scoreboard_border_thickness;
875 tmp.y += body_table_height;
876 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL); // more transparency for the scoreboard
878 // separator header/table
879 pos.y += 1.25 * hud_fontsize.y;
880 tmp.y = autocvar_scoreboard_border_thickness;
881 drawfill(pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
883 pos.y += autocvar_scoreboard_border_thickness;
886 tmp.y = body_table_height;
888 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
890 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
892 // anyway, apply some color
893 //drawfill(pos, tmp + '2 0 0', rgb, 0.1, DRAWFLAG_NORMAL);
895 // go back to the top to make alternated columns highlighting and to print the strings
896 pos.y -= 1.25 * hud_fontsize.y;
897 pos.y -= autocvar_scoreboard_border_thickness;
901 if (scoreboard_highlight)
903 column_dim.y = 1.25 * hud_fontsize.y; // header
904 column_dim.y += autocvar_scoreboard_border_thickness;
905 column_dim.y += body_table_height;
908 // print the strings of the columns headers and draw the columns
910 for(i = 0; i < hud_num_fields; ++i)
912 if(hud_field[i] == SP_SEPARATOR)
914 column_dim.x = hud_size[i] + hud_fontsize.x;
915 if (scoreboard_highlight)
918 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
920 drawstring(pos, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
921 pos.x += column_dim.x;
923 if(hud_field[i] == SP_SEPARATOR)
927 for(i = hud_num_fields-1; i > 0; --i)
929 if(hud_field[i] == SP_SEPARATOR)
932 pos.x -= hud_size[i];
934 if (scoreboard_highlight)
938 if (i == hud_num_fields-1)
939 column_dim.x = hud_size[i] + hud_fontsize.x / 2 + 1;
941 column_dim.x = hud_size[i] + hud_fontsize.x;
942 drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
946 tmp.x = stringwidth(hud_title[i], false, hud_fontsize);
947 tmp.x = (hud_size[i] - tmp.x);
948 drawstring(pos + tmp, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
949 pos.x -= hud_fontsize.x;
954 pos.y += 1.25 * hud_fontsize.y; // skip the header
955 pos.y += autocvar_scoreboard_border_thickness;
959 tmp.y = hud_fontsize.y * 1.25;
961 // fill the table and draw the rows
964 for(pl = players.sort_next; pl; pl = pl.sort_next)
966 if(pl.team != tm.team)
968 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
969 pos.y += 1.25 * hud_fontsize.y;
973 for(pl = players.sort_next; pl; pl = pl.sort_next)
975 if(pl.team == NUM_SPECTATOR)
977 HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
978 pos.y += 1.25 * hud_fontsize.y;
983 pos.y += 1.25 * hud_fontsize.y; // move to the end of the table
984 pos.y += 1.25 * hud_fontsize.y; // move empty row (out of the table)
989 float HUD_WouldDrawScoreboard() {
990 if (QuickMenu_IsOpened())
992 else if (HUD_Radar_Clickable())
994 else if (scoreboard_showscores)
996 else if (intermission == 1)
998 else if (intermission == 2)
1000 else if (spectatee_status != -1 && STAT(HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS && !active_minigame)
1002 else if (scoreboard_showscores_force)
1007 float average_accuracy;
1008 vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
1010 WepSet weapons_stat = WepSet_GetFromStat();
1011 WepSet weapons_inmap = WepSet_GetFromStat_InMap();
1012 float initial_posx = pos.x;
1013 int disownedcnt = 0;
1014 FOREACH(Weapons, it != WEP_Null, {
1015 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1017 WepSet set = it.m_wepset;
1018 if (weapon_stats < 0 && !(weapons_stat & set || weapons_inmap & set))
1022 int weapon_cnt = (Weapons_COUNT - 1) - disownedcnt;
1023 if (weapon_cnt <= 0) return pos;
1026 if (autocvar_scoreboard_accuracy_doublerows && weapon_cnt >= floor((Weapons_COUNT - 1) * 0.5))
1028 int columnns = ceil(weapon_cnt / rows);
1031 float fontsize = height * 1/3;
1032 float weapon_height = height * 2/3;
1033 float weapon_width = sbwidth / columnns / rows;
1035 drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1036 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1037 vector tmp = '0 0 0';
1039 tmp.y = height * rows;
1042 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1044 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1045 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1047 // column highlighting
1048 for (int i = 0; i < columnns; ++i)
1051 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);
1055 for (int i = 0; i < rows; ++i)
1057 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);
1060 average_accuracy = 0;
1061 int weapons_with_stats = 0;
1063 pos.x += weapon_width / 2;
1065 if (autocvar_scoreboard_accuracy_nocolors)
1068 Accuracy_LoadColors();
1070 float oldposx = pos.x;
1074 FOREACH(Weapons, it != WEP_Null, {
1075 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1077 WepSet set = it.m_wepset;
1078 if (weapon_stats < 0 && !(weapons_stat & set || weapons_inmap & set))
1082 if (weapon_stats >= 0)
1083 weapon_alpha = scoreboard_alpha_fg;
1085 weapon_alpha = 0.2 * scoreboard_alpha_fg;
1088 drawpic_aspect_skin(tmpos, it.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
1090 if (weapon_stats >= 0) {
1091 weapons_with_stats += 1;
1092 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1095 s = sprintf("%d%%", weapon_stats*100);
1098 padding = (weapon_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center the accuracy value
1100 if(!autocvar_scoreboard_accuracy_nocolors)
1101 rgb = Accuracy_GetColor(weapon_stats);
1103 drawstring(tmpos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1105 tmpos.x += weapon_width * rows;
1106 pos.x += weapon_width * rows;
1107 if (rows == 2 && column == columnns - 1) {
1115 if (weapons_with_stats)
1116 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1119 pos.y += 1.25 * hud_fontsize.y;
1120 pos.x = initial_posx;
1124 vector HUD_DrawKeyValue(vector pos, string key, string value) {
1126 pos.x += hud_fontsize.x * 0.25;
1127 drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1128 pos.x = xmax - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1129 drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1131 pos.y+= hud_fontsize.y;
1136 vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
1137 float stat_secrets_found, stat_secrets_total;
1138 float stat_monsters_killed, stat_monsters_total;
1142 // get monster stats
1143 stat_monsters_killed = STAT(MONSTERS_KILLED);
1144 stat_monsters_total = STAT(MONSTERS_TOTAL);
1146 // get secrets stats
1147 stat_secrets_found = STAT(SECRETS_FOUND);
1148 stat_secrets_total = STAT(SECRETS_TOTAL);
1150 // get number of rows
1151 if(stat_secrets_total)
1153 if(stat_monsters_total)
1156 // if no rows, return
1160 // draw table header
1161 drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1162 pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness;
1165 vector tmp = '0 0 0';
1167 tmp.y = hud_fontsize.y * rows;
1170 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1172 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1173 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1176 if(stat_monsters_total)
1178 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1179 pos = HUD_DrawKeyValue(pos, _("Monsters killed:"), val);
1183 if(stat_secrets_total)
1185 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1186 pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
1190 pos.y += 1.25 * hud_fontsize.y;
1195 vector HUD_DrawScoreboardRankings(vector pos, entity pl, vector rgb, vector bg_size)
1198 RANKINGS_RECEIVED_CNT = 0;
1199 for (i=RANKINGS_CNT-1; i>=0; --i)
1201 ++RANKINGS_RECEIVED_CNT;
1203 if (RANKINGS_RECEIVED_CNT == 0)
1207 is_spec = (entcs_GetTeam(pl.sv_entnum) == NUM_SPECTATOR);
1209 hl_rgb.x = autocvar_scoreboard_color_bg_r + 0.5;
1210 hl_rgb.y = autocvar_scoreboard_color_bg_g + 0.5;
1211 hl_rgb.z = autocvar_scoreboard_color_bg_b + 0.5;
1213 pos.y += hud_fontsize.y;
1214 drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1215 pos.y += hud_fontsize.y + autocvar_scoreboard_border_thickness;
1216 vector tmp = '0 0 0';
1218 tmp.y = 1.25 * hud_fontsize.y * RANKINGS_RECEIVED_CNT;
1221 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1223 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1224 drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL);
1227 for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1234 n = grecordholder[i];
1235 p = count_ordinal(i+1);
1236 if(grecordholder[i] == entcs_GetName(player_localnum))
1237 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1238 else if(!(i % 2) && scoreboard_highlight)
1239 drawfill(pos, '1 0 0' * sbwidth + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1240 drawstring(pos, p, '1 1 0' * hud_fontsize.y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1241 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);
1242 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize.y, n, '1 1 0' * hud_fontsize.y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1243 pos.y += 1.25 * hud_fontsize.y;
1245 pos.y += autocvar_scoreboard_border_thickness;
1250 float hud_woulddrawscoreboard_prev;
1251 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1252 void HUD_DrawScoreboard()
1254 if(!autocvar__hud_configure)
1256 float hud_woulddrawscoreboard;
1257 hud_woulddrawscoreboard = scoreboard_active;
1258 if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1259 hud_woulddrawscoreboard_change = time;
1260 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1263 if(hud_woulddrawscoreboard) {
1264 if(menu_enabled == 1)
1265 scoreboard_fade_alpha = 1;
1266 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1267 if (scoreboard_fadeinspeed)
1268 scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1270 scoreboard_fade_alpha = 1;
1273 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1274 if (scoreboard_fadeoutspeed)
1275 scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1277 scoreboard_fade_alpha = 0;
1280 if (!scoreboard_fade_alpha)
1284 scoreboard_fade_alpha = 0;
1286 if (autocvar_scoreboard_dynamichud)
1289 HUD_Scale_Disable();
1291 float hud_fade_alpha_save = hud_fade_alpha;
1292 if(menu_enabled == 1)
1295 hud_fade_alpha = scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1296 HUD_Panel_UpdateCvars();
1298 scoreboard_alpha_bg = autocvar_scoreboard_alpha_bg * panel_fg_alpha;
1299 scoreboard_alpha_fg = autocvar_scoreboard_alpha_fg * panel_fg_alpha;
1300 scoreboard_highlight = autocvar_scoreboard_highlight;
1301 scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * panel_fg_alpha;
1302 scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * panel_fg_alpha;
1303 scoreboard_alpha_name = autocvar_scoreboard_alpha_name * panel_fg_alpha;
1304 scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * panel_fg_alpha;
1306 hud_fade_alpha = hud_fade_alpha_save;
1308 // don't overlap with con_notify
1309 if(!autocvar__hud_configure)
1310 panel_pos_y = max((autocvar_con_notify * autocvar_con_notifysize), panel_pos_y);
1312 HUD_Panel_DrawBg(scoreboard_fade_alpha);
1314 if(panel_bg_padding)
1316 panel_pos += '1 1 0' * panel_bg_padding;
1317 panel_size -= '2 2 0' * panel_bg_padding;
1320 HUD_UpdatePlayerTeams();
1322 vector rgb, pos, tmp;
1329 xmax = panel_pos_x + panel_size_x;
1330 ymax = panel_pos_y + panel_size_y;
1332 sbwidth = panel_size_x;
1334 // Initializes position
1340 vector sb_heading_fontsize;
1341 sb_heading_fontsize = hud_fontsize * 2;
1342 draw_beginBoldFont();
1343 drawstring(pos, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1346 pos.y += sb_heading_fontsize.y + hud_fontsize.y * 0.25;
1348 // Draw the scoreboard
1349 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * ((autocvar_scoreboard_bg_scale > 0) ? autocvar_scoreboard_bg_scale : 0.25);
1353 vector team_score_baseoffset;
1354 team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize.y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize.x * 0.25);
1355 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1357 if(tm.team == NUM_SPECTATOR)
1359 if(!tm.team && teamplay)
1362 draw_beginBoldFont();
1363 rgb = Team_ColorRGB(tm.team);
1364 str = ftos(tm.(teamscores[ts_primary]));
1365 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1367 if(ts_primary != ts_secondary)
1369 str = ftos(tm.(teamscores[ts_secondary]));
1370 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);
1374 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1376 rgb.x = autocvar_scoreboard_color_bg_r;
1377 rgb.y = autocvar_scoreboard_color_bg_g;
1378 rgb.z = autocvar_scoreboard_color_bg_b;
1382 rgb.x = autocvar_scoreboard_color_bg_r;
1383 rgb.y = autocvar_scoreboard_color_bg_g;
1384 rgb.z = autocvar_scoreboard_color_bg_b;
1386 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1388 if(tm.team == NUM_SPECTATOR)
1390 if(!tm.team && teamplay)
1393 pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size);
1397 if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1398 if(race_speedaward) {
1399 drawcolorcodedstring(pos, sprintf(_("Speed award: %d%s ^7(%s^7)"), race_speedaward, race_speedaward_unit, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1400 pos.y += 1.25 * hud_fontsize.y;
1402 if(race_speedaward_alltimebest) {
1403 drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d%s ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_unit, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1404 pos.y += 1.25 * hud_fontsize.y;
1406 pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size);
1408 else if (autocvar_scoreboard_accuracy && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
1410 pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size);
1412 pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size);
1417 pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size);
1419 pos = HUD_DrawMapStats(pos, rgb, bg_size);
1426 item_size.x = sbwidth;
1427 item_size.y = hud_fontsize.y * 1.25;
1429 for(pl = players.sort_next; pl; pl = pl.sort_next)
1431 if(pl.team != NUM_SPECTATOR)
1433 pos.y += 1.25 * hud_fontsize.y;
1434 HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
1440 draw_beginBoldFont();
1441 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1443 pos.y += 1.25 * hud_fontsize.y;
1446 // Print info string
1448 str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
1449 tl = STAT(TIMELIMIT);
1450 fl = STAT(FRAGLIMIT);
1451 ll = STAT(LEADLIMIT);
1452 if(gametype == MAPINFO_TYPE_LMS)
1455 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1460 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1464 str = strcat(str, _(" or"));
1467 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl),
1468 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1469 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1470 TranslateScoresLabel(teamscores_label[ts_primary])));
1474 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl),
1475 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1476 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1477 TranslateScoresLabel(scores_label[ps_primary])));
1482 if(tl > 0 || fl > 0)
1483 str = strcat(str, _(" or"));
1486 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll),
1487 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1488 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1489 TranslateScoresLabel(teamscores_label[ts_primary])));
1493 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll),
1494 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1495 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1496 TranslateScoresLabel(scores_label[ps_primary])));
1501 pos.y += 1.2 * hud_fontsize.y;
1502 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1504 // print information about respawn status
1505 float respawn_time = STAT(RESPAWN_TIME);
1509 if(respawn_time < 0)
1511 // a negative number means we are awaiting respawn, time value is still the same
1512 respawn_time *= -1; // remove mark now that we checked it
1513 respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
1515 str = sprintf(_("^1Respawning in ^3%s^1..."),
1516 (autocvar_scoreboard_respawntime_decimals ?
1517 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1519 count_seconds(respawn_time - time)
1523 else if(time < respawn_time)
1525 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
1526 (autocvar_scoreboard_respawntime_decimals ?
1527 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1529 count_seconds(respawn_time - time)
1533 else if(time >= respawn_time)
1534 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
1536 pos.y += 1.2 * hud_fontsize.y;
1537 drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1540 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;