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