1 #include "scoreboard.qh"
3 #include <client/draw.qh>
4 #include <client/hud/panel/chat.qh>
5 #include <client/hud/panel/quickmenu.qh>
6 #include <client/hud/panel/racetimer.qh>
7 #include <client/hud/panel/weapons.qh>
8 #include <common/constants.qh>
9 #include <common/ent_cs.qh>
10 #include <common/mapinfo.qh>
11 #include <common/minigames/cl_minigames.qh>
12 #include <common/net_linked.qh>
13 #include <common/scores.qh>
14 #include <common/stats.qh>
15 #include <common/teams.qh>
16 #include <common/items/inventory.qh>
20 void Scoreboard_Draw_Export(int fh)
22 // allow saving cvars that aesthetically change the panel into hud skin files
23 HUD_Write_Cvar("hud_panel_scoreboard_fadeinspeed");
24 HUD_Write_Cvar("hud_panel_scoreboard_fadeoutspeed");
25 HUD_Write_Cvar("hud_panel_scoreboard_respawntime_decimals");
26 HUD_Write_Cvar("hud_panel_scoreboard_table_bg_alpha");
27 HUD_Write_Cvar("hud_panel_scoreboard_table_bg_scale");
28 HUD_Write_Cvar("hud_panel_scoreboard_table_fg_alpha");
29 HUD_Write_Cvar("hud_panel_scoreboard_table_fg_alpha_self");
30 HUD_Write_Cvar("hud_panel_scoreboard_table_highlight");
31 HUD_Write_Cvar("hud_panel_scoreboard_table_highlight_alpha");
32 HUD_Write_Cvar("hud_panel_scoreboard_table_highlight_alpha_self");
33 HUD_Write_Cvar("hud_panel_scoreboard_table_highlight_alpha_eliminated");
34 HUD_Write_Cvar("hud_panel_scoreboard_bg_teams_color_team");
35 HUD_Write_Cvar("hud_panel_scoreboard_accuracy_doublerows");
36 HUD_Write_Cvar("hud_panel_scoreboard_accuracy_nocolors");
39 const int MAX_SBT_FIELDS = MAX_SCORE;
41 PlayerScoreField sbt_field[MAX_SBT_FIELDS + 1];
42 float sbt_field_size[MAX_SBT_FIELDS + 1];
43 string sbt_field_title[MAX_SBT_FIELDS + 1];
46 string autocvar_hud_fontsize;
47 string hud_fontsize_str;
50 vector duel_score_fontsize;
51 vector duel_name_fontsize;
52 vector duel_score_size;
53 vector team_score_fontsize;
54 vector team_name_fontsize;
55 vector team_score_size;
60 float sbt_fg_alpha_self;
62 float sbt_highlight_alpha;
63 float sbt_highlight_alpha_self;
64 float sbt_highlight_alpha_eliminated;
66 // provide basic panel cvars to old clients
67 // TODO remove them after a future release (0.8.2+)
68 noref string autocvar_hud_panel_scoreboard_pos = "0.150000 0.150000";
69 noref string autocvar_hud_panel_scoreboard_size = "0.700000 0.700000";
70 noref string autocvar_hud_panel_scoreboard_bg = "border_default";
71 noref string autocvar_hud_panel_scoreboard_bg_color = "0 0.3 0.5";
72 noref string autocvar_hud_panel_scoreboard_bg_color_team = "";
73 noref string autocvar_hud_panel_scoreboard_bg_alpha = "0.7";
74 noref string autocvar_hud_panel_scoreboard_bg_border = "";
75 noref string autocvar_hud_panel_scoreboard_bg_padding = "";
77 float autocvar_hud_panel_scoreboard_fadeinspeed = 10;
78 float autocvar_hud_panel_scoreboard_fadeoutspeed = 5;
79 float autocvar_hud_panel_scoreboard_respawntime_decimals = 1;
80 float autocvar_hud_panel_scoreboard_table_bg_alpha = 0;
81 float autocvar_hud_panel_scoreboard_table_bg_scale = 0.25;
82 float autocvar_hud_panel_scoreboard_table_fg_alpha = 0.9;
83 float autocvar_hud_panel_scoreboard_table_fg_alpha_self = 1;
84 bool autocvar_hud_panel_scoreboard_table_highlight = true;
85 float autocvar_hud_panel_scoreboard_table_highlight_alpha = 0.2;
86 float autocvar_hud_panel_scoreboard_table_highlight_alpha_self = 0.4;
87 float autocvar_hud_panel_scoreboard_table_highlight_alpha_eliminated = 0.6;
88 float autocvar_hud_panel_scoreboard_bg_teams_color_team = 0;
89 float autocvar_hud_panel_scoreboard_namesize = 15;
90 float autocvar_hud_panel_scoreboard_team_size_position = 0;
92 bool autocvar_hud_panel_scoreboard_accuracy = true;
93 bool autocvar_hud_panel_scoreboard_accuracy_doublerows = false;
94 bool autocvar_hud_panel_scoreboard_accuracy_nocolors = false;
95 float autocvar_hud_panel_scoreboard_accuracy_showdelay = 2;
96 float autocvar_hud_panel_scoreboard_accuracy_showdelay_minpos = 0.75;
98 bool autocvar_hud_panel_scoreboard_dynamichud = false;
100 float autocvar_hud_panel_scoreboard_maxheight = 0.6;
101 bool autocvar_hud_panel_scoreboard_others_showscore = true;
102 bool autocvar_hud_panel_scoreboard_spectators_showping = true;
103 bool autocvar_hud_panel_scoreboard_spectators_aligned = false;
104 float autocvar_hud_panel_scoreboard_minwidth = 0.4;
105 bool autocvar_hud_panel_scoreboard_playerid = false;
106 string autocvar_hud_panel_scoreboard_playerid_prefix = "#";
107 string autocvar_hud_panel_scoreboard_playerid_suffix = " ";
109 int average_ping[NUM_TEAMS];
111 // mode 0: returns translated label
112 // mode 1: prints name and description of all the labels
113 string Label_getInfo(string label, int mode)
116 label = "bckills"; // first case in the switch
120 case "bckills": if (!mode) return CTX(_("SCO^bckills")); else LOG_HELP(strcat("^3", "bckills", " ^7", _("Number of ball carrier kills")));
121 case "bctime": if (!mode) return CTX(_("SCO^bctime")); else LOG_HELP(strcat("^3", "bctime", " ^7", _("Total amount of time holding the ball in Keepaway")));
122 case "caps": if (!mode) return CTX(_("SCO^caps")); else LOG_HELP(strcat("^3", "caps", " ^7", _("How often a flag (CTF) or a key (KeyHunt) was captured")));
123 case "captime": if (!mode) return CTX(_("SCO^captime")); else LOG_HELP(strcat("^3", "captime", " ^7", _("Time of fastest capture (CTF)")));
124 case "deaths": if (!mode) return CTX(_("SCO^deaths")); else LOG_HELP(strcat("^3", "deaths", " ^7", _("Number of deaths")));
125 case "destroyed": if (!mode) return CTX(_("SCO^destroyed")); else LOG_HELP(strcat("^3", "destroyed", " ^7", _("Number of keys destroyed by pushing them into void")));
126 case "dmg": if (!mode) return CTX(_("SCO^damage")); else LOG_HELP(strcat("^3", "dmg", " ^7", _("The total damage done")));
127 case "dmgtaken": if (!mode) return CTX(_("SCO^dmgtaken")); else LOG_HELP(strcat("^3", "dmgtaken", " ^7", _("The total damage taken")));
128 case "drops": if (!mode) return CTX(_("SCO^drops")); else LOG_HELP(strcat("^3", "drops", " ^7", _("Number of flag drops")));
129 case "elo": if (!mode) return CTX(_("SCO^elo")); else LOG_HELP(strcat("^3", "elo", " ^7", _("Player ELO")));
130 case "fastest": if (!mode) return CTX(_("SCO^fastest")); else LOG_HELP(strcat("^3", "fastest", " ^7", _("Time of fastest lap (Race/CTS)")));
131 case "faults": if (!mode) return CTX(_("SCO^faults")); else LOG_HELP(strcat("^3", "faults", " ^7", _("Number of faults committed")));
132 case "fckills": if (!mode) return CTX(_("SCO^fckills")); else LOG_HELP(strcat("^3", "fckills", " ^7", _("Number of flag carrier kills")));
133 case "fps": if (!mode) return CTX(_("SCO^fps")); else LOG_HELP(strcat("^3", "fps", " ^7", _("FPS")));
134 case "frags": if (!mode) return CTX(_("SCO^frags")); else LOG_HELP(strcat("^3", "frags", " ^7", _("Number of kills minus suicides")));
135 case "goals": if (!mode) return CTX(_("SCO^goals")); else LOG_HELP(strcat("^3", "goals", " ^7", _("Number of goals scored")));
136 case "kckills": if (!mode) return CTX(_("SCO^kckills")); else LOG_HELP(strcat("^3", "kckills", " ^7", _("Number of keys carrier kills")));
137 case "kd": if (!mode) return CTX(_("SCO^k/d")); else LOG_HELP(strcat("^3", "kd", " ^7", _("The kill-death ratio")));
138 case "kdr": if (!mode) return CTX(_("SCO^kdr")); else LOG_HELP(strcat("^3", "kdr", " ^7", _("The kill-death ratio")));
139 case "kdratio": if (!mode) return CTX(_("SCO^kdratio")); else LOG_HELP(strcat("^3", "kdratio", " ^7", _("The kill-death ratio")));
140 case "kills": if (!mode) return CTX(_("SCO^kills")); else LOG_HELP(strcat("^3", "kills", " ^7", _("Number of kills")));
141 case "laps": if (!mode) return CTX(_("SCO^laps")); else LOG_HELP(strcat("^3", "laps", " ^7", _("Number of laps finished (Race/CTS)")));
142 case "lives": if (!mode) return CTX(_("SCO^lives")); else LOG_HELP(strcat("^3", "lives", " ^7", _("Number of lives (LMS)")));
143 case "losses": if (!mode) return CTX(_("SCO^losses")); else LOG_HELP(strcat("^3", "losses", " ^7", _("Number of times a key was lost")));
144 case "name": if (!mode) return CTX(_("SCO^name")); else LOG_HELP(strcat("^3", "name", " ^7", _("Player name")));
145 case "nick": if (!mode) return CTX(_("SCO^nick")); else LOG_HELP(strcat("^3", "nick", " ^7", _("Player name")));
146 case "objectives": if (!mode) return CTX(_("SCO^objectives")); else LOG_HELP(strcat("^3", "objectives", " ^7", _("Number of objectives destroyed")));
147 case "pickups": if (!mode) return CTX(_("SCO^pickups")); else LOG_HELP(strcat("^3", "pickups", " ^7", _("How often a flag (CTF) or a key (KeyHunt) or a ball (Keepaway) was picked up")));
148 case "ping": if (!mode) return CTX(_("SCO^ping")); else LOG_HELP(strcat("^3", "ping", " ^7", _("Ping time")));
149 case "pl": if (!mode) return CTX(_("SCO^pl")); else LOG_HELP(strcat("^3", "pl", " ^7", _("Packet loss")));
150 case "pushes": if (!mode) return CTX(_("SCO^pushes")); else LOG_HELP(strcat("^3", "pushes", " ^7", _("Number of players pushed into void")));
151 case "rank": if (!mode) return CTX(_("SCO^rank")); else LOG_HELP(strcat("^3", "rank", " ^7", _("Player rank")));
152 case "returns": if (!mode) return CTX(_("SCO^returns")); else LOG_HELP(strcat("^3", "returns", " ^7", _("Number of flag returns")));
153 case "revivals": if (!mode) return CTX(_("SCO^revivals")); else LOG_HELP(strcat("^3", "revivals", " ^7", _("Number of revivals")));
154 case "rounds": if (!mode) return CTX(_("SCO^rounds won")); else LOG_HELP(strcat("^3", "rounds", " ^7", _("Number of rounds won")));
155 case "score": if (!mode) return CTX(_("SCO^score")); else LOG_HELP(strcat("^3", "score", " ^7", _("Total score")));
156 case "suicides": if (!mode) return CTX(_("SCO^suicides")); else LOG_HELP(strcat("^3", "suicides", " ^7", _("Number of suicides")));
157 case "sum": if (!mode) return CTX(_("SCO^sum")); else LOG_HELP(strcat("^3", "sum", " ^7", _("Number of kills minus deaths")));
158 case "takes": if (!mode) return CTX(_("SCO^takes")); else LOG_HELP(strcat("^3", "takes", " ^7", _("Number of domination points taken (Domination)")));
159 case "teamkills": if (!mode) return CTX(_("SCO^teamkills")); else LOG_HELP(strcat("^3", "teamkills", " ^7", _("Number of teamkills")));
160 case "ticks": if (!mode) return CTX(_("SCO^ticks")); else LOG_HELP(strcat("^3", "ticks", " ^7", _("Number of ticks (Domination)")));
161 case "time": if (!mode) return CTX(_("SCO^time")); else LOG_HELP(strcat("^3", "time", " ^7", _("Total time raced (Race/CTS)")));
162 default: return label;
167 void PrintScoresLabels() { Label_getInfo(string_null, 1); }
168 string TranslateScoresLabel(string label) { return Label_getInfo(label, 0); }
170 void Scoreboard_InitScores()
174 ps_primary = ps_secondary = NULL;
175 ts_primary = ts_secondary = -1;
176 FOREACH(Scores, true, {
177 f = (scores_flags(it) & SFL_SORT_PRIO_MASK);
178 if(f == SFL_SORT_PRIO_PRIMARY)
180 if(f == SFL_SORT_PRIO_SECONDARY)
183 if(ps_secondary == NULL)
184 ps_secondary = ps_primary;
186 for(i = 0; i < MAX_TEAMSCORE; ++i)
188 f = (teamscores_flags(i) & SFL_SORT_PRIO_MASK);
189 if(f == SFL_SORT_PRIO_PRIMARY)
191 if(f == SFL_SORT_PRIO_SECONDARY)
194 if(ts_secondary == -1)
195 ts_secondary = ts_primary;
197 Cmd_Scoreboard_SetFields(0);
201 void Scoreboard_UpdatePlayerTeams()
205 for(pl = players.sort_next; pl; pl = pl.sort_next)
208 int Team = entcs_GetScoreTeam(pl.sv_entnum);
209 if(SetTeam(pl, Team))
212 Scoreboard_UpdatePlayerPos(pl);
216 pl = players.sort_next;
221 print(strcat("PNUM: ", ftos(num), "\n"));
226 int Scoreboard_CompareScore(int vl, int vr, int f)
228 TC(int, vl); TC(int, vr); TC(int, f);
229 if(f & SFL_ZERO_IS_WORST)
231 if(vl == 0 && vr != 0)
233 if(vl != 0 && vr == 0)
237 return IS_INCREASING(f);
239 return IS_DECREASING(f);
243 float Scoreboard_ComparePlayerScores(entity left, entity right)
246 vl = entcs_GetTeam(left.sv_entnum);
247 vr = entcs_GetTeam(right.sv_entnum);
259 if(vl == NUM_SPECTATOR)
261 // FIRST the one with scores (spectators), THEN the ones without (downloaders)
263 if(!left.gotscores && right.gotscores)
268 r = Scoreboard_CompareScore(left.scores(ps_primary), right.scores(ps_primary), scores_flags(ps_primary));
272 r = Scoreboard_CompareScore(left.scores(ps_secondary), right.scores(ps_secondary), scores_flags(ps_secondary));
276 FOREACH(Scores, true, {
277 r = Scoreboard_CompareScore(left.scores(it), right.scores(it), scores_flags(it));
278 if (r >= 0) return r;
281 if (left.sv_entnum < right.sv_entnum)
287 void Scoreboard_UpdatePlayerPos(entity player)
290 for(ent = player.sort_next; ent && Scoreboard_ComparePlayerScores(player, ent); ent = player.sort_next)
292 SORT_SWAP(player, ent);
294 for(ent = player.sort_prev; ent != players && Scoreboard_ComparePlayerScores(ent, player); ent = player.sort_prev)
296 SORT_SWAP(ent, player);
300 float Scoreboard_CompareTeamScores(entity left, entity right)
304 if(left.team == NUM_SPECTATOR)
306 if(right.team == NUM_SPECTATOR)
309 r = Scoreboard_CompareScore(left.teamscores(ts_primary), right.teamscores(ts_primary), teamscores_flags(ts_primary));
313 r = Scoreboard_CompareScore(left.teamscores(ts_secondary), right.teamscores(ts_secondary), teamscores_flags(ts_secondary));
317 for(i = 0; i < MAX_TEAMSCORE; ++i)
319 r = Scoreboard_CompareScore(left.teamscores(i), right.teamscores(i), teamscores_flags(i));
324 if (left.team < right.team)
330 void Scoreboard_UpdateTeamPos(entity Team)
333 for(ent = Team.sort_next; ent && Scoreboard_CompareTeamScores(Team, ent); ent = Team.sort_next)
335 SORT_SWAP(Team, ent);
337 for(ent = Team.sort_prev; ent != teams && Scoreboard_CompareTeamScores(ent, Team); ent = Team.sort_prev)
339 SORT_SWAP(ent, Team);
343 void Cmd_Scoreboard_Help()
345 LOG_HELP(_("You can modify the scoreboard using the ^2scoreboard_columns_set command."));
346 LOG_HELP(_("Usage:"));
347 LOG_HELP("^2scoreboard_columns_set ^3default");
348 LOG_HELP(_("^2scoreboard_columns_set ^3field1 field2 ..."));
349 LOG_HELP(_("^2scoreboard_columns_set ^7without arguments reads the arguments from the cvar scoreboard_columns"));
350 LOG_HELP(_(" ^5Note: ^7scoreboard_columns_set without arguments is executed on every map start"));
351 LOG_HELP(_("^2scoreboard_columns_set ^3expand_default ^7loads default layout and expands it into the cvar scoreboard_columns so you can edit it"));
352 LOG_HELP(_("You can use a ^3|^7 to start the right-aligned fields."));
353 LOG_HELP(_("The following field names are recognized (case insensitive):"));
359 LOG_HELP(_("Before a field you can put a + or - sign, then a comma separated list\n"
360 "of game types, then a slash, to make the field show up only in these\n"
361 "or in all but these game types. You can also specify 'all' as a\n"
362 "field to show all fields available for the current game mode."));
365 LOG_HELP(_("The special game type names 'teams' and 'noteams' can be used to\n"
366 "include/exclude ALL teams/noteams game modes."));
369 LOG_HELP(_("Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4"));
370 LOG_HELP(_("will display name, ping and pl aligned to the left, and the fields\n"
371 "right of the vertical bar aligned to the right."));
372 LOG_HELP(_("'field3' will only be shown in CTF, and 'field4' will be shown in all\n"
373 "other gamemodes except DM."));
376 // NOTE: adding a gametype with ? to not warn for an optional field
377 // make sure it's excluded in a previous exclusive rule, if any
378 // otherwise the previous exclusive rule warns anyway
379 // e.g. -teams,rc,cts,lms/kills ?+rc/kills
380 #define SCOREBOARD_DEFAULT_COLUMNS \
381 "ping pl fps name |" \
382 " -teams,rc,cts,inv,lms/kills +ft,tdm/kills ?+rc,inv/kills" \
383 " -teams,lms/deaths +ft,tdm/deaths" \
385 " -teams,lms,rc,cts,inv,ka/suicides +ft,tdm/suicides ?+rc,inv/suicides" \
386 " -cts,dm,tdm,ka,ft/frags" /* tdm already has this in "score" */ \
387 " +tdm,ft,dom,ons,as/teamkills"\
388 " -rc,cts,nb/dmg -rc,cts,nb/dmgtaken" \
389 " +ctf/pickups +ctf/fckills +ctf/returns +ctf/caps +ons/takes +ons/caps" \
390 " +lms/lives +lms/rank" \
391 " +kh/kckills +kh/losses +kh/caps" \
392 " ?+rc/laps ?+rc/time +rc,cts/fastest" \
393 " +as/objectives +nb/faults +nb/goals" \
394 " +ka/pickups +ka/bckills +ka/bctime +ft/revivals" \
395 " +dom/ticks +dom/takes" \
396 " -lms,rc,cts,inv,nb/score"
398 void Cmd_Scoreboard_SetFields(int argc)
403 bool have_name = false, have_primary = false, have_secondary = false, have_separator = false;
407 return; // do nothing, we don't know gametype and scores yet
409 // sbt_fields uses strunzone on the titles!
410 if(!sbt_field_title[0])
411 for(i = 0; i < MAX_SBT_FIELDS; ++i)
412 sbt_field_title[i] = strzone("(null)");
414 // TODO: re enable with gametype dependant cvars?
415 if(argc < 3) // no arguments provided
416 argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " ");
419 argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " ");
423 if(argv(2) == "default" || argv(2) == "expand_default")
425 if(argv(2) == "expand_default")
426 cvar_set("scoreboard_columns", SCOREBOARD_DEFAULT_COLUMNS);
427 argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " ");
429 else if(argv(2) == "all")
431 string s = "ping pl name |"; // scores without a label
432 FOREACH(Scores, true, {
434 if(it != ps_secondary)
435 if(scores_label(it) != "")
436 s = strcat(s, " ", scores_label(it));
438 if(ps_secondary != ps_primary)
439 s = strcat(s, " ", scores_label(ps_secondary));
440 s = strcat(s, " ", scores_label(ps_primary));
441 argc = tokenizebyseparator(strcat("0 1 ", s), " ");
448 hud_fontsize = HUD_GetFontsize("hud_fontsize");
450 duel_score_fontsize = hud_fontsize * 3;
451 duel_name_fontsize = hud_fontsize * 1.5;
452 duel_score_size = vec2(duel_score_fontsize.x * 1.5, duel_score_fontsize.y * 1.25);
454 team_score_fontsize = hud_fontsize * 2;
455 team_name_fontsize = hud_fontsize * 1.5;
456 team_score_size = vec2(team_score_fontsize.x * 1.5, team_score_fontsize.y * 1.25);
458 for(i = 1; i < argc - 1; ++i)
461 bool nocomplain = false;
462 if(substring(str, 0, 1) == "?")
465 str = substring(str, 1, strlen(str) - 1);
468 slash = strstrofs(str, "/", 0);
471 pattern = substring(str, 0, slash);
472 str = substring(str, slash + 1, strlen(str) - (slash + 1));
474 if (!isGametypeInFilter(gametype, teamplay, false, pattern))
478 strcpy(sbt_field_title[sbt_num_fields], TranslateScoresLabel(str));
479 sbt_field_size[sbt_num_fields] = stringwidth(sbt_field_title[sbt_num_fields], false, hud_fontsize);
480 str = strtolower(str);
485 case "ping": sbt_field[sbt_num_fields] = SP_PING; break;
486 case "pl": sbt_field[sbt_num_fields] = SP_PL; break;
487 case "kd": case "kdr": case "kdratio": sbt_field[sbt_num_fields] = SP_KDRATIO; break;
488 case "sum": case "diff": case "k-d": sbt_field[sbt_num_fields] = SP_SUM; break;
489 case "name": case "nick": sbt_field[sbt_num_fields] = SP_NAME; have_name = true; break;
490 case "|": sbt_field[sbt_num_fields] = SP_SEPARATOR; have_separator = true; break;
491 case "elo": sbt_field[sbt_num_fields] = SP_ELO; break;
492 case "dmg": case "damage": sbt_field[sbt_num_fields] = SP_DMG; break;
493 case "dmgtaken": case "damagetaken": sbt_field[sbt_num_fields] = SP_DMGTAKEN; break;
494 case "fps": sbt_field[sbt_num_fields] = SP_FPS; break;
497 FOREACH(Scores, true, {
498 if (str == strtolower(scores_label(it))) {
500 goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
510 LOG_INFOF("^1Error:^7 Unknown score field: '%s'", str);
514 sbt_field[sbt_num_fields] = j;
517 if(j == ps_secondary)
518 have_secondary = true;
523 if(sbt_num_fields >= MAX_SBT_FIELDS)
527 if(scores_flags(ps_primary) & SFL_ALLOW_HIDE)
529 if(scores_flags(ps_secondary) & SFL_ALLOW_HIDE)
530 have_secondary = true;
531 if(ps_primary == ps_secondary)
532 have_secondary = true;
533 missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
535 if(sbt_num_fields + missing < MAX_SBT_FIELDS)
539 strunzone(sbt_field_title[sbt_num_fields]);
540 for(i = sbt_num_fields; i > 0; --i)
542 sbt_field_title[i] = sbt_field_title[i-1];
543 sbt_field_size[i] = sbt_field_size[i-1];
544 sbt_field[i] = sbt_field[i-1];
546 sbt_field_title[0] = strzone(TranslateScoresLabel("name"));
547 sbt_field[0] = SP_NAME;
549 LOG_INFO("fixed missing field 'name'");
553 strunzone(sbt_field_title[sbt_num_fields]);
554 for(i = sbt_num_fields; i > 1; --i)
556 sbt_field_title[i] = sbt_field_title[i-1];
557 sbt_field_size[i] = sbt_field_size[i-1];
558 sbt_field[i] = sbt_field[i-1];
560 sbt_field_title[1] = strzone("|");
561 sbt_field[1] = SP_SEPARATOR;
562 sbt_field_size[1] = stringwidth("|", false, hud_fontsize);
564 LOG_INFO("fixed missing field '|'");
567 else if(!have_separator)
569 strcpy(sbt_field_title[sbt_num_fields], "|");
570 sbt_field_size[sbt_num_fields] = stringwidth("|", false, hud_fontsize);
571 sbt_field[sbt_num_fields] = SP_SEPARATOR;
573 LOG_INFO("fixed missing field '|'");
577 strcpy(sbt_field_title[sbt_num_fields], TranslateScoresLabel(scores_label(ps_secondary)));
578 sbt_field_size[sbt_num_fields] = stringwidth(sbt_field_title[sbt_num_fields], false, hud_fontsize);
579 sbt_field[sbt_num_fields] = ps_secondary;
581 LOG_INFOF("fixed missing field '%s'", scores_label(ps_secondary));
585 strcpy(sbt_field_title[sbt_num_fields], TranslateScoresLabel(scores_label(ps_primary)));
586 sbt_field_size[sbt_num_fields] = stringwidth(sbt_field_title[sbt_num_fields], false, hud_fontsize);
587 sbt_field[sbt_num_fields] = ps_primary;
589 LOG_INFOF("fixed missing field '%s'", scores_label(ps_primary));
593 sbt_field[sbt_num_fields] = SP_END;
596 string Scoreboard_AddPlayerId(string pl_name, entity pl)
598 string pref = autocvar_hud_panel_scoreboard_playerid_prefix;
599 string suf = autocvar_hud_panel_scoreboard_playerid_suffix;
600 return strcat(pref, itos(pl.sv_entnum + 1), suf, pl_name);
604 vector sbt_field_rgb;
605 string sbt_field_icon0;
606 string sbt_field_icon1;
607 string sbt_field_icon2;
608 vector sbt_field_icon0_rgb;
609 vector sbt_field_icon1_rgb;
610 vector sbt_field_icon2_rgb;
611 string Scoreboard_GetName(entity pl)
613 if(ready_waiting && pl.ready)
615 sbt_field_icon0 = "gfx/scoreboard/player_ready";
619 int f = entcs_GetClientColors(pl.sv_entnum);
621 sbt_field_icon0 = "gfx/scoreboard/playercolor_base";
622 sbt_field_icon1 = "gfx/scoreboard/playercolor_shirt";
623 sbt_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0);
624 sbt_field_icon2 = "gfx/scoreboard/playercolor_pants";
625 sbt_field_icon2_rgb = colormapPaletteColor(f % 16, 1);
630 int ccode = entcs_GetCountryCode(pl.sv_entnum);
632 sbt_field_icon0 = strcat("gfx/flags/", ftos(ccode));
635 return entcs_GetName(pl.sv_entnum);
638 vector getPingColor(float f)
641 // 20-80 range is green
642 return '0 1 0' + '1 0 1' * max(0, min(60, f-20)) / 60;
644 // 80-300 range is red
645 return '1 1 1' - '0 1 1' * max(0, min(220, f-80)) / 220;
648 string Scoreboard_GetField(entity pl, PlayerScoreField field)
650 float tmp, num, denom;
653 sbt_field_rgb = '1 1 1';
654 sbt_field_icon0 = "";
655 sbt_field_icon1 = "";
656 sbt_field_icon2 = "";
657 sbt_field_icon0_rgb = '1 1 1';
658 sbt_field_icon1_rgb = '1 1 1';
659 sbt_field_icon2_rgb = '1 1 1';
664 return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6 (Black Right-Pointing Triangle)
665 //str = getplayerkeyvalue(pl.sv_entnum, "ping");
669 sbt_field_rgb = getPingColor(f);
672 //return ftos(pl.team);
677 f = pl.ping_packetloss;
678 tmp = pl.ping_movementloss;
679 if(f == 0 && tmp == 0)
681 str = ftos(ceil(f * 100));
683 str = strcat(str, "~", ftos(ceil(tmp * 100)));
684 tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
685 sbt_field_rgb = '1 0.5 0.5' - '0 0.5 0.5' * tmp;
689 str = Scoreboard_GetName(pl);
690 if (autocvar_hud_panel_scoreboard_playerid)
691 str = Scoreboard_AddPlayerId(str, pl);
695 f = pl.(scores(SP_KILLS));
696 f -= pl.(scores(SP_SUICIDES));
700 num = pl.(scores(SP_KILLS));
701 denom = pl.(scores(SP_DEATHS));
704 sbt_field_rgb = '0 1 0';
705 str = sprintf("%d", num);
706 } else if(num <= 0) {
707 sbt_field_rgb = '1 0 0';
708 str = sprintf("%.1f", num/denom);
710 str = sprintf("%.1f", num/denom);
714 f = pl.(scores(SP_KILLS));
715 f -= pl.(scores(SP_DEATHS));
718 sbt_field_rgb = '0 1 0';
720 sbt_field_rgb = '1 1 1';
722 sbt_field_rgb = '1 0 0';
728 float elo = pl.(scores(SP_ELO));
730 case -1: return "...";
731 case -2: return _("N/A");
732 default: return ftos(elo);
738 float fps = pl.(scores(SP_FPS));
741 sbt_field_rgb = '1 1 1';
742 return ((pl.ping == 0) ? _("N/A") : "..."); // if 0 ping, either connecting or bot (either case can't show proper score)
744 //sbt_field_rgb = HUD_Get_Num_Color(fps, 200, true);
745 sbt_field_rgb = '1 0 0' + '0 1 1' * (bound(0, fps, 60) / 60);
749 case SP_DMG: case SP_DMGTAKEN:
750 return sprintf("%.1f k", pl.(scores(field)) / 1000);
752 default: case SP_SCORE:
753 tmp = pl.(scores(field));
754 f = scores_flags(field);
755 if(field == ps_primary)
756 sbt_field_rgb = '1 1 0';
757 else if(field == ps_secondary)
758 sbt_field_rgb = '0 1 1';
760 sbt_field_rgb = '1 1 1';
761 return ScoreString(f, tmp);
766 float sbt_fixcolumnwidth_len;
767 float sbt_fixcolumnwidth_iconlen;
768 float sbt_fixcolumnwidth_marginlen;
770 string Scoreboard_FixColumnWidth(int i, string str)
776 sbt_fixcolumnwidth_iconlen = 0;
778 if(sbt_field_icon0 != "")
780 sz = draw_getimagesize(sbt_field_icon0);
782 if(sbt_fixcolumnwidth_iconlen < f)
783 sbt_fixcolumnwidth_iconlen = f;
786 if(sbt_field_icon1 != "")
788 sz = draw_getimagesize(sbt_field_icon1);
790 if(sbt_fixcolumnwidth_iconlen < f)
791 sbt_fixcolumnwidth_iconlen = f;
794 if(sbt_field_icon2 != "")
796 sz = draw_getimagesize(sbt_field_icon2);
798 if(sbt_fixcolumnwidth_iconlen < f)
799 sbt_fixcolumnwidth_iconlen = f;
802 if(sbt_fixcolumnwidth_iconlen != 0)
804 sbt_fixcolumnwidth_iconlen *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect
805 sbt_fixcolumnwidth_marginlen = stringwidth(" ", false, hud_fontsize);
808 sbt_fixcolumnwidth_marginlen = 0;
810 if(sbt_field[i] == SP_NAME) // name gets all remaining space
813 float remaining_space = 0;
814 for(j = 0; j < sbt_num_fields; ++j)
816 if (sbt_field[i] != SP_SEPARATOR)
817 remaining_space += sbt_field_size[j] + hud_fontsize.x;
818 sbt_field_size[i] = panel_size.x - remaining_space;
820 if (sbt_fixcolumnwidth_iconlen != 0)
821 remaining_space += sbt_fixcolumnwidth_marginlen + sbt_fixcolumnwidth_iconlen * hud_fontsize.x;
822 float namesize = panel_size.x - remaining_space;
823 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
824 sbt_fixcolumnwidth_len = stringwidth(str, true, hud_fontsize);
826 max_namesize = vid_conwidth - remaining_space;
829 sbt_fixcolumnwidth_len = stringwidth(str, false, hud_fontsize);
831 f = sbt_fixcolumnwidth_len + sbt_fixcolumnwidth_marginlen + sbt_fixcolumnwidth_iconlen * hud_fontsize.x;
832 if(sbt_field_size[i] < f)
833 sbt_field_size[i] = f;
838 void Scoreboard_initFieldSizes()
840 for(int i = 0; i < sbt_num_fields; ++i)
842 sbt_field_size[i] = stringwidth(sbt_field_title[i], false, hud_fontsize);
843 Scoreboard_FixColumnWidth(i, "");
847 vector Scoreboard_DrawHeader(vector pos, vector rgb, bool other_players, int team)
852 vector column_dim = eY * panel_size.y;
855 column_dim.y -= 1.25 * hud_fontsize.y;
856 vector text_offset = eY * (1.25 - 1) / 2 * hud_fontsize.y;
857 pos.x += hud_fontsize.x * 0.5;
858 for(i = 0; i < sbt_num_fields; ++i)
860 if(sbt_field[i] == SP_SEPARATOR)
863 vector text_offset_center = '0 0 0';
865 if(sbt_field[i] == SP_PING && teamplay) {
866 title_str = sprintf("(%d)", average_ping[Team_TeamToIndex(team) - 1]);
867 title_rgb = getPingColor(average_ping[Team_TeamToIndex(team) - 1]);
868 text_offset_center.x = sbt_field_size[i] - stringwidth(title_str, false, hud_fontsize);
870 title_str = sbt_field_title[i];
871 title_rgb = rgb * 1.5;
874 column_dim.x = sbt_field_size[i] + hud_fontsize.x;
877 drawfill(pos - eX * hud_fontsize.x * 0.5, column_dim, '0 0 0', sbt_highlight_alpha, DRAWFLAG_NORMAL);
878 drawstring(pos + text_offset + text_offset_center, title_str, hud_fontsize, title_rgb, sbt_fg_alpha, DRAWFLAG_NORMAL);
879 pos.x += column_dim.x;
881 if(sbt_field[i] == SP_SEPARATOR)
883 pos.x = panel_pos.x + panel_size.x - hud_fontsize.x * 0.5;
884 for(i = sbt_num_fields - 1; i > 0; --i)
886 if(sbt_field[i] == SP_SEPARATOR)
889 pos.x -= sbt_field_size[i];
894 column_dim.x = sbt_field_size[i] + hud_fontsize.x;
895 drawfill(pos - eX * hud_fontsize.x * 0.5, column_dim, '0 0 0', sbt_highlight_alpha, DRAWFLAG_NORMAL);
898 text_offset.x = sbt_field_size[i] - stringwidth(sbt_field_title[i], false, hud_fontsize);
899 drawstring(pos + text_offset, sbt_field_title[i], hud_fontsize, rgb * 1.5, sbt_fg_alpha, DRAWFLAG_NORMAL);
900 pos.x -= hud_fontsize.x;
905 pos.y += 1.25 * hud_fontsize.y;
909 void Scoreboard_DrawItem(vector item_pos, vector rgb, entity pl, bool is_self, int pl_number)
911 TC(bool, is_self); TC(int, pl_number);
913 bool is_spec = (entcs_GetSpecState(pl.sv_entnum) == ENTCS_SPEC_PURE);
915 vector h_pos = item_pos;
916 vector h_size = vec2(panel_size.x, hud_fontsize.y * 1.25);
917 // alternated rows highlighting
919 drawfill(h_pos, h_size, rgb, sbt_highlight_alpha_self, DRAWFLAG_NORMAL);
920 else if((sbt_highlight) && (!(pl_number % 2)))
921 drawfill(h_pos, h_size, rgb, sbt_highlight_alpha, DRAWFLAG_NORMAL);
923 float fg_alpha = (is_self ? sbt_fg_alpha_self : sbt_fg_alpha);
925 vector pos = item_pos;
926 // put a "self indicator" beside the self row, unicode U+25C0 (black left-pointing triangle)
928 drawstring(pos+eX*(panel_size.x+.5*hud_fontsize.x)+eY, "\xE2\x97\x80", vec2(hud_fontsize.x, hud_fontsize.y), rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
930 pos.x += hud_fontsize.x * 0.5;
931 pos.y += (1.25 - 1) / 2 * hud_fontsize.y; // center text vertically
932 vector tmp = '0 0 0';
934 PlayerScoreField field;
935 for(i = 0; i < sbt_num_fields; ++i)
937 field = sbt_field[i];
938 if(field == SP_SEPARATOR)
941 if(is_spec && field != SP_NAME && field != SP_PING) {
942 pos.x += sbt_field_size[i] + hud_fontsize.x;
945 str = Scoreboard_GetField(pl, field);
946 str = Scoreboard_FixColumnWidth(i, str);
948 pos.x += sbt_field_size[i] + hud_fontsize.x;
950 if(field == SP_NAME) {
951 tmp.x = sbt_field_size[i] - hud_fontsize.x * sbt_fixcolumnwidth_iconlen - sbt_fixcolumnwidth_marginlen + hud_fontsize.x;
952 drawcolorcodedstring(pos - tmp, str, hud_fontsize, fg_alpha, DRAWFLAG_NORMAL);
954 tmp.x = sbt_fixcolumnwidth_len + hud_fontsize.x;
955 drawstring(pos - tmp, str, hud_fontsize, sbt_field_rgb, fg_alpha, DRAWFLAG_NORMAL);
958 tmp.x = sbt_field_size[i] + hud_fontsize.x;
959 if(sbt_field_icon0 != "")
960 drawpic(pos - tmp, sbt_field_icon0, vec2(hud_fontsize.x * sbt_fixcolumnwidth_iconlen, hud_fontsize.y), sbt_field_icon1_rgb, fg_alpha, DRAWFLAG_NORMAL);
961 if(sbt_field_icon1 != "")
962 drawpic(pos - tmp, sbt_field_icon1, vec2(hud_fontsize.x * sbt_fixcolumnwidth_iconlen, hud_fontsize.y), sbt_field_icon1_rgb, fg_alpha, DRAWFLAG_NORMAL);
963 if(sbt_field_icon2 != "")
964 drawpic(pos - tmp, sbt_field_icon2, vec2(hud_fontsize.x * sbt_fixcolumnwidth_iconlen, hud_fontsize.y), sbt_field_icon2_rgb, fg_alpha, DRAWFLAG_NORMAL);
967 if(sbt_field[i] == SP_SEPARATOR)
969 pos.x = item_pos.x + panel_size.x - hud_fontsize.x * 0.5;
970 for(i = sbt_num_fields-1; i > 0; --i)
972 field = sbt_field[i];
973 if(field == SP_SEPARATOR)
976 if(is_spec && field != SP_NAME && field != SP_PING) {
977 pos.x -= sbt_field_size[i] + hud_fontsize.x;
981 str = Scoreboard_GetField(pl, field);
982 str = Scoreboard_FixColumnWidth(i, str);
984 if(field == SP_NAME) {
985 tmp.x = sbt_fixcolumnwidth_len; // left or right aligned? let's put it right...
986 drawcolorcodedstring(pos - tmp, str, hud_fontsize, fg_alpha, DRAWFLAG_NORMAL);
988 tmp.x = sbt_fixcolumnwidth_len;
989 drawstring(pos - tmp, str, hud_fontsize, sbt_field_rgb, fg_alpha, DRAWFLAG_NORMAL);
992 tmp.x = sbt_field_size[i];
993 if(sbt_field_icon0 != "")
994 drawpic(pos - tmp, sbt_field_icon0, vec2(hud_fontsize.x * sbt_fixcolumnwidth_iconlen, hud_fontsize.y), sbt_field_icon1_rgb, fg_alpha, DRAWFLAG_NORMAL);
995 if(sbt_field_icon1 != "")
996 drawpic(pos - tmp, sbt_field_icon1, vec2(hud_fontsize.x * sbt_fixcolumnwidth_iconlen, hud_fontsize.y), sbt_field_icon1_rgb, fg_alpha, DRAWFLAG_NORMAL);
997 if(sbt_field_icon2 != "")
998 drawpic(pos - tmp, sbt_field_icon2, vec2(hud_fontsize.x * sbt_fixcolumnwidth_iconlen, hud_fontsize.y), sbt_field_icon2_rgb, fg_alpha, DRAWFLAG_NORMAL);
999 pos.x -= sbt_field_size[i] + hud_fontsize.x;
1004 drawfill(h_pos, h_size, '0 0 0', sbt_highlight_alpha_eliminated, DRAWFLAG_NORMAL);
1007 vector Scoreboard_DrawOthers(vector item_pos, vector rgb, int this_team, entity ignored_pl, entity pl, int pl_number)
1010 vector h_pos = item_pos;
1011 vector h_size = vec2(panel_size.x, hud_fontsize.y * 1.25);
1013 bool complete = (this_team == NUM_SPECTATOR);
1016 if((sbt_highlight) && (!(pl_number % 2)))
1017 drawfill(h_pos, h_size, rgb, sbt_highlight_alpha, DRAWFLAG_NORMAL);
1019 vector pos = item_pos;
1020 pos.x += hud_fontsize.x * 0.5;
1021 pos.y += (1.25 - 1) / 2 * hud_fontsize.y; // center text vertically
1023 float width_limit = item_pos.x + panel_size.x - hud_fontsize.x;
1025 width_limit -= stringwidth("...", false, hud_fontsize);
1026 float namesize = autocvar_hud_panel_scoreboard_namesize * hud_fontsize.x;
1027 static float max_name_width = 0;
1029 float fieldsize = 0;
1030 float min_fieldsize = 0;
1031 float fieldpadding = hud_fontsize.x * 0.25;
1032 if(this_team == NUM_SPECTATOR)
1034 if(autocvar_hud_panel_scoreboard_spectators_showping)
1035 min_fieldsize = stringwidth("999", false, hud_fontsize);
1037 else if(autocvar_hud_panel_scoreboard_others_showscore)
1038 min_fieldsize = stringwidth("99", false, hud_fontsize);
1039 for(i = 0; pl; pl = pl.sort_next)
1041 if(pl.team != this_team)
1043 if(pl == ignored_pl)
1047 if(this_team == NUM_SPECTATOR)
1049 if(autocvar_hud_panel_scoreboard_spectators_showping)
1050 field = Scoreboard_GetField(pl, SP_PING);
1052 else if(autocvar_hud_panel_scoreboard_others_showscore)
1053 field = Scoreboard_GetField(pl, SP_SCORE);
1055 string str = entcs_GetName(pl.sv_entnum);
1056 if (autocvar_hud_panel_scoreboard_playerid)
1057 str = Scoreboard_AddPlayerId(str, pl);
1058 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
1059 float column_width = stringwidth(str, true, hud_fontsize);
1060 if((this_team == NUM_SPECTATOR) && autocvar_hud_panel_scoreboard_spectators_aligned)
1062 if(column_width > max_name_width)
1063 max_name_width = column_width;
1064 column_width = max_name_width;
1068 fieldsize = stringwidth(field, false, hud_fontsize);
1069 column_width += hud_fontsize.x * 0.25 + max(fieldsize, min_fieldsize) + 2 * fieldpadding;
1072 if(pos.x + column_width > width_limit)
1077 drawstring(pos, "...", hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
1082 pos.x = item_pos.x + hud_fontsize.x * 0.5;
1083 pos.y += hud_fontsize.y * 1.25;
1087 vector name_pos = pos;
1088 if((this_team == NUM_SPECTATOR) && autocvar_hud_panel_scoreboard_spectators_aligned)
1089 name_pos.x += max(fieldsize, min_fieldsize) + 2 * fieldpadding + hud_fontsize.x * 0.25;
1090 drawcolorcodedstring(name_pos, str, hud_fontsize, sbt_fg_alpha, DRAWFLAG_NORMAL);
1093 h_size.x = max(fieldsize, min_fieldsize) + 2 * fieldpadding;
1094 h_size.y = hud_fontsize.y;
1095 vector field_pos = pos;
1096 if(!((this_team == NUM_SPECTATOR) && autocvar_hud_panel_scoreboard_spectators_aligned))
1097 field_pos.x += column_width - h_size.x;
1099 drawfill(field_pos, h_size, '1 1 1', sbt_highlight_alpha, DRAWFLAG_NORMAL);
1100 field_pos.x += fieldpadding + (max(fieldsize, min_fieldsize) - fieldsize) * 0.5;
1101 drawstring(field_pos, field, hud_fontsize, sbt_field_rgb, sbt_fg_alpha, DRAWFLAG_NORMAL);
1105 h_size.x = column_width + hud_fontsize.x * 0.25;
1106 h_size.y = hud_fontsize.y;
1107 drawfill(pos - hud_fontsize.x * 0.25 * eX, h_size, '0 0 0', sbt_highlight_alpha_eliminated, DRAWFLAG_NORMAL);
1109 pos.x += column_width;
1110 pos.x += hud_fontsize.x;
1112 return vec2(item_pos.x, item_pos.y + i * hud_fontsize.y * 1.25);
1115 vector Scoreboard_DrawMedal(vector pos, string icon, float height, float number)
1117 if(!number) return pos;
1118 total_medals += number;
1120 vector tmp_sz, tmp_sz2;
1121 tmp_sz = draw_getimagesize(icon);
1122 tmp_sz2 = vec2(height*(tmp_sz.x/tmp_sz.y), height);
1123 string val = ftos(number);
1125 drawpic(pos, icon, tmp_sz2, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1127 pos.x += tmp_sz2.x + hud_fontsize.x * 0.25;
1128 drawstring(pos + eY * ((tmp_sz2.y - hud_fontsize.y) / 2), val, hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1130 pos.x += stringwidth(val, false, hud_fontsize) + hud_fontsize.x * 0.5;
1134 vector Scoreboard_Duel_DrawPickup(vector pos, bool skinned, string icon, vector sz, float number, bool invert)
1136 vector tmp_in = pos;
1137 vector tmp_sz, tmp_sz2;
1142 picpath = strcat(hud_skin_path, "/", icon);
1143 if(precache_pic(picpath) == "")
1144 picpath = strcat("gfx/hud/default/", icon);
1149 tmp_sz = draw_getimagesize(picpath);
1150 tmp_sz2 = vec2(sz.y*(tmp_sz.x/tmp_sz.y), sz.y);
1152 tmp_in.x = pos.x + ((sz.x - tmp_sz2.x) / 2);
1153 drawpic(tmp_in, picpath, tmp_sz2, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1157 tmp_in.x += tmp_sz2.x + hud_fontsize.x * 0.25;
1159 tmp_in.x -= hud_fontsize.x * 0.25 + hud_fontsize.x;
1161 tmp_in.y += (tmp_sz2.y - hud_fontsize.y) / 2;
1163 ((number == -1) ? "?" : ftos(number)),
1164 hud_fontsize, ((number > 0) ? '1 1 1' : '0.5 0.5 0.5'),
1168 pos.y += sz.y * 1.1;
1172 void Scoreboard_Duel_DrawTable(vector pos, bool invert, entity pl, entity tm)
1174 vector tmp, tmp_in, tmp_sz, tmp_acc;
1177 float average_acc = 0;
1183 // Stop here if there are no scores available
1184 if(pl.team != tm.team) return;
1187 tmp.x += panel_bg_padding;
1188 tmp.y += panel_bg_padding;
1189 panel_size.x -= panel_bg_padding * 2;
1192 // drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", tmp, panel_size, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL);
1195 if(invert) { tmp.x += panel_size.x; tmp.x -= duel_score_size.x; }
1196 drawfill(tmp, duel_score_size, '0 0 0', sbt_highlight_alpha, DRAWFLAG_NORMAL);
1199 tmp_str = ftos(pl.(scores(SP_SCORE)));
1201 tmp_in.x += (duel_score_size.x / 2) - (stringwidth(tmp_str, true, duel_score_fontsize) / 2);
1202 tmp_in.y += (duel_score_size.y / 2) - (duel_score_fontsize.y / 2);
1204 draw_beginBoldFont();
1205 drawstring(tmp_in, tmp_str, duel_score_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1209 tmp_str = Scoreboard_GetField(pl, SP_NAME);
1212 tmp_in.x -= stringwidth_colors(tmp_str, duel_name_fontsize) + duel_name_fontsize.x * 0.5;
1214 tmp_in.x += duel_score_size.x + duel_name_fontsize.x * 0.5;
1215 tmp_in.y += (duel_score_size.y - duel_name_fontsize.y) / 2;
1216 drawcolorcodedstring(tmp_in, tmp_str, duel_name_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
1219 if(sbt_field_icon0 != "") {
1220 vector rsz = draw_getimagesize(sbt_field_icon0);
1221 sbt_fixcolumnwidth_iconlen = rsz.x / rsz.y;
1223 tmp_in.x -= hud_fontsize.x * sbt_fixcolumnwidth_iconlen + duel_name_fontsize.x * 0.5;
1225 tmp_in.x += stringwidth_colors(tmp_str, duel_name_fontsize) + duel_name_fontsize.x * 0.5;
1226 tmp_in.y += (duel_name_fontsize.y - hud_fontsize.y) / 2;
1227 drawpic(tmp_in, sbt_field_icon0, vec2(hud_fontsize.x * sbt_fixcolumnwidth_iconlen, hud_fontsize.y), sbt_field_icon1_rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
1231 float column_width = panel_size.x / 5;
1232 tmp.x = pos.x + panel_bg_padding;
1233 tmp.y += hud_fontsize.y * 3 + hud_fontsize.y;
1238 i = (invert ? 4 : 0);
1239 column_dim = vec2(column_width * 4, hud_fontsize.y);
1241 drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth("kills", false, hud_fontsize) / 2),
1242 "kills", hud_fontsize, '0.5 0.5 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1243 drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth("dmg", false, hud_fontsize) / 2),
1244 "dmg", hud_fontsize, '0.5 0.5 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1245 drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth("acc", false, hud_fontsize) / 2),
1246 "acc", hud_fontsize, '0.5 0.5 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1247 drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth("hits", false, hud_fontsize) / 2),
1248 "hits", hud_fontsize, '0.5 0.5 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1249 drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth("ping", false, hud_fontsize) / 2),
1250 "ping", hud_fontsize, '0.5 0.5 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1252 tmp.x = pos.x + panel_bg_padding;
1253 tmp.y += hud_fontsize.y;
1256 i = (invert ? 4 : 0);
1258 tmp_str = ftos(pl.(scores(SP_KILLS)));
1259 drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth(tmp_str, false, hud_fontsize * 1.25) / 2),
1260 tmp_str, hud_fontsize * 1.25, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1262 tmp_str = ftos(pl.(scores(SP_DMG)));
1263 drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth(tmp_str, false, hud_fontsize * 1.25) / 2),
1264 tmp_str, hud_fontsize * 1.25, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1266 tmp_acc = tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2);
1273 tmp_str = Scoreboard_GetField(pl, SP_PING);
1274 drawstring(tmp + eX * column_width * (invert ? i-- : i++) + (eX * column_width / 2) - eX * (stringwidth(tmp_str, false, hud_fontsize * 1.25) / 2),
1275 tmp_str, hud_fontsize * 1.25, sbt_field_rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
1277 tmp.x = pos.x + panel_bg_padding;
1278 tmp.y += hud_fontsize.y * 2;
1282 int total_weapons = 0;
1285 WepSet weapons_inmap = WepSet_GetFromStat_InMap();
1286 FOREACH(Weapons, it != WEP_Null, {
1287 WepSet set = it.m_wepset;
1288 if (!(weapons_inmap & set))
1290 if (it.spawnflags & WEP_TYPE_OTHER)
1293 int weapon_cnt_fired = pl.accuracy_cnt_fired[i - WEP_FIRST];
1294 int weapon_cnt_hit = pl.accuracy_cnt_hit[i - WEP_FIRST];
1296 if(weapon_cnt_fired)
1297 weapon_acc = floor((weapon_cnt_hit / weapon_cnt_fired) * 100);
1298 average_acc += weapon_acc;
1303 int c = (invert ? 4 : 0);
1305 drawfill(tmp_in + eX * column_width * (invert ? 1 : 0), column_dim, '0 0 0', sbt_highlight_alpha, DRAWFLAG_NORMAL);
1307 draw_str = ftos(pl.accuracy_frags[i - WEP_FIRST]);
1308 drawstring(tmp_in + eX * column_width * (invert ? c-- : c++) + eX * ((column_width - stringwidth(draw_str, false, hud_fontsize)) / 2),
1309 draw_str, hud_fontsize, (weapon_cnt_fired ? '1 1 1' : '0.5 0.5 0.5'), panel_fg_alpha, DRAWFLAG_NORMAL);
1311 draw_str = ftos(pl.accuracy_hit[i - WEP_FIRST]);
1312 drawstring(tmp_in + eX * column_width * (invert ? c-- : c++) + eX * ((column_width - stringwidth(draw_str, false, hud_fontsize)) / 2),
1313 draw_str, hud_fontsize, (weapon_cnt_fired ? '1 1 1' : '0.5 0.5 0.5'), panel_fg_alpha, DRAWFLAG_NORMAL);
1315 draw_str = sprintf("%d%%", weapon_acc);
1316 drawstring(tmp_in + eX * column_width * (invert ? c-- : c++) + eX * ((column_width - stringwidth(draw_str, false, hud_fontsize)) / 2),
1317 draw_str, hud_fontsize, (weapon_cnt_fired ? '1 1 1' : '0.5 0.5 0.5'), panel_fg_alpha, DRAWFLAG_NORMAL);
1319 draw_str = strcat(ftos(weapon_cnt_hit), " / ", ftos(weapon_cnt_fired));
1320 drawstring(tmp_in + eX * column_width * (invert ? c-- : c++) + eX * (column_width / 2) - eX * stringwidth("36 /", false, hud_fontsize),
1321 draw_str,hud_fontsize, (weapon_cnt_fired ? '1 1 1' : '0.5 0.5 0.5'), panel_fg_alpha, DRAWFLAG_NORMAL);
1325 tmp_in.x = pos.x + panel_size.x - panel_bg_padding - hud_fontsize.x / 2;
1326 drawpic_aspect_skin(tmp_in, it.model2, vec2(50, hud_fontsize.y), '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1329 tmp_in.x = pos.x + panel_bg_padding;
1330 tmp_in.y += hud_fontsize.y * 1.25;
1332 if(weapon_cnt_fired)
1336 average_acc = floor((average_acc / total_weapons) + 0.5);
1338 // draw total accuracy now
1339 tmp_str = sprintf("%d%%", average_acc);
1340 drawstring(tmp_acc - eX * (stringwidth(tmp_str, false, hud_fontsize * 1.25) / 2),
1341 tmp_str, hud_fontsize * 1.25, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1344 vector icon_sz = vec2(column_width, hud_fontsize.y*1.5);
1347 tmp.x += column_width * 4;
1349 drawstring(tmp + eX * ((column_width - stringwidth("medals", false, hud_fontsize)) / 2),
1350 "medals", hud_fontsize, '0.5 0.5 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1351 tmp.y += hud_fontsize.y * 1.25;
1353 tmp = Scoreboard_Duel_DrawPickup(tmp, false, "gfx/medal/humiliation", icon_sz, pl.(scores(SP_MEDAL_HUMILIATION)), invert);
1354 tmp = Scoreboard_Duel_DrawPickup(tmp, false, "gfx/medal/impressive", icon_sz, pl.(scores(SP_MEDAL_IMPRESSIVE)), invert);
1355 tmp = Scoreboard_Duel_DrawPickup(tmp, false, "gfx/medal/excellent", icon_sz, pl.(scores(SP_MEDAL_EXCELLENT)), invert);
1358 drawstring(tmp + eX * ((column_width - stringwidth("items", false, hud_fontsize)) / 2),
1359 "items", hud_fontsize, '0.5 0.5 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1360 tmp.y += hud_fontsize.y * 1.25;
1364 it.m_id == ITEM_ArmorMega.m_id ||
1365 it.m_id == ITEM_HealthMega.m_id ||
1366 it.m_id == ITEM_ArmorBig.m_id, {
1367 // If the match isn't over, Only show pickups if we're spectating or they're our own
1368 if(intermission || warmup_stage || spectatee_status || pl.sv_entnum == current_player)
1369 inv_num = inventoryslots[pl.sv_entnum].inv_items[it.m_id];
1370 tmp = Scoreboard_Duel_DrawPickup(tmp, true, it.m_icon, icon_sz, inv_num, invert);
1372 if(it.m_id == REGISTRY_MAX(Items))
1376 vector Scoreboard_MakeDuelTable(vector pos, entity tm, vector rgb, vector bg_size)
1378 vector end_pos = pos;
1379 float screen_half = panel_size.x / 2;
1380 float weapon_margin = hud_fontsize.x;
1382 panel_size.x = screen_half - weapon_margin;
1383 panel_size.y = (duel_score_size.y * 5.5);
1385 entity pl_left = players.sort_next;
1386 entity pl_right = pl_left.sort_next;
1388 Scoreboard_Duel_DrawTable(pos, true, pl_left, tm);
1389 Scoreboard_Duel_DrawTable(pos + eX * screen_half + eX * weapon_margin, false, pl_right, tm);
1391 end_pos.y += panel_size.y + (panel_bg_padding * 2);
1392 panel_size.x = screen_half * 2;
1396 vector Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
1398 int max_players = 999;
1399 if(autocvar_hud_panel_scoreboard_maxheight > 0)
1401 float height = autocvar_hud_panel_scoreboard_maxheight * vid_conheight;
1404 height -= (panel_bg_padding * 2 + hud_fontsize.y * 1.25) * team_count; // - padding and header
1405 height -= hud_fontsize.y * (team_count - 1); // - spacing between tables
1406 height /= team_count;
1409 height -= panel_bg_padding * 2; // - padding
1410 max_players = floor(height / (hud_fontsize.y * 1.25));
1411 if(max_players <= 1)
1413 if(max_players == tm.team_size)
1418 entity me = playerslots[current_player];
1420 panel_size.y = 1.25 * hud_fontsize.y * (1 + bound(1, tm.team_size, max_players));
1421 panel_size.y += panel_bg_padding * 2;
1424 vector end_pos = panel_pos + eY * (panel_size.y + 0.5* hud_fontsize.y);
1425 if(panel.current_panel_bg != "0")
1426 end_pos.y += panel_bg_border * 2;
1428 if(panel_bg_padding)
1430 panel_pos += '1 1 0' * panel_bg_padding;
1431 panel_size -= '2 2 0' * panel_bg_padding;
1435 vector tmp = vec2(panel_size.x, 1.25 * hud_fontsize.y);
1439 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', sbt_bg_alpha, DRAWFLAG_NORMAL);
1441 pos.y += 1.25 * hud_fontsize.y;
1444 tmp.y = panel_size.y - 1.25 * hud_fontsize.y;
1446 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL);
1449 // print header row and highlight columns
1450 pos = Scoreboard_DrawHeader(panel_pos, rgb, (max_players < tm.team_size), tm.team);
1452 // fill the table and draw the rows
1453 bool is_self = false;
1454 bool self_shown = false;
1457 if(Team_IsValidTeam(tm.team)) average_ping[Team_TeamToIndex(tm.team) - 1] = 0;
1458 for(pl = players.sort_next; pl; pl = pl.sort_next)
1460 if(pl.team != tm.team)
1462 if(i == max_players - 2 && pl != me)
1464 if(!self_shown && me.team == tm.team)
1466 Scoreboard_DrawItem(pos, rgb, me, true, i);
1468 pos.y += 1.25 * hud_fontsize.y;
1472 if(i >= max_players - 1)
1474 pos = Scoreboard_DrawOthers(pos, rgb, tm.team, (self_shown ? me : NULL), pl, i);
1477 is_self = (pl.sv_entnum == current_player);
1478 Scoreboard_DrawItem(pos, rgb, pl, is_self, i);
1480 if(Team_IsValidTeam(tm.team) && pl.ping) {
1481 average_ping[Team_TeamToIndex(tm.team) - 1] += pl.ping;
1486 pos.y += 1.25 * hud_fontsize.y;
1489 if(with_ping) average_ping[Team_TeamToIndex(tm.team) - 1] /= with_ping;
1491 panel_size.x += panel_bg_padding * 2; // restore initial width
1495 bool Scoreboard_WouldDraw()
1497 if (MUTATOR_CALLHOOK(DrawScoreboard))
1499 else if (QuickMenu_IsOpened())
1501 else if (HUD_Radar_Clickable())
1503 else if (scoreboard_showscores)
1505 else if (intermission == 1)
1507 else if (intermission == 2)
1509 else if (spectatee_status != -1 && STAT(HEALTH) <= 0 && autocvar_cl_deathscoreboard && !MUTATOR_CALLHOOK(DrawDeathScoreboard)
1510 && (!HUD_MinigameMenu_IsOpened() || !active_minigame))
1514 else if (scoreboard_showscores_force || MUTATOR_CALLHOOK(DrawScoreboard_Force))
1519 vector Scoreboard_MedalStats_Draw(vector pos)
1522 float height = hud_fontsize.y * 2;
1524 entity pl = playerslots[current_player];
1526 vector title_pos = pos;
1527 pos.x += 0.5 * hud_fontsize.x + panel_bg_padding;
1528 pos.y += 1.25 * hud_fontsize.y;
1532 pos = Scoreboard_DrawMedal(pos, "gfx/medal/airshot", height, pl.(scores(SP_MEDAL_AIRSHOT)));
1533 pos = Scoreboard_DrawMedal(pos, "gfx/medal/damage", height, pl.(scores(SP_MEDAL_DAMAGE)));
1534 pos = Scoreboard_DrawMedal(pos, "gfx/medal/electrobitch", height, pl.(scores(SP_MEDAL_ELECTROBITCH)));
1535 pos = Scoreboard_DrawMedal(pos, "gfx/medal/excellent", height, pl.(scores(SP_MEDAL_EXCELLENT)));
1536 pos = Scoreboard_DrawMedal(pos, "gfx/medal/firstblood", height, pl.(scores(SP_MEDAL_FIRSTBLOOD)));
1537 pos = Scoreboard_DrawMedal(pos, "gfx/medal/headshot", height, pl.(scores(SP_MEDAL_HEADSHOT)));
1538 pos = Scoreboard_DrawMedal(pos, "gfx/medal/humiliation", height, pl.(scores(SP_MEDAL_HUMILIATION)));
1539 pos = Scoreboard_DrawMedal(pos, "gfx/medal/impressive", height, pl.(scores(SP_MEDAL_IMPRESSIVE)));
1540 pos = Scoreboard_DrawMedal(pos, "gfx/medal/yoda", height, pl.(scores(SP_MEDAL_YODA)));
1541 pos = Scoreboard_DrawMedal(pos, "gfx/medal/telefrag", height, pl.(scores(SP_MEDAL_TELEFRAG)));
1544 pos.x += hud_fontsize.x;
1546 pos = Scoreboard_DrawMedal(pos, "gfx/medal/accuracy", height, pl.(scores(SP_MEDAL_ACCURACY)));
1547 pos = Scoreboard_DrawMedal(pos, "gfx/medal/assist", height, pl.(scores(SP_MEDAL_ASSIST)));
1548 pos = Scoreboard_DrawMedal(pos, "gfx/medal/capture", height, pl.(scores(SP_MEDAL_CAPTURE)));
1549 pos = Scoreboard_DrawMedal(pos, "gfx/medal/defense", height, pl.(scores(SP_MEDAL_DEFENSE)));
1550 pos = Scoreboard_DrawMedal(pos, "gfx/medal/perfect", height, pl.(scores(SP_MEDAL_PERFECT)));
1552 if(!total_medals) return orig;
1554 drawstring(title_pos, sprintf(_("Medal stats (total %d)"), total_medals),
1555 hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1558 pos.y += height + hud_fontsize.y * 0.5;
1562 float average_accuracy;
1563 vector Scoreboard_AccuracyStats_Draw(vector pos, vector rgb, vector bg_size)
1567 if (scoreboard_fade_alpha == 1)
1568 scoreboard_acc_fade_alpha = min(1, scoreboard_acc_fade_alpha + frametime * 10);
1570 scoreboard_acc_fade_alpha = 1; // sync fading with the scoreboard
1572 vector initial_pos = pos;
1574 WepSet weapons_stat = WepSet_GetFromStat();
1575 WepSet weapons_inmap = WepSet_GetFromStat_InMap();
1576 int disownedcnt = 0;
1578 FOREACH(Weapons, it != WEP_Null, {
1579 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1581 WepSet set = it.m_wepset;
1582 if(it.spawnflags & WEP_TYPE_OTHER)
1587 if (weapon_stats < 0 && !((weapons_stat & set) || (weapons_inmap & set)))
1589 if (it.spawnflags & (WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_SPECIALATTACK))
1596 int weapon_cnt = (REGISTRY_COUNT(Weapons) - 1) - disownedcnt - nHidden;
1597 if (weapon_cnt <= 0) return pos;
1600 if (autocvar_hud_panel_scoreboard_accuracy_doublerows && weapon_cnt >= floor((REGISTRY_COUNT(Weapons) - nHidden - 1) * 0.5))
1602 int columnns = ceil(weapon_cnt / rows);
1604 float weapon_height = 29;
1605 float height = hud_fontsize.y + weapon_height;
1607 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);
1608 pos.y += 1.25 * hud_fontsize.y;
1609 if(panel.current_panel_bg != "0")
1610 pos.y += panel_bg_border;
1613 panel_size.y = height * rows;
1614 panel_size.y += panel_bg_padding * 2;
1616 float panel_bg_alpha_save = panel_bg_alpha;
1617 panel_bg_alpha *= scoreboard_acc_fade_alpha;
1619 panel_bg_alpha = panel_bg_alpha_save;
1621 vector end_pos = panel_pos + eY * (panel_size.y + hud_fontsize.y);
1622 if(panel.current_panel_bg != "0")
1623 end_pos.y += panel_bg_border * 2;
1625 if(panel_bg_padding)
1627 panel_pos += '1 1 0' * panel_bg_padding;
1628 panel_size -= '2 2 0' * panel_bg_padding;
1632 vector tmp = panel_size;
1634 float weapon_width = tmp.x / columnns / rows;
1637 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, sbt_bg_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL);
1641 // column highlighting
1642 for (int i = 0; i < columnns; ++i)
1644 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);
1647 for (int i = 0; i < rows; ++i)
1648 drawfill(pos + eY * (weapon_height + height * i), vec2(tmp.x, hud_fontsize.y), rgb, sbt_highlight_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL);
1651 average_accuracy = 0;
1652 int weapons_with_stats = 0;
1654 pos.x += weapon_width / 2;
1656 if (autocvar_hud_panel_scoreboard_accuracy_nocolors)
1659 Accuracy_LoadColors();
1661 float oldposx = pos.x;
1665 FOREACH(Weapons, it != WEP_Null, {
1666 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1668 WepSet set = it.m_wepset;
1669 if (weapon_stats < 0 && !((weapons_stat & set) || (weapons_inmap & set)))
1671 if (it.spawnflags & WEP_TYPE_OTHER)
1675 if (weapon_stats >= 0)
1676 weapon_alpha = sbt_fg_alpha;
1678 weapon_alpha = 0.2 * sbt_fg_alpha;
1681 drawpic_aspect_skin(tmpos, it.model2, vec2(weapon_width, weapon_height), '1 1 1', weapon_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL);
1683 if (weapon_stats >= 0) {
1684 weapons_with_stats += 1;
1685 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1688 s = sprintf("%d%%", weapon_stats * 100);
1691 padding = (weapon_width - stringwidth(s, false, hud_fontsize)) / 2; // center the accuracy value
1693 if(!autocvar_hud_panel_scoreboard_accuracy_nocolors)
1694 rgb = Accuracy_GetColor(weapon_stats);
1696 drawstring(tmpos + vec2(padding, weapon_height), s, hud_fontsize, rgb, sbt_fg_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL);
1698 tmpos.x += weapon_width * rows;
1699 pos.x += weapon_width * rows;
1700 if (rows == 2 && column == columnns - 1) {
1708 if (weapons_with_stats)
1709 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1711 panel_size.x += panel_bg_padding * 2; // restore initial width
1713 if (scoreboard_acc_fade_alpha == 1)
1715 return initial_pos + (end_pos - initial_pos) * scoreboard_acc_fade_alpha;
1718 vector MapStats_DrawKeyValue(vector pos, string key, string value) {
1720 pos.x += hud_fontsize.x * 0.25;
1721 drawstring(pos, key, hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
1722 pos.x = panel_pos.x + panel_size.x - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
1723 drawstring(pos, value, hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
1725 pos.y += hud_fontsize.y;
1731 vector Scoreboard_MapStats_Draw(vector pos, vector rgb, vector bg_size) {
1732 float stat_secrets_found, stat_secrets_total;
1733 float stat_monsters_killed, stat_monsters_total;
1737 // get monster stats
1738 stat_monsters_killed = STAT(MONSTERS_KILLED);
1739 stat_monsters_total = STAT(MONSTERS_TOTAL);
1741 // get secrets stats
1742 stat_secrets_found = STAT(SECRETS_FOUND);
1743 stat_secrets_total = STAT(SECRETS_TOTAL);
1745 // get number of rows
1746 if(stat_secrets_total)
1748 if(stat_monsters_total)
1751 // if no rows, return
1755 // draw table header
1756 drawstring(pos + eX * panel_bg_padding, _("Map stats:"), hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1757 pos.y += 1.25 * hud_fontsize.y;
1758 if(panel.current_panel_bg != "0")
1759 pos.y += panel_bg_border;
1762 panel_size.y = hud_fontsize.y * rows;
1763 panel_size.y += panel_bg_padding * 2;
1766 vector end_pos = panel_pos + eY * (panel_size.y + hud_fontsize.y);
1767 if(panel.current_panel_bg != "0")
1768 end_pos.y += panel_bg_border * 2;
1770 if(panel_bg_padding)
1772 panel_pos += '1 1 0' * panel_bg_padding;
1773 panel_size -= '2 2 0' * panel_bg_padding;
1777 vector tmp = panel_size;
1780 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL);
1783 if(stat_monsters_total)
1785 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
1786 pos = MapStats_DrawKeyValue(pos, _("Monsters killed:"), val);
1790 if(stat_secrets_total)
1792 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
1793 pos = MapStats_DrawKeyValue(pos, _("Secrets found:"), val);
1796 panel_size.x += panel_bg_padding * 2; // restore initial width
1801 vector Scoreboard_Rankings_Draw(vector pos, string ranktitle, entity pl, vector rgb, vector bg_size)
1804 RANKINGS_RECEIVED_CNT = 0;
1805 for (i=RANKINGS_CNT-1; i>=0; --i)
1807 ++RANKINGS_RECEIVED_CNT;
1809 if (RANKINGS_RECEIVED_CNT == 0)
1812 vector hl_rgb = rgb + '0.5 0.5 0.5';
1814 pos.y += hud_fontsize.y;
1815 drawstring(pos + eX * panel_bg_padding, ranktitle, hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
1816 pos.y += 1.25 * hud_fontsize.y;
1817 if(panel.current_panel_bg != "0")
1818 pos.y += panel_bg_border;
1823 for(i = 0; i < RANKINGS_RECEIVED_CNT; ++i)
1825 float f = stringwidth(ColorTranslateRGB(grecordholder[i]), true, hud_fontsize);
1830 if(namesize > autocvar_hud_panel_scoreboard_namesize * hud_fontsize.x)
1832 namesize = autocvar_hud_panel_scoreboard_namesize * hud_fontsize.x;
1836 float ranksize = 3 * hud_fontsize.x;
1837 float timesize = 5 * hud_fontsize.x;
1838 vector columnsize = vec2(ranksize + timesize + namesize + hud_fontsize.x, 1.25 * hud_fontsize.y);
1839 int columns = max(1, floor((panel_size.x - 2 * panel_bg_padding) / columnsize.x));
1840 columns = min(columns, RANKINGS_RECEIVED_CNT);
1842 // expand name column to fill the entire row
1843 float available_space = (panel_size.x - 2 * panel_bg_padding - columnsize.x * columns) / columns;
1844 namesize += available_space;
1845 columnsize.x += available_space;
1847 panel_size.y = ceil(RANKINGS_RECEIVED_CNT / columns) * 1.25 * hud_fontsize.y;
1848 panel_size.y += panel_bg_padding * 2;
1852 vector end_pos = panel_pos + eY * (panel_size.y + hud_fontsize.y);
1853 if(panel.current_panel_bg != "0")
1854 end_pos.y += panel_bg_border * 2;
1856 if(panel_bg_padding)
1858 panel_pos += '1 1 0' * panel_bg_padding;
1859 panel_size -= '2 2 0' * panel_bg_padding;
1865 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, panel_size, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL);
1867 vector text_ofs = vec2(0.5 * hud_fontsize.x, (1.25 - 1) / 2 * hud_fontsize.y); // center text vertically
1869 int column = 0, j = 0;
1870 string zoned_name_self = strzone(strdecolorize(entcs_GetName(player_localnum)));
1871 for(i = 0; i < RANKINGS_RECEIVED_CNT; ++i)
1878 if(strdecolorize(grecordholder[i]) == zoned_name_self)
1879 drawfill(pos, columnsize, hl_rgb, sbt_highlight_alpha_self, DRAWFLAG_NORMAL);
1880 else if(!((j + column) & 1) && sbt_highlight)
1881 drawfill(pos, columnsize, hl_rgb, sbt_highlight_alpha, DRAWFLAG_NORMAL);
1883 str = count_ordinal(i+1);
1884 drawstring(pos + text_ofs, str, hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
1885 drawstring(pos + text_ofs + eX * ranksize, TIME_ENCODED_TOSTRING(t), hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
1886 str = ColorTranslateRGB(grecordholder[i]);
1888 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
1889 drawcolorcodedstring(pos + text_ofs + eX * (ranksize + timesize), str, hud_fontsize, sbt_fg_alpha, DRAWFLAG_NORMAL);
1891 pos.y += 1.25 * hud_fontsize.y;
1893 if(j >= ceil(RANKINGS_RECEIVED_CNT / columns))
1897 pos.x += panel_size.x / columns;
1898 pos.y = panel_pos.y;
1901 strfree(zoned_name_self);
1903 panel_size.x += panel_bg_padding * 2; // restore initial width
1907 float scoreboard_time;
1908 bool have_weapon_stats;
1909 bool Scoreboard_AccuracyStats_WouldDraw(float ypos)
1911 if (MUTATOR_CALLHOOK(DrawScoreboardAccuracy))
1913 if (!autocvar_hud_panel_scoreboard_accuracy || warmup_stage || ypos > 0.91 * vid_conheight)
1916 if (time < scoreboard_time + autocvar_hud_panel_scoreboard_accuracy_showdelay
1917 && ypos > autocvar_hud_panel_scoreboard_accuracy_showdelay_minpos * vid_conheight
1923 if (!have_weapon_stats)
1925 FOREACH(Weapons, it != WEP_Null, {
1926 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1927 if (weapon_stats >= 0)
1929 have_weapon_stats = true;
1933 if (!have_weapon_stats)
1940 void Scoreboard_Draw()
1942 if(!autocvar__hud_configure)
1944 if(!hud_draw_maximized) return;
1946 // frametime checks allow to toggle the scoreboard even when the game is paused
1947 if(scoreboard_active) {
1948 if (scoreboard_fade_alpha < 1)
1949 scoreboard_time = time;
1950 if(hud_configure_menu_open == 1)
1951 scoreboard_fade_alpha = 1;
1952 float scoreboard_fadeinspeed = autocvar_hud_panel_scoreboard_fadeinspeed;
1953 if (scoreboard_fadeinspeed && frametime)
1954 scoreboard_fade_alpha = min(1, scoreboard_fade_alpha + frametime * scoreboard_fadeinspeed);
1956 scoreboard_fade_alpha = 1;
1957 if(hud_fontsize_str != autocvar_hud_fontsize)
1959 hud_fontsize = HUD_GetFontsize("hud_fontsize");
1960 Scoreboard_initFieldSizes();
1961 strcpy(hud_fontsize_str, autocvar_hud_fontsize);
1965 float scoreboard_fadeoutspeed = autocvar_hud_panel_scoreboard_fadeoutspeed;
1966 if (scoreboard_fadeoutspeed && frametime)
1967 scoreboard_fade_alpha = max(0, scoreboard_fade_alpha - frametime * scoreboard_fadeoutspeed);
1969 scoreboard_fade_alpha = 0;
1972 if (!scoreboard_fade_alpha)
1974 scoreboard_acc_fade_alpha = 0;
1979 scoreboard_fade_alpha = 0;
1981 if (autocvar_hud_panel_scoreboard_dynamichud)
1984 HUD_Scale_Disable();
1986 if(scoreboard_fade_alpha <= 0)
1988 panel_fade_alpha *= scoreboard_fade_alpha;
1989 HUD_Panel_LoadCvars();
1991 sbt_bg_alpha = autocvar_hud_panel_scoreboard_table_bg_alpha * panel_fg_alpha;
1992 sbt_highlight = autocvar_hud_panel_scoreboard_table_highlight;
1993 sbt_highlight_alpha = autocvar_hud_panel_scoreboard_table_highlight_alpha * panel_fg_alpha;
1994 sbt_highlight_alpha_self = autocvar_hud_panel_scoreboard_table_highlight_alpha_self * panel_fg_alpha;
1995 sbt_highlight_alpha_eliminated = autocvar_hud_panel_scoreboard_table_highlight_alpha_eliminated * panel_fg_alpha;
1996 sbt_fg_alpha = autocvar_hud_panel_scoreboard_table_fg_alpha * panel_fg_alpha;
1997 sbt_fg_alpha_self = autocvar_hud_panel_scoreboard_table_fg_alpha_self * panel_fg_alpha;
1999 // don't overlap with con_notify
2000 if(!autocvar__hud_configure)
2001 panel_pos.y = max((autocvar_con_notify * autocvar_con_notifysize), panel_pos.y);
2003 float excess = max(0, max_namesize - autocvar_hud_panel_scoreboard_namesize * hud_fontsize.x);
2004 float fixed_scoreboard_width = bound(vid_conwidth * autocvar_hud_panel_scoreboard_minwidth, vid_conwidth - excess, vid_conwidth * 0.93);
2005 panel_pos.x = 0.5 * (vid_conwidth - fixed_scoreboard_width);
2006 panel_size.x = fixed_scoreboard_width;
2008 Scoreboard_UpdatePlayerTeams();
2010 vector pos = panel_pos;
2015 vector sb_gameinfo_type_fontsize, sb_gameinfo_detail_fontsize;
2017 // Begin of Game Info Section
2018 sb_gameinfo_type_fontsize = hud_fontsize * 2.5;
2019 sb_gameinfo_detail_fontsize = hud_fontsize * 1.3;
2022 //drawcolorcodedstring(pos, "bienvenidoainternet.org", sb_gameinfo_type_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
2023 //drawpic_aspect(pos + '1 0 0' * (panel_size.x - 150), "gfx/bai_logo", vec2(150, sb_gameinfo_type_fontsize.y), '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
2024 //pos.y += sb_gameinfo_type_fontsize.y;
2026 // Game Info: Game Type
2027 str = MapInfo_Type_ToText(gametype);
2029 draw_beginBoldFont();
2030 //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);
2031 drawcolorcodedstring(pos, str, sb_gameinfo_type_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
2034 vector tmp_old_sz = draw_getimagesize("gfx/bai_logo");
2035 float tmp_aspect = tmp_old_sz.x/tmp_old_sz.y;
2036 vector tmp_new_sz = vec2(sb_gameinfo_type_fontsize.y * tmp_aspect, sb_gameinfo_type_fontsize.y);
2038 drawpic(pos + '1 0 0' * (panel_size.x - tmp_new_sz.x), "gfx/bai_logo", tmp_new_sz, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
2040 pos.y += sb_gameinfo_type_fontsize.y;
2043 drawcolorcodedstring(pos + '0.5 0 0' * (panel_size.x - stringwidth_colors(hostname_full, sb_gameinfo_detail_fontsize)), hostname_full, sb_gameinfo_detail_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
2045 pos.y += sb_gameinfo_detail_fontsize.y;
2047 // Game Info: Game Detail
2048 float tl = STAT(TIMELIMIT);
2049 float fl = STAT(FRAGLIMIT);
2050 float ll = STAT(LEADLIMIT);
2051 float ll_and_fl = STAT(LEADLIMIT_AND_FRAGLIMIT);
2054 str = strcat(str, sprintf(_("^3%1.0f minutes"), tl));
2055 if(!gametype.m_hidelimits)
2060 str = strcat(str, "^7 / "); // delimiter
2063 str = strcat(str, sprintf(_("^5%s %s"), ScoreString(teamscores_flags(ts_primary), fl),
2064 (teamscores_label(ts_primary) == "score") ? CTX(_("SCO^points")) :
2065 (teamscores_label(ts_primary) == "fastest") ? "" :
2066 TranslateScoresLabel(teamscores_label(ts_primary))));
2070 str = strcat(str, sprintf(_("^5%s %s"), ScoreString(scores_flags(ps_primary), fl),
2071 (scores_label(ps_primary) == "score") ? CTX(_("SCO^points")) :
2072 (scores_label(ps_primary) == "fastest") ? "" :
2073 TranslateScoresLabel(scores_label(ps_primary))));
2078 if(tl > 0 || fl > 0)
2081 if (ll_and_fl && fl > 0)
2082 str = strcat(str, "^7 & ");
2084 str = strcat(str, "^7 / ");
2089 str = strcat(str, sprintf(_("^2+%s %s"), ScoreString(teamscores_flags(ts_primary), ll),
2090 (teamscores_label(ts_primary) == "score") ? CTX(_("SCO^points")) :
2091 (teamscores_label(ts_primary) == "fastest") ? "" :
2092 TranslateScoresLabel(teamscores_label(ts_primary))));
2096 str = strcat(str, sprintf(_("^2+%s %s"), ScoreString(scores_flags(ps_primary), ll),
2097 (scores_label(ps_primary) == "score") ? CTX(_("SCO^points")) :
2098 (scores_label(ps_primary) == "fastest") ? "" :
2099 TranslateScoresLabel(scores_label(ps_primary))));
2104 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
2106 str = sprintf(_("^7Map: ^2%s"), shortmapname);
2107 drawcolorcodedstring(pos, str, sb_gameinfo_detail_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL); // align left
2108 // End of Game Info Section
2110 pos.y += sb_gameinfo_detail_fontsize.y + hud_fontsize.y * 0.3; // space between Game Info Section and score table
2111 if(panel.current_panel_bg != "0")
2112 pos.y += panel_bg_border;
2114 // Draw the scoreboard
2115 float scale = autocvar_hud_panel_scoreboard_table_bg_scale;
2118 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * scale;
2122 vector panel_bg_color_save = panel_bg_color;
2123 vector team_score_baseoffset;
2124 vector team_size_baseoffset;
2125 if (autocvar_hud_panel_scoreboard_team_size_position != 1) // team size not on left
2127 // put team score to the left of scoreboard (and team size to the right)
2128 team_score_baseoffset = eY * hud_fontsize.y - eX * hud_fontsize.x * 1.5;
2129 team_size_baseoffset = eY * hud_fontsize.y + eX * hud_fontsize.x * 0.5;
2130 if(panel.current_panel_bg != "0")
2132 team_score_baseoffset.x -= panel_bg_border;
2133 team_size_baseoffset.x += panel_bg_border;
2138 // put team score to the right of scoreboard (and team size to the left)
2139 team_score_baseoffset = eY * hud_fontsize.y + eX * hud_fontsize.x * 1.5;
2140 team_size_baseoffset = eY * hud_fontsize.y - eX * hud_fontsize.x * 0.5;
2141 if(panel.current_panel_bg != "0")
2143 team_score_baseoffset.x += panel_bg_border;
2144 team_size_baseoffset.x -= panel_bg_border;
2148 int team_size_total = 0;
2149 if (autocvar_hud_panel_scoreboard_team_size_position != 0) // team size not off
2151 // calculate team size total (sum of all team sizes)
2152 for(tm = teams.sort_next; tm; tm = tm.sort_next)
2153 if(tm.team != NUM_SPECTATOR)
2154 team_size_total += tm.team_size;
2157 for(tm = teams.sort_next; tm; tm = tm.sort_next)
2159 if(tm.team == NUM_SPECTATOR)
2164 vector rgb = Team_ColorRGB(tm.team);
2165 /*draw_beginBoldFont();
2166 str = ftos(tm.(teamscores(ts_primary)));
2167 if (autocvar_hud_panel_scoreboard_team_size_position != 1) // team size not on left
2169 // team score on the left (default)
2170 str_pos = pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 3);
2174 // team score on the right
2175 str_pos = pos + team_score_baseoffset + eX * (panel_size.x + hud_fontsize.x * 3);
2177 drawstring(str_pos, str, hud_fontsize * 3, rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
2179 // team size (if set to show on the side)
2180 if (autocvar_hud_panel_scoreboard_team_size_position != 0) // team size not off
2182 // calculate the starting position for the whole team size info string
2183 str = sprintf("%d/%d", tm.team_size, team_size_total);
2184 if (autocvar_hud_panel_scoreboard_team_size_position == 1)
2186 // team size on the left
2187 str_pos = pos + team_size_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5);
2191 // team size on the right
2192 str_pos = pos + team_size_baseoffset + eX * (panel_size.x + hud_fontsize.x * 1.5);
2194 str = sprintf("%d", tm.team_size);
2195 drawstring(str_pos, str, hud_fontsize * 1.5, rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
2196 str_pos += eX * stringwidth(str, true, hud_fontsize * 1.5) + eY * hud_fontsize.y * .5;
2197 str = sprintf("/%d", team_size_total);
2198 drawstring(str_pos, str, hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
2202 // secondary score, e.g. keyhunt
2203 if(ts_primary != ts_secondary)
2205 str = ftos(tm.(teamscores(ts_secondary)));
2206 if (autocvar_hud_panel_scoreboard_team_size_position != 1) // team size not on left
2209 str_pos = pos + team_score_baseoffset - vec2(stringwidth(str, false, hud_fontsize), hud_fontsize.y * -1.5);
2214 str_pos = pos + team_score_baseoffset + vec2(panel_size.x + hud_fontsize.x * 1.5, hud_fontsize.y * 1.5);
2217 drawstring(str_pos, str, hud_fontsize, rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
2222 // z411 My team header
2224 drawfill(pos, team_score_size, rgb * 0.5, sbt_highlight_alpha, DRAWFLAG_NORMAL);
2227 str = ftos(tm.(teamscores(ts_primary)));
2229 str_pos.x += (team_score_size.x / 2) - (stringwidth(str, true, team_score_fontsize) / 2);
2230 str_pos.y += (team_score_size.y / 2) - (team_score_fontsize.y / 2);
2232 draw_beginBoldFont();
2233 drawstring(str_pos, str, team_score_fontsize, rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
2237 str = Team_CustomName(tm.team);
2239 str_pos.x += team_score_size.x + team_name_fontsize.x * 0.5;
2240 str_pos.y += (team_score_size.y / 2) - (team_name_fontsize.y / 2);
2241 drawcolorcodedstring(str_pos, str, team_name_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
2243 pos.y += team_score_size.y + (hud_fontsize.y * 0.5);
2245 if(autocvar_hud_panel_scoreboard_bg_teams_color_team > 0)
2246 panel_bg_color = rgb * autocvar_hud_panel_scoreboard_bg_teams_color_team;
2247 else if(panel_bg_color_team > 0)
2248 panel_bg_color = rgb * panel_bg_color_team;
2250 panel_bg_color = rgb;
2251 pos = Scoreboard_MakeTable(pos, tm, panel_bg_color, bg_size);
2253 panel_bg_color = panel_bg_color_save;
2255 else if(gametype == MAPINFO_TYPE_DUEL)
2257 for(tm = teams.sort_next; tm; tm = tm.sort_next)
2258 if(tm.team != NUM_SPECTATOR)
2261 // z411 make DUEL TABLE
2262 pos = Scoreboard_MakeDuelTable(pos, tm, panel_bg_color, bg_size);
2266 for(tm = teams.sort_next; tm; tm = tm.sort_next)
2267 if(tm.team != NUM_SPECTATOR)
2270 // display it anyway
2271 pos = Scoreboard_MakeTable(pos, tm, panel_bg_color, bg_size);
2274 pos = Scoreboard_MedalStats_Draw(pos);
2276 if (Scoreboard_AccuracyStats_WouldDraw(pos.y))
2277 pos = Scoreboard_AccuracyStats_Draw(pos, panel_bg_color, bg_size);
2279 if(MUTATOR_CALLHOOK(ShowRankings)) {
2280 string ranktitle = M_ARGV(0, string);
2281 if(race_speedaward) {
2282 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);
2283 pos.y += 1.25 * hud_fontsize.y;
2285 if(race_speedaward_alltimebest) {
2286 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);
2287 pos.y += 1.25 * hud_fontsize.y;
2289 pos = Scoreboard_Rankings_Draw(pos, ranktitle, playerslots[player_localnum], panel_bg_color, bg_size);
2292 //pos = Scoreboard_MapStats_Draw(pos, panel_bg_color, bg_size);
2295 for(pl = players.sort_next; pl; pl = pl.sort_next)
2297 if(pl.team == NUM_SPECTATOR)
2299 for(tm = teams.sort_next; tm; tm = tm.sort_next)
2300 if(tm.team == NUM_SPECTATOR)
2302 str = sprintf("%s (%d)", _("Spectators"), tm.team_size);
2303 draw_beginBoldFont();
2304 drawstring(pos, str, hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
2306 pos.y += 1.25 * hud_fontsize.y;
2308 pos = Scoreboard_DrawOthers(pos, '0 0 0', pl.team, NULL, pl, 0);
2309 pos.y += 1.25 * hud_fontsize.y;
2316 // print information about respawn status
2317 float respawn_time = STAT(RESPAWN_TIME);
2321 if(respawn_time < 0)
2323 // a negative number means we are awaiting respawn, time value is still the same
2324 respawn_time *= -1; // remove mark now that we checked it
2326 if(respawn_time < time) // it happens for a few frames when server is respawning the player
2327 str = ""; // draw an empty string to not change suddenly scoreboard_bottom
2329 str = sprintf(_("^1Respawning in ^3%s^1..."),
2330 (autocvar_hud_panel_scoreboard_respawntime_decimals ?
2331 count_seconds_decs(respawn_time - time, autocvar_hud_panel_scoreboard_respawntime_decimals)
2333 count_seconds(ceil(respawn_time - time))
2337 else if(time < respawn_time)
2339 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
2340 (autocvar_hud_panel_scoreboard_respawntime_decimals ?
2341 count_seconds_decs(respawn_time - time, autocvar_hud_panel_scoreboard_respawntime_decimals)
2343 count_seconds(ceil(respawn_time - time))
2347 else if(time >= respawn_time)
2348 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump"));
2350 pos.y += 1.2 * hud_fontsize.y;
2351 drawcolorcodedstring(pos + '0.5 0 0' * (panel_size.x - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
2354 scoreboard_bottom = pos.y + 2 * hud_fontsize.y;