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 // wrapper to put all possible scores titles through gettext
23 string TranslateScoresLabel(string l)
27 case "bckills": return CTX(_("SCO^bckills"));
28 case "bctime": return CTX(_("SCO^bctime"));
29 case "caps": return CTX(_("SCO^caps"));
30 case "captime": return CTX(_("SCO^captime"));
31 case "deaths": return CTX(_("SCO^deaths"));
32 case "destroyed": return CTX(_("SCO^destroyed"));
33 case "dmg": return CTX(_("SCO^dmg"));
34 case "dmgtaken": return CTX(_("SCO^dmgtaken"));
35 case "drops": return CTX(_("SCO^drops"));
36 case "faults": return CTX(_("SCO^faults"));
37 case "fckills": return CTX(_("SCO^fckills"));
38 case "goals": return CTX(_("SCO^goals"));
39 case "kckills": return CTX(_("SCO^kckills"));
40 case "kdratio": return CTX(_("SCO^kdratio"));
41 case "k/d": return CTX(_("SCO^k/d"));
42 case "kd": return CTX(_("SCO^kd"));
43 case "kdr": return CTX(_("SCO^kdr"));
44 case "kills": return CTX(_("SCO^kills"));
45 case "laps": return CTX(_("SCO^laps"));
46 case "lives": return CTX(_("SCO^lives"));
47 case "losses": return CTX(_("SCO^losses"));
48 case "name": return CTX(_("SCO^name"));
49 case "sum": return CTX(_("SCO^sum"));
50 case "nick": return CTX(_("SCO^nick"));
51 case "objectives": return CTX(_("SCO^objectives"));
52 case "pickups": return CTX(_("SCO^pickups"));
53 case "ping": return CTX(_("SCO^ping"));
54 case "pl": return CTX(_("SCO^pl"));
55 case "pushes": return CTX(_("SCO^pushes"));
56 case "rank": return CTX(_("SCO^rank"));
57 case "returns": return CTX(_("SCO^returns"));
58 case "revivals": return CTX(_("SCO^revivals"));
59 case "score": return CTX(_("SCO^score"));
60 case "suicides": return CTX(_("SCO^suicides"));
61 case "takes": return CTX(_("SCO^takes"));
62 case "ticks": return CTX(_("SCO^ticks"));
71 ps_primary = ps_secondary = ts_primary = ts_secondary = -1;
72 for(i = 0; i < MAX_SCORE; ++i)
74 f = (scores_flags[i] & SFL_SORT_PRIO_MASK);
75 if(f == SFL_SORT_PRIO_PRIMARY)
77 if(f == SFL_SORT_PRIO_SECONDARY)
80 if(ps_secondary == -1)
81 ps_secondary = ps_primary;
83 for(i = 0; i < MAX_TEAMSCORE; ++i)
85 f = (teamscores_flags[i] & SFL_SORT_PRIO_MASK);
86 if(f == SFL_SORT_PRIO_PRIMARY)
88 if(f == SFL_SORT_PRIO_SECONDARY)
91 if(ts_secondary == -1)
92 ts_secondary = ts_primary;
97 float SetTeam(entity pl, float Team);
99 void HUD_UpdatePlayerTeams()
106 for(pl = players.sort_next; pl; pl = pl.sort_next)
109 Team = entcs_GetScoreTeam(pl.sv_entnum);
110 if(SetTeam(pl, Team))
113 HUD_UpdatePlayerPos(pl);
117 pl = players.sort_next;
122 print(strcat("PNUM: ", ftos(num), "\n"));
127 int HUD_CompareScore(int vl, int vr, int f)
129 TC(int, vl); TC(int, vr); TC(int, f);
130 if(f & SFL_ZERO_IS_WORST)
132 if(vl == 0 && vr != 0)
134 if(vl != 0 && vr == 0)
138 return IS_INCREASING(f);
140 return IS_DECREASING(f);
144 float HUD_ComparePlayerScores(entity left, entity right)
147 vl = entcs_GetTeam(left.sv_entnum);
148 vr = entcs_GetTeam(right.sv_entnum);
160 if(vl == NUM_SPECTATOR)
162 // FIRST the one with scores (spectators), THEN the ones without (downloaders)
164 if(!left.gotscores && right.gotscores)
169 r = HUD_CompareScore(left.scores[ps_primary], right.scores[ps_primary], scores_flags[ps_primary]);
173 r = HUD_CompareScore(left.scores[ps_secondary], right.scores[ps_secondary], scores_flags[ps_secondary]);
178 for(i = 0; i < MAX_SCORE; ++i)
180 r = HUD_CompareScore(left.scores[i], right.scores[i], scores_flags[i]);
185 if (left.sv_entnum < right.sv_entnum)
191 void HUD_UpdatePlayerPos(entity player)
194 for(ent = player.sort_next; ent && HUD_ComparePlayerScores(player, ent); ent = player.sort_next)
196 SORT_SWAP(player, ent);
198 for(ent = player.sort_prev; ent != players && HUD_ComparePlayerScores(ent, player); ent = player.sort_prev)
200 SORT_SWAP(ent, player);
204 float HUD_CompareTeamScores(entity left, entity right)
208 if(left.team == NUM_SPECTATOR)
210 if(right.team == NUM_SPECTATOR)
213 r = HUD_CompareScore(left.teamscores[ts_primary], right.teamscores[ts_primary], teamscores_flags[ts_primary]);
217 r = HUD_CompareScore(left.teamscores[ts_secondary], right.teamscores[ts_secondary], teamscores_flags[ts_secondary]);
221 for(i = 0; i < MAX_SCORE; ++i)
223 r = HUD_CompareScore(left.teamscores[i], right.teamscores[i], teamscores_flags[i]);
228 if (left.team < right.team)
234 void HUD_UpdateTeamPos(entity Team)
237 for(ent = Team.sort_next; ent && HUD_CompareTeamScores(Team, ent); ent = Team.sort_next)
239 SORT_SWAP(Team, ent);
241 for(ent = Team.sort_prev; ent != teams && HUD_CompareTeamScores(ent, Team); ent = Team.sort_prev)
243 SORT_SWAP(ent, Team);
249 LOG_INFO(_("You can modify the scoreboard using the ^2scoreboard_columns_set command.\n"));
250 LOG_INFO(_("^3|---------------------------------------------------------------|\n"));
251 LOG_INFO(_("Usage:\n"));
252 LOG_INFO(_("^2scoreboard_columns_set default\n"));
253 LOG_INFO(_("^2scoreboard_columns_set ^7field1 field2 ...\n"));
254 LOG_INFO(_("The following field names are recognized (case insensitive):\n"));
255 LOG_INFO(_("You can use a ^3|^7 to start the right-aligned fields.\n\n"));
257 LOG_INFO(_("^3name^7 or ^3nick^7 Name of a player\n"));
258 LOG_INFO(_("^3ping^7 Ping time\n"));
259 LOG_INFO(_("^3pl^7 Packet loss\n"));
260 LOG_INFO(_("^3kills^7 Number of kills\n"));
261 LOG_INFO(_("^3deaths^7 Number of deaths\n"));
262 LOG_INFO(_("^3suicides^7 Number of suicides\n"));
263 LOG_INFO(_("^3frags^7 kills - suicides\n"));
264 LOG_INFO(_("^3kd^7 The kill-death ratio\n"));
265 LOG_INFO(_("^3dmg^7 The total damage done\n"));
266 LOG_INFO(_("^3dmgtaken^7 The total damage taken\n"));
267 LOG_INFO(_("^3sum^7 frags - deaths\n"));
268 LOG_INFO(_("^3caps^7 How often a flag (CTF) or a key (KeyHunt) was captured\n"));
269 LOG_INFO(_("^3pickups^7 How often a flag (CTF) or a key (KeyHunt) or a ball (Keepaway) was picked up\n"));
270 LOG_INFO(_("^3captime^7 Time of fastest cap (CTF)\n"));
271 LOG_INFO(_("^3fckills^7 Number of flag carrier kills\n"));
272 LOG_INFO(_("^3returns^7 Number of flag returns\n"));
273 LOG_INFO(_("^3drops^7 Number of flag drops\n"));
274 LOG_INFO(_("^3lives^7 Number of lives (LMS)\n"));
275 LOG_INFO(_("^3rank^7 Player rank\n"));
276 LOG_INFO(_("^3pushes^7 Number of players pushed into void\n"));
277 LOG_INFO(_("^3destroyed^7 Number of keys destroyed by pushing them into void\n"));
278 LOG_INFO(_("^3kckills^7 Number of keys carrier kills\n"));
279 LOG_INFO(_("^3losses^7 Number of times a key was lost\n"));
280 LOG_INFO(_("^3laps^7 Number of laps finished (race/cts)\n"));
281 LOG_INFO(_("^3time^7 Total time raced (race/cts)\n"));
282 LOG_INFO(_("^3fastest^7 Time of fastest lap (race/cts)\n"));
283 LOG_INFO(_("^3ticks^7 Number of ticks (DOM)\n"));
284 LOG_INFO(_("^3takes^7 Number of domination points taken (DOM)\n"));
285 LOG_INFO(_("^3bckills^7 Number of ball carrier kills\n"));
286 LOG_INFO(_("^3bctime^7 Total amount of time holding the ball in Keepaway\n"));
287 LOG_INFO(_("^3score^7 Total score\n\n"));
289 LOG_INFO(_("Before a field you can put a + or - sign, then a comma separated list\n"
290 "of game types, then a slash, to make the field show up only in these\n"
291 "or in all but these game types. You can also specify 'all' as a\n"
292 "field to show all fields available for the current game mode.\n\n"));
294 LOG_INFO(_("The special game type names 'teams' and 'noteams' can be used to\n"
295 "include/exclude ALL teams/noteams game modes.\n\n"));
297 LOG_INFO(_("Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4\n"));
298 LOG_INFO(_("will display name, ping and pl aligned to the left, and the fields\n"
299 "right of the vertical bar aligned to the right.\n"));
300 LOG_INFO(_("'field3' will only be shown in CTF, and 'field4' will be shown in all\n"
301 "other gamemodes except DM.\n"));
304 // NOTE: adding a gametype with ? to not warn for an optional field
305 // make sure it's excluded in a previous exclusive rule, if any
306 // otherwise the previous exclusive rule warns anyway
307 // e.g. -teams,rc,cts,lms/kills ?+rc/kills
308 #define SCOREBOARD_DEFAULT_COLUMNS \
310 " -teams,rc,cts,inv,lms/kills +ft,tdm/kills ?+rc,inv/kills" \
311 " -teams,lms/deaths +ft,tdm/deaths" \
312 " -teams,lms,rc,cts,inv,ka/suicides +ft,tdm/suicides ?+rc,inv/suicides" \
313 " -cts,dm,tdm,ka,ft/frags" /* tdm already has this in "score" */ \
314 " -rc,cts,nb/dmg -rc,cts,nb/dmgtaken" \
315 " +ctf/caps +ctf/pickups +ctf/fckills +ctf/returns +ons/caps +ons/takes" \
316 " +lms/lives +lms/rank" \
317 " +kh/caps +kh/pushes +kh/destroyed" \
318 " ?+rc/laps ?+rc/time +rc,cts/fastest" \
319 " +as/objectives +nb/faults +nb/goals" \
320 " +ka/pickups +ka/bckills +ka/bctime +ft/revivals" \
321 " -lms,rc,cts,inv,nb/score"
323 void Cmd_HUD_SetFields(int argc)
328 float have_name = 0, have_primary = 0, have_secondary = 0, have_separator = 0;
333 // set up a temporary scoreboard layout
334 // no layout can be properly set up until score_info data haven't been received
335 argc = tokenizebyseparator("0 1 ping pl name | score", " ");
337 scores_label[ps_primary] = strzone("score");
338 scores_flags[ps_primary] = SFL_ALLOW_HIDE;
341 // TODO: re enable with gametype dependant cvars?
342 if(argc < 3) // no arguments provided
343 argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " ");
346 argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " ");
350 if(argv(2) == "default")
351 argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " ");
352 else if(argv(2) == "all")
355 s = "ping pl name |";
356 for(i = 0; i < MAX_SCORE; ++i)
359 if(i != ps_secondary)
360 if(scores_label[i] != "")
361 s = strcat(s, " ", scores_label[i]);
363 if(ps_secondary != ps_primary)
364 s = strcat(s, " ", scores_label[ps_secondary]);
365 s = strcat(s, " ", scores_label[ps_primary]);
366 argc = tokenizebyseparator(strcat("0 1 ", s), " ");
373 hud_fontsize = HUD_GetFontsize("hud_fontsize");
375 for(i = 1; i < argc - 1; ++i)
381 if(substring(str, 0, 1) == "?")
384 str = substring(str, 1, strlen(str) - 1);
387 slash = strstrofs(str, "/", 0);
390 pattern = substring(str, 0, slash);
391 str = substring(str, slash + 1, strlen(str) - (slash + 1));
393 if (!isGametypeInFilter(gametype, teamplay, false, pattern))
397 strunzone(hud_title[hud_num_fields]);
398 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(str));
399 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
400 str = strtolower(str);
404 case "ping": hud_field[hud_num_fields] = SP_PING; break;
405 case "pl": hud_field[hud_num_fields] = SP_PL; break;
406 case "kd": case "kdr": case "kdratio": case "k/d": hud_field[hud_num_fields] = SP_KDRATIO; break;
407 case "sum": case "diff": case "k-d": hud_field[hud_num_fields] = SP_SUM; break;
408 case "name": case "nick": hud_field[hud_num_fields] = SP_NAME; have_name = true; break;
409 case "|": hud_field[hud_num_fields] = SP_SEPARATOR; have_separator = true; break;
410 case "dmg": hud_field[hud_num_fields] = SP_DMG; break;
411 case "dmgtaken": hud_field[hud_num_fields] = SP_DMGTAKEN; break;
414 for(j = 0; j < MAX_SCORE; ++j)
415 if(str == strtolower(scores_label[j]))
416 goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
424 LOG_INFOF("^1Error:^7 Unknown score field: '%s'\n", str);
428 hud_field[hud_num_fields] = j;
431 if(j == ps_secondary)
437 if(hud_num_fields >= MAX_HUD_FIELDS)
441 if(scores_flags[ps_primary] & SFL_ALLOW_HIDE)
443 if(scores_flags[ps_secondary] & SFL_ALLOW_HIDE)
445 if(ps_primary == ps_secondary)
447 missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
449 if(hud_num_fields+missing < MAX_HUD_FIELDS)
453 strunzone(hud_title[hud_num_fields]);
454 for(i = hud_num_fields; i > 0; --i)
456 hud_title[i] = hud_title[i-1];
457 hud_size[i] = hud_size[i-1];
458 hud_field[i] = hud_field[i-1];
460 hud_title[0] = strzone(TranslateScoresLabel("name"));
461 hud_field[0] = SP_NAME;
463 LOG_INFO("fixed missing field 'name'\n");
467 strunzone(hud_title[hud_num_fields]);
468 for(i = hud_num_fields; i > 1; --i)
470 hud_title[i] = hud_title[i-1];
471 hud_size[i] = hud_size[i-1];
472 hud_field[i] = hud_field[i-1];
474 hud_title[1] = strzone("|");
475 hud_field[1] = SP_SEPARATOR;
476 hud_size[1] = stringwidth("|", false, hud_fontsize);
478 LOG_INFO("fixed missing field '|'\n");
481 else if(!have_separator)
483 strunzone(hud_title[hud_num_fields]);
484 hud_title[hud_num_fields] = strzone("|");
485 hud_size[hud_num_fields] = stringwidth("|", false, hud_fontsize);
486 hud_field[hud_num_fields] = SP_SEPARATOR;
488 LOG_INFO("fixed missing field '|'\n");
492 strunzone(hud_title[hud_num_fields]);
493 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_secondary]));
494 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
495 hud_field[hud_num_fields] = ps_secondary;
497 LOG_INFOF("fixed missing field '%s'\n", scores_label[ps_secondary]);
501 strunzone(hud_title[hud_num_fields]);
502 hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_primary]));
503 hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize);
504 hud_field[hud_num_fields] = ps_primary;
506 LOG_INFOF("fixed missing field '%s'\n", scores_label[ps_primary]);
510 hud_field[hud_num_fields] = SP_END;
514 vector hud_field_rgb;
515 string hud_field_icon0;
516 string hud_field_icon1;
517 string hud_field_icon2;
518 vector hud_field_icon0_rgb;
519 vector hud_field_icon1_rgb;
520 vector hud_field_icon2_rgb;
521 float hud_field_icon0_alpha;
522 float hud_field_icon1_alpha;
523 float hud_field_icon2_alpha;
524 string HUD_GetField(entity pl, int field)
527 float tmp, num, denom;
530 hud_field_rgb = '1 1 1';
531 hud_field_icon0 = "";
532 hud_field_icon1 = "";
533 hud_field_icon2 = "";
534 hud_field_icon0_rgb = '1 1 1';
535 hud_field_icon1_rgb = '1 1 1';
536 hud_field_icon2_rgb = '1 1 1';
537 hud_field_icon0_alpha = 1;
538 hud_field_icon1_alpha = 1;
539 hud_field_icon2_alpha = 1;
544 return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6
545 //str = getplayerkeyvalue(pl.sv_entnum, "ping");
549 tmp = max(0, min(220, f-80)) / 220;
550 hud_field_rgb = '1 1 1' - '0 1 1'*tmp;
556 f = pl.ping_packetloss;
557 tmp = pl.ping_movementloss;
558 if(f == 0 && tmp == 0)
560 str = ftos(ceil(f * 100));
562 str = strcat(str, "~", ftos(ceil(tmp * 100)));
563 tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
564 hud_field_rgb = '1 0.5 0.5' - '0 0.5 0.5'*tmp;
568 if(ready_waiting && pl.ready)
570 hud_field_icon0 = "gfx/scoreboard/player_ready";
574 f = stof(getplayerkeyvalue(pl.sv_entnum, "colors"));
576 hud_field_icon0 = "gfx/scoreboard/playercolor_base";
577 hud_field_icon1 = "gfx/scoreboard/playercolor_shirt";
578 hud_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
579 hud_field_icon2 = "gfx/scoreboard/playercolor_pants";
580 hud_field_icon2_rgb = colormapPaletteColor(f % 16, 1);
583 return entcs_GetName(pl.sv_entnum);
586 f = pl.(scores[SP_KILLS]);
587 f -= pl.(scores[SP_SUICIDES]);
591 num = pl.(scores[SP_KILLS]);
592 denom = pl.(scores[SP_DEATHS]);
595 hud_field_rgb = '0 1 0';
596 str = sprintf("%d", num);
597 } else if(num <= 0) {
598 hud_field_rgb = '1 0 0';
599 str = sprintf("%.1f", num/denom);
601 str = sprintf("%.1f", num/denom);
605 f = pl.(scores[SP_KILLS]);
606 f -= pl.(scores[SP_DEATHS]);
609 hud_field_rgb = '0 1 0';
611 hud_field_rgb = '1 1 1';
613 hud_field_rgb = '1 0 0';
618 num = pl.(scores[SP_DMG]);
621 str = sprintf("%.1f k", num/denom);
625 num = pl.(scores[SP_DMGTAKEN]);
628 str = sprintf("%.1f k", num/denom);
632 tmp = pl.(scores[field]);
633 f = scores_flags[field];
634 if(field == ps_primary)
635 hud_field_rgb = '1 1 0';
636 else if(field == ps_secondary)
637 hud_field_rgb = '0 1 1';
639 hud_field_rgb = '1 1 1';
640 return ScoreString(f, tmp);
645 float hud_fixscoreboardcolumnwidth_len;
646 float hud_fixscoreboardcolumnwidth_iconlen;
647 float hud_fixscoreboardcolumnwidth_marginlen;
649 string HUD_FixScoreboardColumnWidth(int i, string str)
654 field = hud_field[i];
656 hud_fixscoreboardcolumnwidth_iconlen = 0;
658 if(hud_field_icon0 != "")
660 sz = draw_getimagesize(hud_field_icon0);
662 if(hud_fixscoreboardcolumnwidth_iconlen < f)
663 hud_fixscoreboardcolumnwidth_iconlen = f;
666 if(hud_field_icon1 != "")
668 sz = draw_getimagesize(hud_field_icon1);
670 if(hud_fixscoreboardcolumnwidth_iconlen < f)
671 hud_fixscoreboardcolumnwidth_iconlen = f;
674 if(hud_field_icon2 != "")
676 sz = draw_getimagesize(hud_field_icon2);
678 if(hud_fixscoreboardcolumnwidth_iconlen < f)
679 hud_fixscoreboardcolumnwidth_iconlen = f;
682 hud_fixscoreboardcolumnwidth_iconlen *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect
684 if(hud_fixscoreboardcolumnwidth_iconlen != 0)
685 hud_fixscoreboardcolumnwidth_marginlen = stringwidth(" ", false, hud_fontsize);
687 hud_fixscoreboardcolumnwidth_marginlen = 0;
689 if(field == SP_NAME) // name gets all remaining space
693 namesize = panel_size.x;
694 for(j = 0; j < hud_num_fields; ++j)
696 if (hud_field[i] != SP_SEPARATOR)
697 namesize -= hud_size[j] + hud_fontsize.x;
698 namesize += hud_fontsize.x;
699 hud_size[i] = namesize;
701 if (hud_fixscoreboardcolumnwidth_iconlen != 0)
702 namesize -= hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
703 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
704 hud_fixscoreboardcolumnwidth_len = stringwidth(str, true, hud_fontsize);
707 hud_fixscoreboardcolumnwidth_len = stringwidth(str, false, hud_fontsize);
709 f = hud_fixscoreboardcolumnwidth_len + hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen;
716 void HUD_PrintScoreboardItem(vector item_pos, vector rgb, entity pl, bool is_self, int pl_number)
718 TC(bool, is_self); TC(int, pl_number);
722 is_spec = (entcs_GetTeam(pl.sv_entnum) == NUM_SPECTATOR);
723 if(is_spec && !is_self)
726 vector h_pos = item_pos;
727 vector h_size = eX * panel_size.x + eY * hud_fontsize.y * 1.25;
728 // alternated rows highlighting
730 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
731 else if((scoreboard_highlight) && (!(pl_number % 2)))
732 drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
734 vector pos = item_pos;
735 vector tmp = '0 0 0';
737 for(i = 0; i < hud_num_fields; ++i)
739 field = hud_field[i];
740 if(field == SP_SEPARATOR)
743 if(is_spec && field != SP_NAME && field != SP_PING) {
744 pos.x += hud_size[i] + hud_fontsize.x;
747 str = HUD_GetField(pl, field);
748 str = HUD_FixScoreboardColumnWidth(i, str);
750 pos.x += hud_size[i] + hud_fontsize.x;
752 if(field == SP_NAME) {
753 tmp.x = hud_size[i] - hud_fontsize.x*hud_fixscoreboardcolumnwidth_iconlen - hud_fixscoreboardcolumnwidth_marginlen + hud_fontsize.x;
755 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
757 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
759 tmp.x = hud_fixscoreboardcolumnwidth_len + hud_fontsize.x;
761 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
763 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
766 tmp.x = hud_size[i] + hud_fontsize.x;
767 if(hud_field_icon0 != "")
769 drawpic(pos - tmp, hud_field_icon0, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon0_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
771 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);
772 if(hud_field_icon1 != "")
774 drawpic(pos - tmp, hud_field_icon1, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon1_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
776 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);
777 if(hud_field_icon2 != "")
779 drawpic(pos - tmp, hud_field_icon2, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon2_rgb, hud_field_icon2_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
781 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);
784 if(hud_field[i] == SP_SEPARATOR)
786 pos.x = item_pos.x + panel_size.x;
787 for(i = hud_num_fields-1; i > 0; --i)
789 field = hud_field[i];
790 if(field == SP_SEPARATOR)
793 if(is_spec && field != SP_NAME && field != SP_PING) {
794 pos.x -= hud_size[i] + hud_fontsize.x;
798 str = HUD_GetField(pl, field);
799 str = HUD_FixScoreboardColumnWidth(i, str);
801 if(field == SP_NAME) {
802 tmp.x = hud_fixscoreboardcolumnwidth_len; // left or right aligned? let's put it right...
804 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
806 drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL);
808 tmp.x = hud_fixscoreboardcolumnwidth_len;
810 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL);
812 drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL);
816 if(hud_field_icon0 != "")
818 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);
820 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);
821 if(hud_field_icon1 != "")
823 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);
825 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);
826 if(hud_field_icon2 != "")
828 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);
830 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);
831 pos.x -= hud_size[i] + hud_fontsize.x;
836 drawfill(h_pos, h_size, '0 0 0', 0.5, DRAWFLAG_NORMAL);
839 vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
841 vector column_dim = '0 0 0';
845 panel_size.y = 1.25 * hud_fontsize.y * (1 + max(1, tm.team_size));
846 panel_size.y += panel_bg_padding * 2;
847 HUD_Panel_DrawBg(scoreboard_fade_alpha);
849 vector end_pos = panel_pos + eY * (panel_size.y + panel_bg_border * 2 + hud_fontsize.y);
853 panel_pos += '1 1 0' * panel_bg_padding;
854 panel_size -= '2 2 0' * panel_bg_padding;
858 vector tmp = eX * panel_size.x + eY * 1.25 * hud_fontsize.y;
861 if (scoreboard_alpha_bg)
862 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL);
864 pos.y += 1.25 * hud_fontsize.y;
867 tmp.y = panel_size.y - 1.25 * hud_fontsize.y;
868 if (scoreboard_alpha_bg)
869 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
871 // go back to the top to make alternated columns highlighting and to print the strings
872 pos.y -= 1.25 * hud_fontsize.y;
874 if (scoreboard_highlight)
875 column_dim.y = panel_size.y;
877 // print the strings of the columns headers and draw the columns
879 for(i = 0; i < hud_num_fields; ++i)
881 if(hud_field[i] == SP_SEPARATOR)
883 column_dim.x = hud_size[i] + hud_fontsize.x;
884 if (scoreboard_highlight)
887 drawfill(pos - eX * hud_fontsize.x * 0.5, column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
889 drawstring(pos, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
890 pos.x += column_dim.x;
892 if(hud_field[i] == SP_SEPARATOR)
894 pos.x = panel_pos.x + panel_size.x;
896 for(i = hud_num_fields - 1; i > 0; --i)
898 if(hud_field[i] == SP_SEPARATOR)
901 pos.x -= hud_size[i];
903 if (scoreboard_highlight)
907 if (i == hud_num_fields-1)
908 column_dim.x = hud_size[i] + hud_fontsize.x * 0.5;
910 column_dim.x = hud_size[i] + hud_fontsize.x;
911 if(i == hud_num_fields - 1)
912 drawfill(pos - eX * hud_fontsize.x * 0.5, column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
914 drawfill(pos - eX * hud_fontsize.x * 0.5, column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL);
918 tmp.x = stringwidth(hud_title[i], false, hud_fontsize);
919 tmp.x = (hud_size[i] - tmp.x);
920 drawstring(pos + tmp, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
921 pos.x -= hud_fontsize.x;
926 pos.y += 1.25 * hud_fontsize.y; // skip the header
929 tmp.x = panel_size.x;
930 tmp.y = hud_fontsize.y * 1.25;
932 // fill the table and draw the rows
935 for(pl = players.sort_next; pl; pl = pl.sort_next)
937 if(pl.team != tm.team)
939 HUD_PrintScoreboardItem(pos, rgb, pl, (pl.sv_entnum == player_localnum), i);
940 pos.y += 1.25 * hud_fontsize.y;
944 for(pl = players.sort_next; pl; pl = pl.sort_next)
946 if(pl.team == NUM_SPECTATOR)
948 HUD_PrintScoreboardItem(pos, rgb, pl, (pl.sv_entnum == player_localnum), i);
949 pos.y += 1.25 * hud_fontsize.y;
953 panel_size.x += panel_bg_padding * 2; // restore initial width
957 float HUD_WouldDrawScoreboard() {
958 if (QuickMenu_IsOpened())
960 else if (HUD_Radar_Clickable())
962 else if (scoreboard_showscores)
964 else if (intermission == 1)
966 else if (intermission == 2)
968 else if (spectatee_status != -1 && STAT(HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS && !active_minigame)
970 else if (scoreboard_showscores_force)
975 float average_accuracy;
976 vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size)
978 WepSet weapons_stat = WepSet_GetFromStat();
979 WepSet weapons_inmap = WepSet_GetFromStat_InMap();
981 FOREACH(Weapons, it != WEP_Null, {
982 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
984 WepSet set = it.m_wepset;
985 if (weapon_stats < 0 && !(weapons_stat & set || weapons_inmap & set))
989 int weapon_cnt = (Weapons_COUNT - 1) - disownedcnt;
990 if (weapon_cnt <= 0) return pos;
993 if (autocvar_scoreboard_accuracy_doublerows && weapon_cnt >= floor((Weapons_COUNT - 1) * 0.5))
995 int columnns = ceil(weapon_cnt / rows);
999 drawstring(pos + eX * panel_bg_padding, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1000 pos.y += 1.25 * hud_fontsize.y;
1001 pos.y += panel_bg_border;
1004 panel_size.y = height * rows;
1005 panel_size.y += panel_bg_padding * 2;
1006 HUD_Panel_DrawBg(scoreboard_fade_alpha);
1008 vector end_pos = panel_pos + eY * (panel_size.y + panel_bg_border * 2 + hud_fontsize.y);
1010 if(panel_bg_padding)
1012 panel_pos += '1 1 0' * panel_bg_padding;
1013 panel_size -= '2 2 0' * panel_bg_padding;
1017 vector tmp = panel_size;
1019 float fontsize = height * 1/3;
1020 float weapon_height = height * 2/3;
1021 float weapon_width = tmp.x / columnns / rows;
1023 if (scoreboard_alpha_bg)
1024 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1026 // column highlighting
1027 for (int i = 0; i < columnns; ++i)
1030 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);
1034 for (int i = 0; i < rows; ++i)
1036 drawfill(pos + '0 1 0' * weapon_height + '0 1 0' * height * i, '1 0 0' * tmp.x + '0 1 0' * fontsize, '1 1 1', scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1039 average_accuracy = 0;
1040 int weapons_with_stats = 0;
1042 pos.x += weapon_width / 2;
1044 if (autocvar_scoreboard_accuracy_nocolors)
1047 Accuracy_LoadColors();
1049 float oldposx = pos.x;
1053 FOREACH(Weapons, it != WEP_Null, {
1054 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1056 WepSet set = it.m_wepset;
1057 if (weapon_stats < 0 && !(weapons_stat & set || weapons_inmap & set))
1061 if (weapon_stats >= 0)
1062 weapon_alpha = scoreboard_alpha_fg;
1064 weapon_alpha = 0.2 * scoreboard_alpha_fg;
1067 drawpic_aspect_skin(tmpos, it.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL);
1069 if (weapon_stats >= 0) {
1070 weapons_with_stats += 1;
1071 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1074 s = sprintf("%d%%", weapon_stats*100);
1077 padding = (weapon_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center the accuracy value
1079 if(!autocvar_scoreboard_accuracy_nocolors)
1080 rgb = Accuracy_GetColor(weapon_stats);
1082 drawstring(tmpos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1084 tmpos.x += weapon_width * rows;
1085 pos.x += weapon_width * rows;
1086 if (rows == 2 && column == columnns - 1) {
1094 if (weapons_with_stats)
1095 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1097 panel_size.x += panel_bg_padding * 2; // restore initial width
1101 vector HUD_DrawKeyValue(vector pos, string key, string value) {
1103 pos.x += hud_fontsize.x * 0.25;
1104 drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1105 pos.x = panel_pos.x + panel_size.x - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1106 drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1108 pos.y += hud_fontsize.y;
1113 vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) {
1114 float stat_secrets_found, stat_secrets_total;
1115 float stat_monsters_killed, stat_monsters_total;
1119 // get monster stats
1120 stat_monsters_killed = STAT(MONSTERS_KILLED);
1121 stat_monsters_total = STAT(MONSTERS_TOTAL);
1123 // get secrets stats
1124 stat_secrets_found = STAT(SECRETS_FOUND);
1125 stat_secrets_total = STAT(SECRETS_TOTAL);
1127 // get number of rows
1128 if(stat_secrets_total)
1130 if(stat_monsters_total)
1133 // if no rows, return
1137 // draw table header
1138 drawstring(pos + eX * panel_bg_padding, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1139 pos.y += 1.25 * hud_fontsize.y;
1140 pos.y += panel_bg_border;
1143 panel_size.y = hud_fontsize.y * rows;
1144 panel_size.y += panel_bg_padding * 2;
1145 HUD_Panel_DrawBg(scoreboard_fade_alpha);
1147 vector end_pos = panel_pos + eY * (panel_size.y + panel_bg_border * 2 + hud_fontsize.y);
1149 if(panel_bg_padding)
1151 panel_pos += '1 1 0' * panel_bg_padding;
1152 panel_size -= '2 2 0' * panel_bg_padding;
1156 vector tmp = panel_size;
1158 if (scoreboard_alpha_bg)
1159 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1162 if(stat_monsters_total)
1164 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1165 pos = HUD_DrawKeyValue(pos, _("Monsters killed:"), val);
1169 if(stat_secrets_total)
1171 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1172 pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val);
1175 panel_size.x += panel_bg_padding * 2; // restore initial width
1180 vector HUD_DrawScoreboardRankings(vector pos, entity pl, vector rgb, vector bg_size)
1183 RANKINGS_RECEIVED_CNT = 0;
1184 for (i=RANKINGS_CNT-1; i>=0; --i)
1186 ++RANKINGS_RECEIVED_CNT;
1188 if (RANKINGS_RECEIVED_CNT == 0)
1191 vector hl_rgb = rgb + '0.5 0.5 0.5';
1193 pos.y += hud_fontsize.y;
1194 drawstring(pos + eX * panel_bg_padding, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1195 pos.y += 1.25 * hud_fontsize.y;
1196 pos.y += panel_bg_border;
1199 panel_size.y = 1.25 * hud_fontsize.y * RANKINGS_RECEIVED_CNT;
1200 panel_size.y += panel_bg_padding * 2;
1201 HUD_Panel_DrawBg(scoreboard_fade_alpha);
1203 vector end_pos = panel_pos + eY * (panel_size.y + panel_bg_border * 2 + hud_fontsize.y);
1205 if(panel_bg_padding)
1207 panel_pos += '1 1 0' * panel_bg_padding;
1208 panel_size -= '2 2 0' * panel_bg_padding;
1212 vector tmp = panel_size;
1214 if (scoreboard_alpha_bg)
1215 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL);
1218 for(i = 0; i<RANKINGS_RECEIVED_CNT; ++i)
1225 n = grecordholder[i];
1226 p = count_ordinal(i+1);
1227 if(grecordholder[i] == entcs_GetName(player_localnum))
1228 drawfill(pos, '1 0 0' * panel_size.x + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
1229 else if(!(i % 2) && scoreboard_highlight)
1230 drawfill(pos, '1 0 0' * panel_size.x + '0 1.25 0' * hud_fontsize.y, hl_rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
1231 drawstring(pos, p, '1 1 0' * hud_fontsize.y, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1232 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);
1233 drawcolorcodedstring(pos + '8 0 0' * hud_fontsize.y, n, '1 1 0' * hud_fontsize.y, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1234 pos.y += 1.25 * hud_fontsize.y;
1237 panel_size.x += panel_bg_padding * 2; // restore initial width
1241 float hud_woulddrawscoreboard_prev;
1242 float hud_woulddrawscoreboard_change; // "time" at which HUD_WouldDrawScoreboard() changed
1243 void HUD_DrawScoreboard()
1245 if(!autocvar__hud_configure)
1247 float hud_woulddrawscoreboard;
1248 hud_woulddrawscoreboard = scoreboard_active;
1249 if(hud_woulddrawscoreboard != hud_woulddrawscoreboard_prev) {
1250 hud_woulddrawscoreboard_change = time;
1251 hud_woulddrawscoreboard_prev = hud_woulddrawscoreboard;
1254 if(hud_woulddrawscoreboard) {
1255 if(menu_enabled == 1)
1256 scoreboard_fade_alpha = 1;
1257 float scoreboard_fadeinspeed = autocvar_scoreboard_fadeinspeed;
1258 if (scoreboard_fadeinspeed)
1259 scoreboard_fade_alpha = bound (0, (time - hud_woulddrawscoreboard_change) * scoreboard_fadeinspeed, 1);
1261 scoreboard_fade_alpha = 1;
1264 float scoreboard_fadeoutspeed = autocvar_scoreboard_fadeoutspeed;
1265 if (scoreboard_fadeoutspeed)
1266 scoreboard_fade_alpha = bound (0, (1/scoreboard_fadeoutspeed - (time - hud_woulddrawscoreboard_change)) * scoreboard_fadeoutspeed, 1);
1268 scoreboard_fade_alpha = 0;
1271 if (!scoreboard_fade_alpha)
1275 scoreboard_fade_alpha = 0;
1277 if (autocvar_scoreboard_dynamichud)
1280 HUD_Scale_Disable();
1282 float hud_fade_alpha_save = hud_fade_alpha;
1283 if(menu_enabled == 1)
1286 hud_fade_alpha = scoreboard_fade_alpha * (1 - autocvar__menu_alpha);
1287 HUD_Panel_UpdateCvars();
1289 scoreboard_alpha_bg = panel_bg_alpha;
1290 scoreboard_alpha_fg = panel_fg_alpha;
1291 scoreboard_highlight = autocvar_scoreboard_highlight;
1292 scoreboard_highlight_alpha = autocvar_scoreboard_highlight_alpha * panel_fg_alpha;
1293 scoreboard_highlight_alpha_self = autocvar_scoreboard_highlight_alpha_self * panel_fg_alpha;
1294 scoreboard_alpha_name = autocvar_scoreboard_alpha_name * panel_fg_alpha;
1295 scoreboard_alpha_name_self = autocvar_scoreboard_alpha_name_self * panel_fg_alpha;
1297 hud_fade_alpha = hud_fade_alpha_save;
1299 // don't overlap with con_notify
1300 if(!autocvar__hud_configure)
1301 panel_pos.y = max((autocvar_con_notify * autocvar_con_notifysize), panel_pos.y);
1303 HUD_UpdatePlayerTeams();
1309 // Initializes position
1313 vector sb_heading_fontsize;
1314 sb_heading_fontsize = hud_fontsize * 2;
1315 draw_beginBoldFont();
1316 drawstring(pos + eX * panel_bg_padding, _("Scoreboard"), sb_heading_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1319 pos.y += sb_heading_fontsize.y;
1320 pos.y += panel_bg_border;
1322 // Draw the scoreboard
1323 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * ((autocvar_scoreboard_bg_scale > 0) ? autocvar_scoreboard_bg_scale : 0.25);
1327 vector team_score_baseoffset = eY * hud_fontsize.y - eX * (panel_bg_border + hud_fontsize.x * 0.5);
1328 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1330 if(tm.team == NUM_SPECTATOR)
1332 if(!tm.team && teamplay)
1335 draw_beginBoldFont();
1336 vector rgb = Team_ColorRGB(tm.team);
1337 str = ftos(tm.(teamscores[ts_primary]));
1338 drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1340 if(ts_primary != ts_secondary)
1342 str = ftos(tm.(teamscores[ts_secondary]));
1343 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);
1347 panel_bg_color = rgb * panel_bg_color_team;
1348 pos = HUD_Scoreboard_MakeTable(pos, tm, panel_bg_color, bg_size);
1350 panel_bg_color = Team_ColorRGB(myteam) * panel_bg_color_team;
1354 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1356 if(tm.team == NUM_SPECTATOR)
1358 if(!tm.team && teamplay)
1361 pos = HUD_Scoreboard_MakeTable(pos, tm, panel_bg_color, bg_size);
1365 if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) {
1366 if(race_speedaward) {
1367 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);
1368 pos.y += 1.25 * hud_fontsize.y;
1370 if(race_speedaward_alltimebest) {
1371 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);
1372 pos.y += 1.25 * hud_fontsize.y;
1374 pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], panel_bg_color, bg_size);
1376 else if (autocvar_scoreboard_accuracy && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) {
1377 pos = HUD_DrawScoreboardAccuracyStats(pos, panel_bg_color, bg_size);
1380 pos = HUD_DrawMapStats(pos, panel_bg_color, bg_size);
1385 for(pl = players.sort_next; pl; pl = pl.sort_next)
1387 if(pl.team != NUM_SPECTATOR)
1389 pos.y += 1.25 * hud_fontsize.y;
1390 HUD_PrintScoreboardItem(pos, panel_bg_color, pl, (pl.sv_entnum == player_localnum), specs);
1396 draw_beginBoldFont();
1397 drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1399 pos.y += 1.25 * hud_fontsize.y;
1402 // Print info string
1404 str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname);
1405 tl = STAT(TIMELIMIT);
1406 fl = STAT(FRAGLIMIT);
1407 ll = STAT(LEADLIMIT);
1408 if(gametype == MAPINFO_TYPE_LMS)
1411 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1416 str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl));
1420 str = strcat(str, _(" or"));
1423 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl),
1424 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1425 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1426 TranslateScoresLabel(teamscores_label[ts_primary])));
1430 str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl),
1431 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1432 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1433 TranslateScoresLabel(scores_label[ps_primary])));
1438 if(tl > 0 || fl > 0)
1439 str = strcat(str, _(" or"));
1442 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll),
1443 (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) :
1444 (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1445 TranslateScoresLabel(teamscores_label[ts_primary])));
1449 str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll),
1450 (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) :
1451 (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) :
1452 TranslateScoresLabel(scores_label[ps_primary])));
1457 pos.y += 1.2 * hud_fontsize.y;
1458 drawcolorcodedstring(pos + '0.5 0 0' * (panel_size.x - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1460 // print information about respawn status
1461 float respawn_time = STAT(RESPAWN_TIME);
1465 if(respawn_time < 0)
1467 // a negative number means we are awaiting respawn, time value is still the same
1468 respawn_time *= -1; // remove mark now that we checked it
1469 respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag)
1471 str = sprintf(_("^1Respawning in ^3%s^1..."),
1472 (autocvar_scoreboard_respawntime_decimals ?
1473 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1475 count_seconds(respawn_time - time)
1479 else if(time < respawn_time)
1481 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
1482 (autocvar_scoreboard_respawntime_decimals ?
1483 count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals)
1485 count_seconds(respawn_time - time)
1489 else if(time >= respawn_time)
1490 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
1492 pos.y += 1.2 * hud_fontsize.y;
1493 drawcolorcodedstring(pos + '0.5 0 0' * (panel_size.x - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL);
1496 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;