1 #include "scoreboard.qh"
3 #include <client/autocvars.qh>
4 #include <client/defs.qh>
5 #include <client/main.qh>
6 #include <client/miscfunctions.qh>
7 #include "quickmenu.qh"
8 #include <common/ent_cs.qh>
9 #include <common/constants.qh>
10 #include <common/net_linked.qh>
11 #include <common/mapinfo.qh>
12 #include <common/minigames/cl_minigames.qh>
13 #include <common/scores.qh>
14 #include <common/stats.qh>
15 #include <common/teams.qh>
19 const int MAX_SBT_FIELDS = MAX_SCORE;
21 PlayerScoreField sbt_field[MAX_SBT_FIELDS + 1];
22 float sbt_field_size[MAX_SBT_FIELDS + 1];
23 string sbt_field_title[MAX_SBT_FIELDS + 1];
26 string autocvar_hud_fontsize;
27 string hud_fontsize_str;
32 float sbt_fg_alpha_self;
34 float sbt_highlight_alpha;
35 float sbt_highlight_alpha_self;
36 int sbt_team_size_position;
38 // provide basic panel cvars to old clients
39 // TODO remove them after a future release (0.8.2+)
40 noref string autocvar_hud_panel_scoreboard_pos = "0.150000 0.150000";
41 noref string autocvar_hud_panel_scoreboard_size = "0.700000 0.700000";
42 noref string autocvar_hud_panel_scoreboard_bg = "border_default";
43 noref string autocvar_hud_panel_scoreboard_bg_color = "0 0.3 0.5";
44 noref string autocvar_hud_panel_scoreboard_bg_color_team = "";
45 noref string autocvar_hud_panel_scoreboard_bg_alpha = "0.7";
46 noref string autocvar_hud_panel_scoreboard_bg_border = "";
47 noref string autocvar_hud_panel_scoreboard_bg_padding = "";
49 float autocvar_hud_panel_scoreboard_fadeinspeed = 10;
50 float autocvar_hud_panel_scoreboard_fadeoutspeed = 5;
51 float autocvar_hud_panel_scoreboard_respawntime_decimals = 1;
52 float autocvar_hud_panel_scoreboard_table_bg_alpha = 0;
53 float autocvar_hud_panel_scoreboard_table_bg_scale = 0.25;
54 float autocvar_hud_panel_scoreboard_table_fg_alpha = 0.9;
55 float autocvar_hud_panel_scoreboard_table_fg_alpha_self = 1;
56 bool autocvar_hud_panel_scoreboard_table_highlight = true;
57 float autocvar_hud_panel_scoreboard_table_highlight_alpha = 0.2;
58 float autocvar_hud_panel_scoreboard_table_highlight_alpha_self = 0.4;
59 float autocvar_hud_panel_scoreboard_bg_teams_color_team = 0;
60 float autocvar_hud_panel_scoreboard_namesize = 15;
61 float autocvar_hud_panel_scoreboard_team_size_position = 0;
63 bool autocvar_hud_panel_scoreboard_accuracy = true;
64 bool autocvar_hud_panel_scoreboard_accuracy_doublerows = false;
65 bool autocvar_hud_panel_scoreboard_accuracy_nocolors = false;
66 float autocvar_hud_panel_scoreboard_accuracy_showdelay = 2;
67 float autocvar_hud_panel_scoreboard_accuracy_showdelay_minpos = 0.75;
69 bool autocvar_hud_panel_scoreboard_ctf_leaderboard = true;
71 bool autocvar_hud_panel_scoreboard_dynamichud = false;
73 float autocvar_hud_panel_scoreboard_maxheight = 0.6;
74 bool autocvar_hud_panel_scoreboard_others_showscore = true;
75 bool autocvar_hud_panel_scoreboard_spectators_showping = true;
76 bool autocvar_hud_panel_scoreboard_spectators_aligned = false;
77 float autocvar_hud_panel_scoreboard_minwidth = 0.4;
79 // mode 0: returns translated label
80 // mode 1: prints name and description of all the labels
81 string Label_getInfo(string label, int mode)
84 label = "bckills"; // first case in the switch
88 case "bckills": if (!mode) return CTX(_("SCO^bckills")); else LOG_INFO(strcat("^3", "bckills", " ^7", _("Number of ball carrier kills")));
89 case "bctime": if (!mode) return CTX(_("SCO^bctime")); else LOG_INFO(strcat("^3", "bctime", " ^7", _("Total amount of time holding the ball in Keepaway")));
90 case "caps": if (!mode) return CTX(_("SCO^caps")); else LOG_INFO(strcat("^3", "caps", " ^7", _("How often a flag (CTF) or a key (KeyHunt) was captured")));
91 case "captime": if (!mode) return CTX(_("SCO^captime")); else LOG_INFO(strcat("^3", "captime", " ^7", _("Time of fastest capture (CTF)")));
92 case "deaths": if (!mode) return CTX(_("SCO^deaths")); else LOG_INFO(strcat("^3", "deaths", " ^7", _("Number of deaths")));
93 case "destroyed": if (!mode) return CTX(_("SCO^destroyed")); else LOG_INFO(strcat("^3", "destroyed", " ^7", _("Number of keys destroyed by pushing them into void")));
94 case "dmg": if (!mode) return CTX(_("SCO^damage")); else LOG_INFO(strcat("^3", "dmg", " ^7", _("The total damage done")));
95 case "dmgtaken": if (!mode) return CTX(_("SCO^dmgtaken")); else LOG_INFO(strcat("^3", "dmgtaken", " ^7", _("The total damage taken")));
96 case "drops": if (!mode) return CTX(_("SCO^drops")); else LOG_INFO(strcat("^3", "drops", " ^7", _("Number of flag drops")));
97 case "elo": if (!mode) return CTX(_("SCO^elo")); else LOG_INFO(strcat("^3", "elo", " ^7", _("Player ELO")));
98 case "fastest": if (!mode) return CTX(_("SCO^fastest")); else LOG_INFO(strcat("^3", "fastest", " ^7", _("Time of fastest lap (Race/CTS)")));
99 case "faults": if (!mode) return CTX(_("SCO^faults")); else LOG_INFO(strcat("^3", "faults", " ^7", _("Number of faults committed")));
100 case "fckills": if (!mode) return CTX(_("SCO^fckills")); else LOG_INFO(strcat("^3", "fckills", " ^7", _("Number of flag carrier kills")));
101 case "fps": if (!mode) return CTX(_("SCO^fps")); else LOG_INFO(strcat("^3", "fps", " ^7", _("FPS")));
102 case "frags": if (!mode) return CTX(_("SCO^frags")); else LOG_INFO(strcat("^3", "frags", " ^7", _("Number of kills minus suicides")));
103 case "goals": if (!mode) return CTX(_("SCO^goals")); else LOG_INFO(strcat("^3", "goals", " ^7", _("Number of goals scored")));
104 case "kckills": if (!mode) return CTX(_("SCO^kckills")); else LOG_INFO(strcat("^3", "kckills", " ^7", _("Number of keys carrier kills")));
105 case "kd": if (!mode) return CTX(_("SCO^k/d")); else LOG_INFO(strcat("^3", "kd", " ^7", _("The kill-death ratio")));
106 case "kdr": if (!mode) return CTX(_("SCO^kdr")); else LOG_INFO(strcat("^3", "kdr", " ^7", _("The kill-death ratio")));
107 case "kdratio": if (!mode) return CTX(_("SCO^kdratio")); else LOG_INFO(strcat("^3", "kdratio", " ^7", _("The kill-death ratio")));
108 case "kills": if (!mode) return CTX(_("SCO^kills")); else LOG_INFO(strcat("^3", "kills", " ^7", _("Number of kills")));
109 case "laps": if (!mode) return CTX(_("SCO^laps")); else LOG_INFO(strcat("^3", "laps", " ^7", _("Number of laps finished (Race/CTS)")));
110 case "lives": if (!mode) return CTX(_("SCO^lives")); else LOG_INFO(strcat("^3", "lives", " ^7", _("Number of lives (LMS)")));
111 case "losses": if (!mode) return CTX(_("SCO^losses")); else LOG_INFO(strcat("^3", "losses", " ^7", _("Number of times a key was lost")));
112 case "name": if (!mode) return CTX(_("SCO^name")); else LOG_INFO(strcat("^3", "name", " ^7", _("Player name")));
113 case "nick": if (!mode) return CTX(_("SCO^nick")); else LOG_INFO(strcat("^3", "nick", " ^7", _("Player name")));
114 case "objectives": if (!mode) return CTX(_("SCO^objectives")); else LOG_INFO(strcat("^3", "objectives", " ^7", _("Number of objectives destroyed")));
115 case "pickups": if (!mode) return CTX(_("SCO^pickups")); else LOG_INFO(strcat("^3", "pickups", " ^7", _("How often a flag (CTF) or a key (KeyHunt) or a ball (Keepaway) was picked up")));
116 case "ping": if (!mode) return CTX(_("SCO^ping")); else LOG_INFO(strcat("^3", "ping", " ^7", _("Ping time")));
117 case "pl": if (!mode) return CTX(_("SCO^pl")); else LOG_INFO(strcat("^3", "pl", " ^7", _("Packet loss")));
118 case "pushes": if (!mode) return CTX(_("SCO^pushes")); else LOG_INFO(strcat("^3", "pushes", " ^7", _("Number of players pushed into void")));
119 case "rank": if (!mode) return CTX(_("SCO^rank")); else LOG_INFO(strcat("^3", "rank", " ^7", _("Player rank")));
120 case "returns": if (!mode) return CTX(_("SCO^returns")); else LOG_INFO(strcat("^3", "returns", " ^7", _("Number of flag returns")));
121 case "revivals": if (!mode) return CTX(_("SCO^revivals")); else LOG_INFO(strcat("^3", "revivals", " ^7", _("Number of revivals")));
122 case "rounds": if (!mode) return CTX(_("SCO^rounds won")); else LOG_INFO(strcat("^3", "rounds", " ^7", _("Number of rounds won")));
123 case "score": if (!mode) return CTX(_("SCO^score")); else LOG_INFO(strcat("^3", "score", " ^7", _("Total score")));
124 case "suicides": if (!mode) return CTX(_("SCO^suicides")); else LOG_INFO(strcat("^3", "suicides", " ^7", _("Number of suicides")));
125 case "sum": if (!mode) return CTX(_("SCO^sum")); else LOG_INFO(strcat("^3", "sum", " ^7", _("Number of kills minus deaths")));
126 case "takes": if (!mode) return CTX(_("SCO^takes")); else LOG_INFO(strcat("^3", "takes", " ^7", _("Number of domination points taken (Domination)")));
127 case "teamkills": if (!mode) return CTX(_("SCO^teamkills")); else LOG_INFO(strcat("^3", "teamkills", " ^7", _("Number of teamkills")));
128 case "ticks": if (!mode) return CTX(_("SCO^ticks")); else LOG_INFO(strcat("^3", "ticks", " ^7", _("Number of ticks (Domination)")));
129 case "time": if (!mode) return CTX(_("SCO^time")); else LOG_INFO(strcat("^3", "time", " ^7", _("Total time raced (Race/CTS)")));
130 default: return label;
135 void PrintScoresLabels() { Label_getInfo(string_null, 1); }
136 string TranslateScoresLabel(string label) { return Label_getInfo(label, 0); }
138 void Scoreboard_InitScores()
142 ps_primary = ps_secondary = NULL;
143 ts_primary = ts_secondary = -1;
144 FOREACH(Scores, true, {
145 f = (scores_flags(it) & SFL_SORT_PRIO_MASK);
146 if(f == SFL_SORT_PRIO_PRIMARY)
148 if(f == SFL_SORT_PRIO_SECONDARY)
151 if(ps_secondary == NULL)
152 ps_secondary = ps_primary;
154 for(i = 0; i < MAX_TEAMSCORE; ++i)
156 f = (teamscores_flags(i) & SFL_SORT_PRIO_MASK);
157 if(f == SFL_SORT_PRIO_PRIMARY)
159 if(f == SFL_SORT_PRIO_SECONDARY)
162 if(ts_secondary == -1)
163 ts_secondary = ts_primary;
165 Cmd_Scoreboard_SetFields(0);
169 void Scoreboard_UpdatePlayerTeams()
173 for(pl = players.sort_next; pl; pl = pl.sort_next)
176 int Team = entcs_GetScoreTeam(pl.sv_entnum);
177 if(SetTeam(pl, Team))
180 Scoreboard_UpdatePlayerPos(pl);
184 pl = players.sort_next;
189 print(strcat("PNUM: ", ftos(num), "\n"));
194 int Scoreboard_CompareScore(int vl, int vr, int f)
196 TC(int, vl); TC(int, vr); TC(int, f);
197 if(f & SFL_ZERO_IS_WORST)
199 if(vl == 0 && vr != 0)
201 if(vl != 0 && vr == 0)
205 return IS_INCREASING(f);
207 return IS_DECREASING(f);
211 float Scoreboard_ComparePlayerScores(entity left, entity right)
214 vl = entcs_GetTeam(left.sv_entnum);
215 vr = entcs_GetTeam(right.sv_entnum);
227 if(vl == NUM_SPECTATOR)
229 // FIRST the one with scores (spectators), THEN the ones without (downloaders)
231 if(!left.gotscores && right.gotscores)
236 r = Scoreboard_CompareScore(left.scores(ps_primary), right.scores(ps_primary), scores_flags(ps_primary));
240 r = Scoreboard_CompareScore(left.scores(ps_secondary), right.scores(ps_secondary), scores_flags(ps_secondary));
244 FOREACH(Scores, true, {
245 r = Scoreboard_CompareScore(left.scores(it), right.scores(it), scores_flags(it));
246 if (r >= 0) return r;
249 if (left.sv_entnum < right.sv_entnum)
255 void Scoreboard_UpdatePlayerPos(entity player)
258 for(ent = player.sort_next; ent && Scoreboard_ComparePlayerScores(player, ent); ent = player.sort_next)
260 SORT_SWAP(player, ent);
262 for(ent = player.sort_prev; ent != players && Scoreboard_ComparePlayerScores(ent, player); ent = player.sort_prev)
264 SORT_SWAP(ent, player);
268 float Scoreboard_CompareTeamScores(entity left, entity right)
272 if(left.team == NUM_SPECTATOR)
274 if(right.team == NUM_SPECTATOR)
277 r = Scoreboard_CompareScore(left.teamscores(ts_primary), right.teamscores(ts_primary), teamscores_flags(ts_primary));
281 r = Scoreboard_CompareScore(left.teamscores(ts_secondary), right.teamscores(ts_secondary), teamscores_flags(ts_secondary));
285 for(i = 0; i < MAX_TEAMSCORE; ++i)
287 r = Scoreboard_CompareScore(left.teamscores(i), right.teamscores(i), teamscores_flags(i));
292 if (left.team < right.team)
298 void Scoreboard_UpdateTeamPos(entity Team)
301 for(ent = Team.sort_next; ent && Scoreboard_CompareTeamScores(Team, ent); ent = Team.sort_next)
303 SORT_SWAP(Team, ent);
305 for(ent = Team.sort_prev; ent != teams && Scoreboard_CompareTeamScores(ent, Team); ent = Team.sort_prev)
307 SORT_SWAP(ent, Team);
311 void Cmd_Scoreboard_Help()
313 LOG_INFO(_("You can modify the scoreboard using the ^2scoreboard_columns_set command."));
314 LOG_INFO(_("Usage:"));
315 LOG_INFO("^2scoreboard_columns_set ^3default");
316 LOG_INFO(_("^2scoreboard_columns_set ^3field1 field2 ..."));
317 LOG_INFO(_("^2scoreboard_columns_set ^7without arguments reads the arguments from the cvar scoreboard_columns"));
318 LOG_INFO(_(" ^5Note: ^7scoreboard_columns_set without arguments is executed on every map start"));
319 LOG_INFO(_("^2scoreboard_columns_set ^3expand_default ^7loads default layout and expands it into the cvar scoreboard_columns so you can edit it"));
320 LOG_INFO(_("You can use a ^3|^7 to start the right-aligned fields."));
321 LOG_INFO(_("The following field names are recognized (case insensitive):"));
327 LOG_INFO(_("Before a field you can put a + or - sign, then a comma separated list\n"
328 "of game types, then a slash, to make the field show up only in these\n"
329 "or in all but these game types. You can also specify 'all' as a\n"
330 "field to show all fields available for the current game mode."));
333 LOG_INFO(_("The special game type names 'teams' and 'noteams' can be used to\n"
334 "include/exclude ALL teams/noteams game modes."));
337 LOG_INFO(_("Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4"));
338 LOG_INFO(_("will display name, ping and pl aligned to the left, and the fields\n"
339 "right of the vertical bar aligned to the right."));
340 LOG_INFO(_("'field3' will only be shown in CTF, and 'field4' will be shown in all\n"
341 "other gamemodes except DM."));
344 // NOTE: adding a gametype with ? to not warn for an optional field
345 // make sure it's excluded in a previous exclusive rule, if any
346 // otherwise the previous exclusive rule warns anyway
347 // e.g. -teams,rc,cts,lms/kills ?+rc/kills
348 #define SCOREBOARD_DEFAULT_COLUMNS \
349 "ping pl fps name |" \
350 " -teams,rc,cts,inv,lms/kills +ft,tdm/kills ?+rc,inv/kills" \
351 " -teams,lms/deaths +ft,tdm/deaths" \
353 " -teams,lms,rc,cts,inv,ka/suicides +ft,tdm/suicides ?+rc,inv/suicides" \
354 " -cts,dm,tdm,ka,ft/frags" /* tdm already has this in "score" */ \
355 " +tdm,ft,dom,ons,as/teamkills"\
356 " -rc,cts,nb/dmg -rc,cts,nb/dmgtaken" \
357 " +ctf/pickups +ctf/fckills +ctf/returns +ctf/caps +ons/takes +ons/caps" \
358 " +lms/lives +lms/rank" \
359 " +kh/kckills +kh/losses +kh/caps" \
360 " ?+rc/laps ?+rc/time +rc,cts/fastest" \
361 " +as/objectives +nb/faults +nb/goals" \
362 " +ka/pickups +ka/bckills +ka/bctime +ft/revivals" \
363 " +dom/ticks +dom/takes" \
364 " -lms,rc,cts,inv,nb/score"
366 void Cmd_Scoreboard_SetFields(int argc)
371 bool have_name = false, have_primary = false, have_secondary = false, have_separator = false;
375 return; // do nothing, we don't know gametype and scores yet
377 // sbt_fields uses strunzone on the titles!
378 if(!sbt_field_title[0])
379 for(i = 0; i < MAX_SBT_FIELDS; ++i)
380 sbt_field_title[i] = strzone("(null)");
382 // TODO: re enable with gametype dependant cvars?
383 if(argc < 3) // no arguments provided
384 argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " ");
387 argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " ");
391 if(argv(2) == "default" || argv(2) == "expand_default")
393 if(argv(2) == "expand_default")
394 cvar_set("scoreboard_columns", SCOREBOARD_DEFAULT_COLUMNS);
395 argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " ");
397 else if(argv(2) == "all")
399 string s = "ping pl name |"; // scores without a label
400 FOREACH(Scores, true, {
402 if(it != ps_secondary)
403 if(scores_label(it) != "")
404 s = strcat(s, " ", scores_label(it));
406 if(ps_secondary != ps_primary)
407 s = strcat(s, " ", scores_label(ps_secondary));
408 s = strcat(s, " ", scores_label(ps_primary));
409 argc = tokenizebyseparator(strcat("0 1 ", s), " ");
416 hud_fontsize = HUD_GetFontsize("hud_fontsize");
418 for(i = 1; i < argc - 1; ++i)
421 bool nocomplain = false;
422 if(substring(str, 0, 1) == "?")
425 str = substring(str, 1, strlen(str) - 1);
428 slash = strstrofs(str, "/", 0);
431 pattern = substring(str, 0, slash);
432 str = substring(str, slash + 1, strlen(str) - (slash + 1));
434 if (!isGametypeInFilter(gametype, teamplay, false, pattern))
438 strcpy(sbt_field_title[sbt_num_fields], TranslateScoresLabel(str));
439 sbt_field_size[sbt_num_fields] = stringwidth(sbt_field_title[sbt_num_fields], false, hud_fontsize);
440 str = strtolower(str);
445 case "ping": sbt_field[sbt_num_fields] = SP_PING; break;
446 case "pl": sbt_field[sbt_num_fields] = SP_PL; break;
447 case "kd": case "kdr": case "kdratio": sbt_field[sbt_num_fields] = SP_KDRATIO; break;
448 case "sum": case "diff": case "k-d": sbt_field[sbt_num_fields] = SP_SUM; break;
449 case "name": case "nick": sbt_field[sbt_num_fields] = SP_NAME; have_name = true; break;
450 case "|": sbt_field[sbt_num_fields] = SP_SEPARATOR; have_separator = true; break;
451 case "elo": sbt_field[sbt_num_fields] = SP_ELO; break;
452 case "dmg": case "damage": sbt_field[sbt_num_fields] = SP_DMG; break;
453 case "dmgtaken": case "damagetaken": sbt_field[sbt_num_fields] = SP_DMGTAKEN; break;
454 case "fps": sbt_field[sbt_num_fields] = SP_FPS; break;
457 FOREACH(Scores, true, {
458 if (str == strtolower(scores_label(it))) {
460 goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
470 LOG_INFOF("^1Error:^7 Unknown score field: '%s'", str);
474 sbt_field[sbt_num_fields] = j;
477 if(j == ps_secondary)
478 have_secondary = true;
483 if(sbt_num_fields >= MAX_SBT_FIELDS)
487 if(scores_flags(ps_primary) & SFL_ALLOW_HIDE)
489 if(scores_flags(ps_secondary) & SFL_ALLOW_HIDE)
490 have_secondary = true;
491 if(ps_primary == ps_secondary)
492 have_secondary = true;
493 missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
495 if(sbt_num_fields + missing < MAX_SBT_FIELDS)
499 strunzone(sbt_field_title[sbt_num_fields]);
500 for(i = sbt_num_fields; i > 0; --i)
502 sbt_field_title[i] = sbt_field_title[i-1];
503 sbt_field_size[i] = sbt_field_size[i-1];
504 sbt_field[i] = sbt_field[i-1];
506 sbt_field_title[0] = strzone(TranslateScoresLabel("name"));
507 sbt_field[0] = SP_NAME;
509 LOG_INFO("fixed missing field 'name'");
513 strunzone(sbt_field_title[sbt_num_fields]);
514 for(i = sbt_num_fields; i > 1; --i)
516 sbt_field_title[i] = sbt_field_title[i-1];
517 sbt_field_size[i] = sbt_field_size[i-1];
518 sbt_field[i] = sbt_field[i-1];
520 sbt_field_title[1] = strzone("|");
521 sbt_field[1] = SP_SEPARATOR;
522 sbt_field_size[1] = stringwidth("|", false, hud_fontsize);
524 LOG_INFO("fixed missing field '|'");
527 else if(!have_separator)
529 strcpy(sbt_field_title[sbt_num_fields], "|");
530 sbt_field_size[sbt_num_fields] = stringwidth("|", false, hud_fontsize);
531 sbt_field[sbt_num_fields] = SP_SEPARATOR;
533 LOG_INFO("fixed missing field '|'");
537 strcpy(sbt_field_title[sbt_num_fields], TranslateScoresLabel(scores_label(ps_secondary)));
538 sbt_field_size[sbt_num_fields] = stringwidth(sbt_field_title[sbt_num_fields], false, hud_fontsize);
539 sbt_field[sbt_num_fields] = ps_secondary;
541 LOG_INFOF("fixed missing field '%s'", scores_label(ps_secondary));
545 strcpy(sbt_field_title[sbt_num_fields], TranslateScoresLabel(scores_label(ps_primary)));
546 sbt_field_size[sbt_num_fields] = stringwidth(sbt_field_title[sbt_num_fields], false, hud_fontsize);
547 sbt_field[sbt_num_fields] = ps_primary;
549 LOG_INFOF("fixed missing field '%s'", scores_label(ps_primary));
553 sbt_field[sbt_num_fields] = SP_END;
557 vector sbt_field_rgb;
558 string sbt_field_icon0;
559 string sbt_field_icon1;
560 string sbt_field_icon2;
561 vector sbt_field_icon0_rgb;
562 vector sbt_field_icon1_rgb;
563 vector sbt_field_icon2_rgb;
564 string sbt_font_self;
565 string Scoreboard_GetName(entity pl)
567 if(ready_waiting && pl.ready)
569 sbt_field_icon0 = "gfx/scoreboard/player_ready";
573 int f = entcs_GetClientColors(pl.sv_entnum);
575 sbt_field_icon0 = "gfx/scoreboard/playercolor_base";
576 sbt_field_icon1 = "gfx/scoreboard/playercolor_shirt";
577 sbt_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
578 sbt_field_icon2 = "gfx/scoreboard/playercolor_pants";
579 sbt_field_icon2_rgb = colormapPaletteColor(f % 16, 1);
582 return entcs_GetName(pl.sv_entnum);
584 string Scoreboard_GetField(entity pl, PlayerScoreField field)
586 float tmp, num, denom;
589 sbt_field_rgb = '1 1 1';
590 sbt_field_icon0 = "";
591 sbt_field_icon1 = "";
592 sbt_field_icon2 = "";
593 sbt_field_icon0_rgb = '1 1 1';
594 sbt_field_icon1_rgb = '1 1 1';
595 sbt_field_icon2_rgb = '1 1 1';
600 return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6
601 //str = getplayerkeyvalue(pl.sv_entnum, "ping");
605 tmp = max(0, min(220, f-80)) / 220;
606 sbt_field_rgb = '1 1 1' - '0 1 1' * tmp;
612 f = pl.ping_packetloss;
613 tmp = pl.ping_movementloss;
614 if(f == 0 && tmp == 0)
616 str = ftos(ceil(f * 100));
618 str = strcat(str, "~", ftos(ceil(tmp * 100)));
619 tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
620 sbt_field_rgb = '1 0.5 0.5' - '0 0.5 0.5' * tmp;
624 return Scoreboard_GetName(pl);
627 f = pl.(scores(SP_KILLS));
628 f -= pl.(scores(SP_SUICIDES));
632 num = pl.(scores(SP_KILLS));
633 denom = pl.(scores(SP_DEATHS));
636 sbt_field_rgb = '0 1 0';
637 str = sprintf("%d", num);
638 } else if(num <= 0) {
639 sbt_field_rgb = '1 0 0';
640 str = sprintf("%.1f", num/denom);
642 str = sprintf("%.1f", num/denom);
646 f = pl.(scores(SP_KILLS));
647 f -= pl.(scores(SP_DEATHS));
650 sbt_field_rgb = '0 1 0';
652 sbt_field_rgb = '1 1 1';
654 sbt_field_rgb = '1 0 0';
660 float elo = pl.(scores(SP_ELO));
662 case -1: return "...";
663 case -2: return _("N/A");
664 default: return ftos(elo);
670 float fps = pl.(scores(SP_FPS));
673 sbt_field_rgb = '1 1 1';
674 return ((pl.ping == 0) ? _("N/A") : "..."); // if 0 ping, either connecting or bot (either case can't show proper score)
676 //sbt_field_rgb = HUD_Get_Num_Color(fps, 200);
677 sbt_field_rgb = '1 0 0' + '0 1 1' * (bound(0, fps, 60) / 60);
681 case SP_DMG: case SP_DMGTAKEN:
682 return sprintf("%.1f k", pl.(scores(field)) / 1000);
684 default: case SP_SCORE:
685 tmp = pl.(scores(field));
686 f = scores_flags(field);
687 if(field == ps_primary)
688 sbt_field_rgb = '1 1 0';
689 else if(field == ps_secondary)
690 sbt_field_rgb = '0 1 1';
692 sbt_field_rgb = '1 1 1';
693 return ScoreString(f, tmp);
698 float sbt_fixcolumnwidth_len;
699 float sbt_fixcolumnwidth_iconlen;
700 float sbt_fixcolumnwidth_marginlen;
702 string Scoreboard_FixColumnWidth(int i, string str)
708 sbt_fixcolumnwidth_iconlen = 0;
710 if(sbt_field_icon0 != "")
712 sz = draw_getimagesize(sbt_field_icon0);
714 if(sbt_fixcolumnwidth_iconlen < f)
715 sbt_fixcolumnwidth_iconlen = f;
718 if(sbt_field_icon1 != "")
720 sz = draw_getimagesize(sbt_field_icon1);
722 if(sbt_fixcolumnwidth_iconlen < f)
723 sbt_fixcolumnwidth_iconlen = f;
726 if(sbt_field_icon2 != "")
728 sz = draw_getimagesize(sbt_field_icon2);
730 if(sbt_fixcolumnwidth_iconlen < f)
731 sbt_fixcolumnwidth_iconlen = f;
734 if(sbt_fixcolumnwidth_iconlen != 0)
736 sbt_fixcolumnwidth_iconlen *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect
737 sbt_fixcolumnwidth_marginlen = stringwidth(" ", false, hud_fontsize);
740 sbt_fixcolumnwidth_marginlen = 0;
742 if(sbt_field[i] == SP_NAME) // name gets all remaining space
745 float remaining_space = 0;
746 for(j = 0; j < sbt_num_fields; ++j)
748 if (sbt_field[i] != SP_SEPARATOR)
749 remaining_space += sbt_field_size[j] + hud_fontsize.x;
750 sbt_field_size[i] = panel_size.x - remaining_space;
752 if (sbt_fixcolumnwidth_iconlen != 0)
753 remaining_space += sbt_fixcolumnwidth_marginlen + sbt_fixcolumnwidth_iconlen * hud_fontsize.x;
754 float namesize = panel_size.x - remaining_space;
755 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
756 sbt_fixcolumnwidth_len = stringwidth(str, true, hud_fontsize);
758 max_namesize = vid_conwidth - remaining_space;
761 sbt_fixcolumnwidth_len = stringwidth(str, false, hud_fontsize);
763 f = sbt_fixcolumnwidth_len + sbt_fixcolumnwidth_marginlen + sbt_fixcolumnwidth_iconlen * hud_fontsize.x;
764 if(sbt_field_size[i] < f)
765 sbt_field_size[i] = f;
770 void Scoreboard_initFieldSizes()
772 for(int i = 0; i < sbt_num_fields; ++i)
774 sbt_field_size[i] = stringwidth(sbt_field_title[i], false, hud_fontsize);
775 Scoreboard_FixColumnWidth(i, "");
779 vector Scoreboard_DrawHeader(vector pos, vector rgb, bool other_players)
782 vector column_dim = eY * panel_size.y;
784 column_dim.y -= 1.25 * hud_fontsize.y;
785 vector text_offset = eY * (1.25 - 1) / 2 * hud_fontsize.y;
786 pos.x += hud_fontsize.x * 0.5;
787 for(i = 0; i < sbt_num_fields; ++i)
789 if(sbt_field[i] == SP_SEPARATOR)
791 column_dim.x = sbt_field_size[i] + hud_fontsize.x;
794 drawfill(pos - eX * hud_fontsize.x * 0.5, column_dim, '0 0 0', sbt_highlight_alpha, DRAWFLAG_NORMAL);
795 drawstring(pos + text_offset, sbt_field_title[i], hud_fontsize, rgb * 1.5, sbt_fg_alpha, DRAWFLAG_NORMAL);
796 pos.x += column_dim.x;
798 if(sbt_field[i] == SP_SEPARATOR)
800 pos.x = panel_pos.x + panel_size.x - hud_fontsize.x * 0.5;
801 for(i = sbt_num_fields - 1; i > 0; --i)
803 if(sbt_field[i] == SP_SEPARATOR)
806 pos.x -= sbt_field_size[i];
811 column_dim.x = sbt_field_size[i] + hud_fontsize.x;
812 drawfill(pos - eX * hud_fontsize.x * 0.5, column_dim, '0 0 0', sbt_highlight_alpha, DRAWFLAG_NORMAL);
815 text_offset.x = sbt_field_size[i] - stringwidth(sbt_field_title[i], false, hud_fontsize);
816 drawstring(pos + text_offset, sbt_field_title[i], hud_fontsize, rgb * 1.5, sbt_fg_alpha, DRAWFLAG_NORMAL);
817 pos.x -= hud_fontsize.x;
822 pos.y += 1.25 * hud_fontsize.y;
826 void Scoreboard_DrawItem(vector item_pos, vector rgb, entity pl, bool is_self, int pl_number)
828 TC(bool, is_self); TC(int, pl_number);
830 bool is_spec = (entcs_GetSpecState(pl.sv_entnum) == ENTCS_SPEC_PURE);
832 vector h_pos = item_pos;
833 vector h_size = vec2(panel_size.x, hud_fontsize.y * 1.25);
834 // alternated rows highlighting
836 drawfill(h_pos, h_size, rgb, sbt_highlight_alpha_self, DRAWFLAG_NORMAL);
837 else if((sbt_highlight) && (!(pl_number % 2)))
838 drawfill(h_pos, h_size, rgb, sbt_highlight_alpha, DRAWFLAG_NORMAL);
840 float fg_alpha = (is_self ? sbt_fg_alpha_self : sbt_fg_alpha);
842 vector pos = item_pos;
843 // put a "self indicator" beside the self row
844 sbt_font_self = "â—€"; // unicode U+25C0 (black left-pointing triangle)
846 drawstring(pos+eX*(panel_size.x+.5*hud_fontsize.x)+eY, sbt_font_self, vec2(hud_fontsize.x, hud_fontsize.y), rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
848 pos.x += hud_fontsize.x * 0.5;
849 pos.y += (1.25 - 1) / 2 * hud_fontsize.y; // center text vertically
850 vector tmp = '0 0 0';
852 PlayerScoreField field;
853 for(i = 0; i < sbt_num_fields; ++i)
855 field = sbt_field[i];
856 if(field == SP_SEPARATOR)
859 if(is_spec && field != SP_NAME && field != SP_PING) {
860 pos.x += sbt_field_size[i] + hud_fontsize.x;
863 str = Scoreboard_GetField(pl, field);
864 str = Scoreboard_FixColumnWidth(i, str);
866 pos.x += sbt_field_size[i] + hud_fontsize.x;
868 if(field == SP_NAME) {
869 tmp.x = sbt_field_size[i] - hud_fontsize.x * sbt_fixcolumnwidth_iconlen - sbt_fixcolumnwidth_marginlen + hud_fontsize.x;
870 drawcolorcodedstring(pos - tmp, str, hud_fontsize, fg_alpha, DRAWFLAG_NORMAL);
872 tmp.x = sbt_fixcolumnwidth_len + hud_fontsize.x;
873 drawstring(pos - tmp, str, hud_fontsize, sbt_field_rgb, fg_alpha, DRAWFLAG_NORMAL);
876 tmp.x = sbt_field_size[i] + hud_fontsize.x;
877 if(sbt_field_icon0 != "")
878 drawpic(pos - tmp, sbt_field_icon0, vec2(hud_fontsize.x * sbt_fixcolumnwidth_iconlen, hud_fontsize.y), sbt_field_icon1_rgb, fg_alpha, DRAWFLAG_NORMAL);
879 if(sbt_field_icon1 != "")
880 drawpic(pos - tmp, sbt_field_icon1, vec2(hud_fontsize.x * sbt_fixcolumnwidth_iconlen, hud_fontsize.y), sbt_field_icon1_rgb, fg_alpha, DRAWFLAG_NORMAL);
881 if(sbt_field_icon2 != "")
882 drawpic(pos - tmp, sbt_field_icon2, vec2(hud_fontsize.x * sbt_fixcolumnwidth_iconlen, hud_fontsize.y), sbt_field_icon2_rgb, fg_alpha, DRAWFLAG_NORMAL);
885 if(sbt_field[i] == SP_SEPARATOR)
887 pos.x = item_pos.x + panel_size.x - hud_fontsize.x * 0.5;
888 for(i = sbt_num_fields-1; i > 0; --i)
890 field = sbt_field[i];
891 if(field == SP_SEPARATOR)
894 if(is_spec && field != SP_NAME && field != SP_PING) {
895 pos.x -= sbt_field_size[i] + hud_fontsize.x;
899 str = Scoreboard_GetField(pl, field);
900 str = Scoreboard_FixColumnWidth(i, str);
902 if(field == SP_NAME) {
903 tmp.x = sbt_fixcolumnwidth_len; // left or right aligned? let's put it right...
904 drawcolorcodedstring(pos - tmp, str, hud_fontsize, fg_alpha, DRAWFLAG_NORMAL);
906 tmp.x = sbt_fixcolumnwidth_len;
907 drawstring(pos - tmp, str, hud_fontsize, sbt_field_rgb, fg_alpha, DRAWFLAG_NORMAL);
910 tmp.x = sbt_field_size[i];
911 if(sbt_field_icon0 != "")
912 drawpic(pos - tmp, sbt_field_icon0, vec2(hud_fontsize.x * sbt_fixcolumnwidth_iconlen, hud_fontsize.y), sbt_field_icon1_rgb, fg_alpha, DRAWFLAG_NORMAL);
913 if(sbt_field_icon1 != "")
914 drawpic(pos - tmp, sbt_field_icon1, vec2(hud_fontsize.x * sbt_fixcolumnwidth_iconlen, hud_fontsize.y), sbt_field_icon1_rgb, fg_alpha, DRAWFLAG_NORMAL);
915 if(sbt_field_icon2 != "")
916 drawpic(pos - tmp, sbt_field_icon2, vec2(hud_fontsize.x * sbt_fixcolumnwidth_iconlen, hud_fontsize.y), sbt_field_icon2_rgb, fg_alpha, DRAWFLAG_NORMAL);
917 pos.x -= sbt_field_size[i] + hud_fontsize.x;
922 drawfill(h_pos, h_size, '0 0 0', 0.5 * panel_fg_alpha, DRAWFLAG_NORMAL);
925 vector Scoreboard_DrawOthers(vector item_pos, vector rgb, int this_team, entity ignored_pl, entity pl, int pl_number)
928 vector h_pos = item_pos;
929 vector h_size = vec2(panel_size.x, hud_fontsize.y * 1.25);
931 bool complete = (this_team == NUM_SPECTATOR);
934 if((sbt_highlight) && (!(pl_number % 2)))
935 drawfill(h_pos, h_size, rgb, sbt_highlight_alpha, DRAWFLAG_NORMAL);
937 vector pos = item_pos;
938 pos.x += hud_fontsize.x * 0.5;
939 pos.y += (1.25 - 1) / 2 * hud_fontsize.y; // center text vertically
941 float width_limit = item_pos.x + panel_size.x - hud_fontsize.x;
943 width_limit -= stringwidth("...", false, hud_fontsize);
944 float namesize = autocvar_hud_panel_scoreboard_namesize * hud_fontsize.x;
945 static float max_name_width = 0;
948 float min_fieldsize = 0;
949 float fieldpadding = hud_fontsize.x * 0.25;
950 if(this_team == NUM_SPECTATOR)
952 if(autocvar_hud_panel_scoreboard_spectators_showping)
953 min_fieldsize = stringwidth("999", false, hud_fontsize);
955 else if(autocvar_hud_panel_scoreboard_others_showscore)
956 min_fieldsize = stringwidth("99", false, hud_fontsize);
957 for(i = 0; pl; pl = pl.sort_next)
959 if(pl.team != this_team)
965 if(this_team == NUM_SPECTATOR)
967 if(autocvar_hud_panel_scoreboard_spectators_showping)
968 field = Scoreboard_GetField(pl, SP_PING);
970 else if(autocvar_hud_panel_scoreboard_others_showscore)
971 field = Scoreboard_GetField(pl, SP_SCORE);
973 string str = textShortenToWidth(entcs_GetName(pl.sv_entnum), namesize, hud_fontsize, stringwidth_colors);
974 float column_width = stringwidth(str, true, hud_fontsize);
975 if((this_team == NUM_SPECTATOR) && autocvar_hud_panel_scoreboard_spectators_aligned)
977 if(column_width > max_name_width)
978 max_name_width = column_width;
979 column_width = max_name_width;
983 fieldsize = stringwidth(field, false, hud_fontsize);
984 column_width += hud_fontsize.x * 0.25 + max(fieldsize, min_fieldsize) + 2 * fieldpadding;
987 if(pos.x + column_width > width_limit)
992 drawstring(pos, "...", hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
997 pos.x = item_pos.x + hud_fontsize.x * 0.5;
998 pos.y += hud_fontsize.y * 1.25;
1002 vector name_pos = pos;
1003 if((this_team == NUM_SPECTATOR) && autocvar_hud_panel_scoreboard_spectators_aligned)
1004 name_pos.x += max(fieldsize, min_fieldsize) + 2 * fieldpadding + hud_fontsize.x * 0.25;
1005 drawcolorcodedstring(name_pos, str, hud_fontsize, sbt_fg_alpha, DRAWFLAG_NORMAL);
1008 h_size.x = max(fieldsize, min_fieldsize) + 2 * fieldpadding;
1009 h_size.y = hud_fontsize.y;
1010 vector field_pos = pos;
1011 if(!((this_team == NUM_SPECTATOR) && autocvar_hud_panel_scoreboard_spectators_aligned))
1012 field_pos.x += column_width - h_size.x;
1014 drawfill(field_pos, h_size, '1 1 1', sbt_highlight_alpha, DRAWFLAG_NORMAL);
1015 field_pos.x += fieldpadding + (max(fieldsize, min_fieldsize) - fieldsize) * 0.5;
1016 drawstring(field_pos, field, hud_fontsize, sbt_field_rgb, sbt_fg_alpha, DRAWFLAG_NORMAL);
1020 h_size.x = column_width + hud_fontsize.x * 0.25;
1021 h_size.y = hud_fontsize.y;
1022 drawfill(pos - hud_fontsize.x * 0.25 * eX, h_size, '0 0 0', 0.5 * panel_fg_alpha, DRAWFLAG_NORMAL);
1024 pos.x += column_width;
1025 pos.x += hud_fontsize.x;
1027 return vec2(item_pos.x, item_pos.y + i * hud_fontsize.y * 1.25);
1030 vector Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
1032 int max_players = 999;
1033 if(autocvar_hud_panel_scoreboard_maxheight > 0)
1035 float height = autocvar_hud_panel_scoreboard_maxheight * vid_conheight;
1038 height -= (panel_bg_padding * 2 + hud_fontsize.y * 1.25) * team_count; // - padding and header
1039 height -= hud_fontsize.y * (team_count - 1); // - spacing between tables
1040 height /= team_count;
1043 height -= panel_bg_padding * 2; // - padding
1044 max_players = floor(height / (hud_fontsize.y * 1.25));
1045 if(max_players <= 1)
1047 if(max_players == tm.team_size)
1052 entity me = playerslots[current_player];
1054 panel_size.y = 1.25 * hud_fontsize.y * (1 + bound(1, tm.team_size, max_players));
1055 panel_size.y += panel_bg_padding * 2;
1058 vector end_pos = panel_pos + eY * (panel_size.y + 0.5* hud_fontsize.y);
1059 if(panel.current_panel_bg != "0")
1060 end_pos.y += panel_bg_border * 2;
1062 if(panel_bg_padding)
1064 panel_pos += '1 1 0' * panel_bg_padding;
1065 panel_size -= '2 2 0' * panel_bg_padding;
1069 vector tmp = vec2(panel_size.x, 1.25 * hud_fontsize.y);
1073 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', sbt_bg_alpha, DRAWFLAG_NORMAL);
1075 pos.y += 1.25 * hud_fontsize.y;
1078 tmp.y = panel_size.y - 1.25 * hud_fontsize.y;
1080 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL);
1083 // print header row and highlight columns
1084 pos = Scoreboard_DrawHeader(panel_pos, rgb, (max_players < tm.team_size));
1086 // fill the table and draw the rows
1087 bool is_self = false;
1088 bool self_shown = false;
1090 for(pl = players.sort_next; pl; pl = pl.sort_next)
1092 if(pl.team != tm.team)
1094 if(i == max_players - 2 && pl != me)
1096 if(!self_shown && me.team == tm.team)
1098 Scoreboard_DrawItem(pos, rgb, me, true, i);
1100 pos.y += 1.25 * hud_fontsize.y;
1104 if(i >= max_players - 1)
1106 pos = Scoreboard_DrawOthers(pos, rgb, tm.team, (self_shown ? me : NULL), pl, i);
1109 is_self = (pl.sv_entnum == current_player);
1110 Scoreboard_DrawItem(pos, rgb, pl, is_self, i);
1113 pos.y += 1.25 * hud_fontsize.y;
1117 panel_size.x += panel_bg_padding * 2; // restore initial width
1121 bool Scoreboard_WouldDraw()
1123 if (MUTATOR_CALLHOOK(DrawScoreboard))
1125 else if (QuickMenu_IsOpened())
1127 else if (HUD_Radar_Clickable())
1129 else if (scoreboard_showscores)
1131 else if (intermission == 1)
1133 else if (intermission == 2)
1135 else if (spectatee_status != -1 && STAT(HEALTH) <= 0 && autocvar_cl_deathscoreboard && !ISGAMETYPE(CTS) && !active_minigame)
1137 else if (scoreboard_showscores_force)
1142 float average_accuracy;
1143 vector Scoreboard_AccuracyStats_Draw(vector pos, vector rgb, vector bg_size)
1147 if (scoreboard_fade_alpha == 1)
1148 scoreboard_acc_fade_alpha = min(1, scoreboard_acc_fade_alpha + frametime * 10);
1150 scoreboard_acc_fade_alpha = 1; // sync fading with the scoreboard
1152 vector initial_pos = pos;
1154 WepSet weapons_stat = WepSet_GetFromStat();
1155 WepSet weapons_inmap = WepSet_GetFromStat_InMap();
1156 int disownedcnt = 0;
1158 FOREACH(Weapons, it != WEP_Null, {
1159 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1161 WepSet set = it.m_wepset;
1162 if(it.spawnflags & WEP_TYPE_OTHER)
1167 if (weapon_stats < 0 && !((weapons_stat & set) || (weapons_inmap & set)))
1169 if (((it.spawnflags & WEP_FLAG_HIDDEN) || (it.spawnflags & WEP_FLAG_MUTATORBLOCKED)))
1176 int weapon_cnt = (Weapons_COUNT - 1) - disownedcnt - nHidden;
1177 if (weapon_cnt <= 0) return pos;
1180 if (autocvar_hud_panel_scoreboard_accuracy_doublerows && weapon_cnt >= floor((Weapons_COUNT - nHidden - 1) * 0.5))
1182 int columnns = ceil(weapon_cnt / rows);
1184 float weapon_height = 29;
1185 float height = hud_fontsize.y + weapon_height;
1187 drawstring(pos + eX * panel_bg_padding, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', panel_fg_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL);
1188 pos.y += 1.25 * hud_fontsize.y;
1189 if(panel.current_panel_bg != "0")
1190 pos.y += panel_bg_border;
1193 panel_size.y = height * rows;
1194 panel_size.y += panel_bg_padding * 2;
1196 float panel_bg_alpha_save = panel_bg_alpha;
1197 panel_bg_alpha *= scoreboard_acc_fade_alpha;
1199 panel_bg_alpha = panel_bg_alpha_save;
1201 vector end_pos = panel_pos + eY * (panel_size.y + hud_fontsize.y);
1202 if(panel.current_panel_bg != "0")
1203 end_pos.y += panel_bg_border * 2;
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 float weapon_width = tmp.x / columnns / rows;
1217 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, sbt_bg_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL);
1221 // column highlighting
1222 for (int i = 0; i < columnns; ++i)
1224 drawfill(pos + eX * weapon_width * rows * i, vec2(weapon_width * rows, height * rows), '0 0 0', sbt_highlight_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL);
1227 for (int i = 0; i < rows; ++i)
1228 drawfill(pos + eY * (weapon_height + height * i), vec2(tmp.x, hud_fontsize.y), rgb, sbt_highlight_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL);
1231 average_accuracy = 0;
1232 int weapons_with_stats = 0;
1234 pos.x += weapon_width / 2;
1236 if (autocvar_hud_panel_scoreboard_accuracy_nocolors)
1239 Accuracy_LoadColors();
1241 float oldposx = pos.x;
1245 FOREACH(Weapons, it != WEP_Null, {
1246 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1248 WepSet set = it.m_wepset;
1249 if (weapon_stats < 0 && !((weapons_stat & set) || (weapons_inmap & set)))
1251 if (it.spawnflags & WEP_TYPE_OTHER)
1255 if (weapon_stats >= 0)
1256 weapon_alpha = sbt_fg_alpha;
1258 weapon_alpha = 0.2 * sbt_fg_alpha;
1261 drawpic_aspect_skin(tmpos, it.model2, vec2(weapon_width, weapon_height), '1 1 1', weapon_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL);
1263 if (weapon_stats >= 0) {
1264 weapons_with_stats += 1;
1265 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1268 s = sprintf("%d%%", weapon_stats * 100);
1271 padding = (weapon_width - stringwidth(s, false, hud_fontsize)) / 2; // center the accuracy value
1273 if(!autocvar_hud_panel_scoreboard_accuracy_nocolors)
1274 rgb = Accuracy_GetColor(weapon_stats);
1276 drawstring(tmpos + vec2(padding, weapon_height), s, hud_fontsize, rgb, sbt_fg_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL);
1278 tmpos.x += weapon_width * rows;
1279 pos.x += weapon_width * rows;
1280 if (rows == 2 && column == columnns - 1) {
1288 if (weapons_with_stats)
1289 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1291 panel_size.x += panel_bg_padding * 2; // restore initial width
1293 if (scoreboard_acc_fade_alpha == 1)
1295 return initial_pos + (end_pos - initial_pos) * scoreboard_acc_fade_alpha;
1298 vector MapStats_DrawKeyValue(vector pos, string key, string value) {
1300 pos.x += hud_fontsize.x * 0.25;
1301 drawstring(pos, key, hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
1302 pos.x = panel_pos.x + panel_size.x - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1303 drawstring(pos, value, hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
1305 pos.y += hud_fontsize.y;
1310 vector Scoreboard_MapStats_Draw(vector pos, vector rgb, vector bg_size) {
1311 float stat_secrets_found, stat_secrets_total;
1312 float stat_monsters_killed, stat_monsters_total;
1316 // get monster stats
1317 stat_monsters_killed = STAT(MONSTERS_KILLED);
1318 stat_monsters_total = STAT(MONSTERS_TOTAL);
1320 // get secrets stats
1321 stat_secrets_found = STAT(SECRETS_FOUND);
1322 stat_secrets_total = STAT(SECRETS_TOTAL);
1324 // get number of rows
1325 if(stat_secrets_total)
1327 if(stat_monsters_total)
1330 // if no rows, return
1334 // draw table header
1335 drawstring(pos + eX * panel_bg_padding, _("Map stats:"), hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1336 pos.y += 1.25 * hud_fontsize.y;
1337 if(panel.current_panel_bg != "0")
1338 pos.y += panel_bg_border;
1341 panel_size.y = hud_fontsize.y * rows;
1342 panel_size.y += panel_bg_padding * 2;
1345 vector end_pos = panel_pos + eY * (panel_size.y + hud_fontsize.y);
1346 if(panel.current_panel_bg != "0")
1347 end_pos.y += panel_bg_border * 2;
1349 if(panel_bg_padding)
1351 panel_pos += '1 1 0' * panel_bg_padding;
1352 panel_size -= '2 2 0' * panel_bg_padding;
1356 vector tmp = panel_size;
1359 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL);
1362 if(stat_monsters_total)
1364 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1365 pos = MapStats_DrawKeyValue(pos, _("Monsters killed:"), val);
1369 if(stat_secrets_total)
1371 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1372 pos = MapStats_DrawKeyValue(pos, _("Secrets found:"), val);
1375 panel_size.x += panel_bg_padding * 2; // restore initial width
1380 vector Scoreboard_Rankings_Draw(vector pos, entity pl, vector rgb, vector bg_size)
1383 RANKINGS_RECEIVED_CNT = 0;
1384 for (i=RANKINGS_CNT-1; i>=0; --i)
1386 ++RANKINGS_RECEIVED_CNT;
1388 if (RANKINGS_RECEIVED_CNT == 0)
1391 vector hl_rgb = rgb + '0.5 0.5 0.5';
1393 pos.y += hud_fontsize.y;
1394 drawstring(pos + eX * panel_bg_padding, ((ISGAMETYPE(CTF)) ? _("Capture time rankings") : _("Rankings")), hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1395 pos.y += 1.25 * hud_fontsize.y;
1396 if(panel.current_panel_bg != "0")
1397 pos.y += panel_bg_border;
1402 for(i = 0; i < RANKINGS_RECEIVED_CNT; ++i)
1404 float f = stringwidth(ColorTranslateRGB(grecordholder[i]), true, hud_fontsize);
1409 if(namesize > autocvar_hud_panel_scoreboard_namesize * hud_fontsize.x)
1411 namesize = autocvar_hud_panel_scoreboard_namesize * hud_fontsize.x;
1415 float ranksize = 3 * hud_fontsize.x;
1416 float timesize = 5 * hud_fontsize.x;
1417 vector columnsize = vec2(ranksize + timesize + namesize + hud_fontsize.x, 1.25 * hud_fontsize.y);
1418 int columns = max(1, floor((panel_size.x - 2 * panel_bg_padding) / columnsize.x));
1419 columns = min(columns, RANKINGS_RECEIVED_CNT);
1421 // expand name column to fill the entire row
1422 float available_space = (panel_size.x - 2 * panel_bg_padding - columnsize.x * columns) / columns;
1423 namesize += available_space;
1424 columnsize.x += available_space;
1426 panel_size.y = ceil(RANKINGS_RECEIVED_CNT / columns) * 1.25 * hud_fontsize.y;
1427 panel_size.y += panel_bg_padding * 2;
1431 vector end_pos = panel_pos + eY * (panel_size.y + hud_fontsize.y);
1432 if(panel.current_panel_bg != "0")
1433 end_pos.y += panel_bg_border * 2;
1435 if(panel_bg_padding)
1437 panel_pos += '1 1 0' * panel_bg_padding;
1438 panel_size -= '2 2 0' * panel_bg_padding;
1444 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, panel_size, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL);
1446 vector text_ofs = vec2(0.5 * hud_fontsize.x, (1.25 - 1) / 2 * hud_fontsize.y); // center text vertically
1448 int column = 0, j = 0;
1449 string zoned_name_self = strzone(strdecolorize(entcs_GetName(player_localnum)));
1450 for(i = 0; i < RANKINGS_RECEIVED_CNT; ++i)
1457 if(strdecolorize(grecordholder[i]) == zoned_name_self)
1458 drawfill(pos, columnsize, hl_rgb, sbt_highlight_alpha_self, DRAWFLAG_NORMAL);
1459 else if(!((j + column) & 1) && sbt_highlight)
1460 drawfill(pos, columnsize, hl_rgb, sbt_highlight_alpha, DRAWFLAG_NORMAL);
1462 str = count_ordinal(i+1);
1463 drawstring(pos + text_ofs, str, hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
1464 drawstring(pos + text_ofs + eX * ranksize, TIME_ENCODED_TOSTRING(t), hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
1465 str = ColorTranslateRGB(grecordholder[i]);
1467 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
1468 drawcolorcodedstring(pos + text_ofs + eX * (ranksize + timesize), str, hud_fontsize, sbt_fg_alpha, DRAWFLAG_NORMAL);
1470 pos.y += 1.25 * hud_fontsize.y;
1472 if(j >= ceil(RANKINGS_RECEIVED_CNT / columns))
1476 pos.x += panel_size.x / columns;
1477 pos.y = panel_pos.y;
1480 strfree(zoned_name_self);
1482 panel_size.x += panel_bg_padding * 2; // restore initial width
1486 float scoreboard_time;
1487 bool have_weapon_stats;
1488 bool Scoreboard_AccuracyStats_WouldDraw(float ypos)
1490 if (ISGAMETYPE(CTS) || ISGAMETYPE(RACE) || ISGAMETYPE(NEXBALL))
1492 if (!autocvar_hud_panel_scoreboard_accuracy || warmup_stage || ypos > 0.91 * vid_conheight)
1495 if (time < scoreboard_time + autocvar_hud_panel_scoreboard_accuracy_showdelay
1496 && ypos > autocvar_hud_panel_scoreboard_accuracy_showdelay_minpos * vid_conheight
1502 if (!have_weapon_stats)
1504 FOREACH(Weapons, it != WEP_Null, {
1505 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1506 if (weapon_stats >= 0)
1508 have_weapon_stats = true;
1512 if (!have_weapon_stats)
1519 void Scoreboard_Draw()
1521 if(!autocvar__hud_configure)
1523 if(!hud_draw_maximized) return;
1525 // frametime checks allow to toggle the scoreboard even when the game is paused
1526 if(scoreboard_active) {
1527 if (scoreboard_fade_alpha < 1)
1528 scoreboard_time = time;
1529 if(hud_configure_menu_open == 1)
1530 scoreboard_fade_alpha = 1;
1531 float scoreboard_fadeinspeed = autocvar_hud_panel_scoreboard_fadeinspeed;
1532 if (scoreboard_fadeinspeed && frametime)
1533 scoreboard_fade_alpha = min(1, scoreboard_fade_alpha + frametime * scoreboard_fadeinspeed);
1535 scoreboard_fade_alpha = 1;
1536 if(hud_fontsize_str != autocvar_hud_fontsize)
1538 hud_fontsize = HUD_GetFontsize("hud_fontsize");
1539 Scoreboard_initFieldSizes();
1540 strcpy(hud_fontsize_str, autocvar_hud_fontsize);
1544 float scoreboard_fadeoutspeed = autocvar_hud_panel_scoreboard_fadeoutspeed;
1545 if (scoreboard_fadeoutspeed && frametime)
1546 scoreboard_fade_alpha = max(0, scoreboard_fade_alpha - frametime * scoreboard_fadeoutspeed);
1548 scoreboard_fade_alpha = 0;
1551 if (!scoreboard_fade_alpha)
1553 scoreboard_acc_fade_alpha = 0;
1558 scoreboard_fade_alpha = 0;
1560 if (autocvar_hud_panel_scoreboard_dynamichud)
1563 HUD_Scale_Disable();
1565 if(scoreboard_fade_alpha <= 0)
1567 panel_fade_alpha *= scoreboard_fade_alpha;
1568 HUD_Panel_LoadCvars();
1570 sbt_bg_alpha = autocvar_hud_panel_scoreboard_table_bg_alpha * panel_fg_alpha;
1571 sbt_highlight = autocvar_hud_panel_scoreboard_table_highlight;
1572 sbt_highlight_alpha = autocvar_hud_panel_scoreboard_table_highlight_alpha * panel_fg_alpha;
1573 sbt_highlight_alpha_self = autocvar_hud_panel_scoreboard_table_highlight_alpha_self * panel_fg_alpha;
1574 sbt_fg_alpha = autocvar_hud_panel_scoreboard_table_fg_alpha * panel_fg_alpha;
1575 sbt_fg_alpha_self = autocvar_hud_panel_scoreboard_table_fg_alpha_self * panel_fg_alpha;
1576 sbt_team_size_position = autocvar_hud_panel_scoreboard_team_size_position;
1578 // don't overlap with con_notify
1579 if(!autocvar__hud_configure)
1580 panel_pos.y = max((autocvar_con_notify * autocvar_con_notifysize), panel_pos.y);
1582 float excess = max(0, max_namesize - autocvar_hud_panel_scoreboard_namesize * hud_fontsize.x);
1583 float fixed_scoreboard_width = bound(vid_conwidth * autocvar_hud_panel_scoreboard_minwidth, vid_conwidth - excess, vid_conwidth * 0.93);
1584 panel_pos.x = 0.5 * (vid_conwidth - fixed_scoreboard_width);
1585 panel_size.x = fixed_scoreboard_width;
1587 Scoreboard_UpdatePlayerTeams();
1589 vector pos = panel_pos;
1594 vector sb_gameinfo_type_fontsize, sb_gameinfo_detail_fontsize;
1596 // Begin of Game Info Section
1597 sb_gameinfo_type_fontsize = hud_fontsize * 2.5;
1598 sb_gameinfo_detail_fontsize = hud_fontsize * 1.3;
1600 // Game Info: Game Type
1601 str = MapInfo_Type_ToText(gametype);
1602 draw_beginBoldFont();
1603 drawcolorcodedstring(pos + '0.5 0 0' * (panel_size.x - stringwidth(str, true, sb_gameinfo_type_fontsize)), str, sb_gameinfo_type_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
1606 // Game Info: Game Detail
1608 str = ""; // optionally "^7Limits: "
1609 tl = STAT(TIMELIMIT);
1610 fl = STAT(FRAGLIMIT);
1611 ll = STAT(LEADLIMIT);
1613 str = strcat(str, sprintf(_("^3%1.0f minutes"), tl));
1614 if(!ISGAMETYPE(LMS))
1619 str = strcat(str, "^7 / "); // delimiter
1622 str = strcat(str, sprintf(_("^5%s %s"), ScoreString(teamscores_flags(ts_primary), fl),
1623 (teamscores_label(ts_primary) == "score") ? CTX(_("SCO^points")) :
1624 (teamscores_label(ts_primary) == "fastest") ? CTX(_("SCO^is beaten")) :
1625 TranslateScoresLabel(teamscores_label(ts_primary))));
1629 str = strcat(str, sprintf(_("^5%s %s"), ScoreString(scores_flags(ps_primary), fl),
1630 (scores_label(ps_primary) == "score") ? CTX(_("SCO^points")) :
1631 (scores_label(ps_primary) == "fastest") ? CTX(_("SCO^is beaten")) :
1632 TranslateScoresLabel(scores_label(ps_primary))));
1637 if(tl > 0 || fl > 0)
1638 str = strcat(str, "^7 / "); // delimiter
1641 str = strcat(str, sprintf(_("^2+%s %s"), ScoreString(teamscores_flags(ts_primary), ll),
1642 (teamscores_label(ts_primary) == "score") ? CTX(_("SCO^points")) :
1643 (teamscores_label(ts_primary) == "fastest") ? CTX(_("SCO^is beaten")) :
1644 TranslateScoresLabel(teamscores_label(ts_primary))));
1648 str = strcat(str, sprintf(_("^2+%s %s"), ScoreString(scores_flags(ps_primary), ll),
1649 (scores_label(ps_primary) == "score") ? CTX(_("SCO^points")) :
1650 (scores_label(ps_primary) == "fastest") ? CTX(_("SCO^is beaten")) :
1651 TranslateScoresLabel(scores_label(ps_primary))));
1656 pos.y += sb_gameinfo_type_fontsize.y;
1657 drawcolorcodedstring(pos + '1 0 0' * (panel_size.x - stringwidth(str, true, sb_gameinfo_detail_fontsize)), str, sb_gameinfo_detail_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL); // align right
1659 str = sprintf(_("^7Map: ^2%s"), shortmapname);
1660 drawcolorcodedstring(pos, str, sb_gameinfo_detail_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL); // align left
1661 // End of Game Info Section
1663 pos.y += sb_gameinfo_detail_fontsize.y + hud_fontsize.y * 0.3; // space between Game Info Section and score table
1664 if(panel.current_panel_bg != "0")
1665 pos.y += panel_bg_border;
1667 // Draw the scoreboard
1668 float scale = autocvar_hud_panel_scoreboard_table_bg_scale;
1671 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * scale;
1675 vector panel_bg_color_save = panel_bg_color;
1676 vector team_score_baseoffset;
1677 vector team_size_baseoffset;
1678 if (sbt_team_size_position != 1) // team size not on left
1680 // put team score to the left of scoreboard (and team size to the right)
1681 team_score_baseoffset = eY * hud_fontsize.y - eX * hud_fontsize.x * 0.5;
1682 team_size_baseoffset = eY * hud_fontsize.y + eX * hud_fontsize.x * 0.5;
1683 if(panel.current_panel_bg != "0")
1685 team_score_baseoffset.x -= panel_bg_border;
1686 team_size_baseoffset.x += panel_bg_border;
1691 // put team score to the right of scoreboard (and team size to the left)
1692 team_score_baseoffset = eY * hud_fontsize.y + eX * hud_fontsize.x * 0.5;
1693 team_size_baseoffset = eY * hud_fontsize.y - eX * hud_fontsize.x * 0.5;
1694 if(panel.current_panel_bg != "0")
1696 team_score_baseoffset.x += panel_bg_border;
1697 team_size_baseoffset.x -= panel_bg_border;
1701 int team_size_total = 0;
1702 if (sbt_team_size_position != 0) // team size not off
1704 // calculate team size total (sum of all team sizes)
1705 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1706 if(tm.team != NUM_SPECTATOR)
1707 team_size_total += tm.team_size;
1710 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1712 if(tm.team == NUM_SPECTATOR)
1717 draw_beginBoldFont();
1718 vector rgb = Team_ColorRGB(tm.team);
1719 str = ftos(tm.(teamscores(ts_primary)));
1720 if (sbt_team_size_position != 1) // team size not on left
1722 // team score on the left (default)
1723 str_pos = pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5);
1727 // team score on the right
1728 str_pos = pos + team_score_baseoffset + eX * (panel_size.x + hud_fontsize.x * 1.5);
1730 drawstring(str_pos, str, hud_fontsize * 1.5, rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
1732 // team size (if set to show on the side)
1733 if (sbt_team_size_position != 0) // team size not off
1735 // calculate the starting position for the whole team size info string
1736 str = sprintf("%d/%d", tm.team_size, team_size_total);
1737 if (sbt_team_size_position == 1)
1739 // team size on the left
1740 str_pos = pos + team_size_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5);
1744 // team size on the right
1745 str_pos = pos + team_size_baseoffset + eX * (panel_size.x + hud_fontsize.x * 1.5);
1747 str = sprintf("%d", tm.team_size);
1748 drawstring(str_pos, str, hud_fontsize * 1.5, rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
1749 str_pos += eX * stringwidth(str, true, hud_fontsize * 1.5) + eY * hud_fontsize.y * .5;
1750 str = sprintf("/%d", team_size_total);
1751 drawstring(str_pos, str, hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1755 // secondary score, e.g. keyhunt
1756 if(ts_primary != ts_secondary)
1758 str = ftos(tm.(teamscores(ts_secondary)));
1759 if (sbt_team_size_position != 1) // team size not on left
1762 str_pos = pos + team_score_baseoffset - vec2(stringwidth(str, false, hud_fontsize), hud_fontsize.y * -1.5);
1767 str_pos = pos + team_score_baseoffset + vec2(panel_size.x + hud_fontsize.x * 1.5, hud_fontsize.y * 1.5);
1770 drawstring(str_pos, str, hud_fontsize, rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
1773 if(autocvar_hud_panel_scoreboard_bg_teams_color_team > 0)
1774 panel_bg_color = rgb * autocvar_hud_panel_scoreboard_bg_teams_color_team;
1775 else if(panel_bg_color_team > 0)
1776 panel_bg_color = rgb * panel_bg_color_team;
1778 panel_bg_color = rgb;
1779 pos = Scoreboard_MakeTable(pos, tm, panel_bg_color, bg_size);
1781 panel_bg_color = panel_bg_color_save;
1785 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1786 if(tm.team != NUM_SPECTATOR)
1788 // display it anyway
1789 pos = Scoreboard_MakeTable(pos, tm, panel_bg_color, bg_size);
1792 if (Scoreboard_AccuracyStats_WouldDraw(pos.y))
1793 pos = Scoreboard_AccuracyStats_Draw(pos, panel_bg_color, bg_size);
1795 if(ISGAMETYPE(CTS) || ISGAMETYPE(RACE) || (autocvar_hud_panel_scoreboard_ctf_leaderboard && ISGAMETYPE(CTF) && STAT(CTF_SHOWLEADERBOARD))) {
1796 if(race_speedaward) {
1797 drawcolorcodedstring(pos, sprintf(_("Speed award: %d%s ^7(%s^7)"), race_speedaward, race_speedaward_unit, ColorTranslateRGB(race_speedaward_holder)), hud_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
1798 pos.y += 1.25 * hud_fontsize.y;
1800 if(race_speedaward_alltimebest) {
1801 drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d%s ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_unit, ColorTranslateRGB(race_speedaward_alltimebest_holder)), hud_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
1802 pos.y += 1.25 * hud_fontsize.y;
1804 pos = Scoreboard_Rankings_Draw(pos, playerslots[player_localnum], panel_bg_color, bg_size);
1807 pos = Scoreboard_MapStats_Draw(pos, panel_bg_color, bg_size);
1810 for(pl = players.sort_next; pl; pl = pl.sort_next)
1812 if(pl.team == NUM_SPECTATOR)
1814 for(tm = teams.sort_next; tm; tm = tm.sort_next)
1815 if(tm.team == NUM_SPECTATOR)
1817 str = sprintf("%s (%d)", _("Spectators"), tm.team_size);
1818 draw_beginBoldFont();
1819 drawstring(pos, str, hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1821 pos.y += 1.25 * hud_fontsize.y;
1823 pos = Scoreboard_DrawOthers(pos, '0 0 0', pl.team, NULL, pl, 0);
1824 pos.y += 1.25 * hud_fontsize.y;
1831 // print information about respawn status
1832 float respawn_time = STAT(RESPAWN_TIME);
1836 if(respawn_time < 0)
1838 // a negative number means we are awaiting respawn, time value is still the same
1839 respawn_time *= -1; // remove mark now that we checked it
1841 if(respawn_time < time) // it happens for a few frames when server is respawning the player
1842 str = ""; // draw an empty string to not change suddenly scoreboard_bottom
1844 str = sprintf(_("^1Respawning in ^3%s^1..."),
1845 (autocvar_hud_panel_scoreboard_respawntime_decimals ?
1846 count_seconds_decs(respawn_time - time, autocvar_hud_panel_scoreboard_respawntime_decimals)
1848 count_seconds(ceil(respawn_time - time))
1852 else if(time < respawn_time)
1854 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
1855 (autocvar_hud_panel_scoreboard_respawntime_decimals ?
1856 count_seconds_decs(respawn_time - time, autocvar_hud_panel_scoreboard_respawntime_decimals)
1858 count_seconds(ceil(respawn_time - time))
1862 else if(time >= respawn_time)
1863 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
1865 pos.y += 1.2 * hud_fontsize.y;
1866 drawcolorcodedstring(pos + '0.5 0 0' * (panel_size.x - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
1869 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;