Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
scoreboard.qc
Go to the documentation of this file.
1#include "scoreboard.qh"
2
4#include <client/draw.qh>
10#include <common/constants.qh>
11#include <common/ent_cs.qh>
12#include <common/mapinfo.qh>
14#include <common/net_linked.qh>
15#include <common/scores.qh>
16#include <common/stats.qh>
17#include <common/teams.qh>
19
20// Scoreboard (#24)
21
23{
24 // allow saving cvars that aesthetically change the panel into hud skin files
25 HUD_Write_Cvar("hud_panel_scoreboard_fadeinspeed");
26 HUD_Write_Cvar("hud_panel_scoreboard_fadeoutspeed");
27 HUD_Write_Cvar("hud_panel_scoreboard_respawntime_decimals");
28 HUD_Write_Cvar("hud_panel_scoreboard_table_bg_alpha");
29 HUD_Write_Cvar("hud_panel_scoreboard_table_bg_scale");
30 HUD_Write_Cvar("hud_panel_scoreboard_table_fg_alpha");
31 HUD_Write_Cvar("hud_panel_scoreboard_table_fg_alpha_self");
32 HUD_Write_Cvar("hud_panel_scoreboard_table_highlight");
33 HUD_Write_Cvar("hud_panel_scoreboard_table_highlight_alpha");
34 HUD_Write_Cvar("hud_panel_scoreboard_table_highlight_alpha_self");
35 HUD_Write_Cvar("hud_panel_scoreboard_table_highlight_alpha_eliminated");
36 HUD_Write_Cvar("hud_panel_scoreboard_bg_teams_color_team");
37 HUD_Write_Cvar("hud_panel_scoreboard_accuracy_doublerows");
38 HUD_Write_Cvar("hud_panel_scoreboard_accuracy_nocolors");
39 HUD_Write_Cvar("hud_panel_scoreboard_spectators_position");
40}
41
43
52
57
65
81
87
92float autocvar_hud_panel_scoreboard_itemstats_showdelay = 2.2; // slightly more delayed than accuracy
94
96
106
108
109SHUTDOWN(scoreboard)
110{
112 cvar_set("hud_panel_scoreboard_scores_per_round", "0");
113}
114
115// mode 0: returns translated label
116// mode 1: prints name and description of all the labels
117string Label_getInfo(string label, int mode)
118{
119 if (mode == 1)
120 label = "bckills"; // first case in the switch
121
122#define SCO_LABEL(strlabel, label, padding, help) \
123 case label: \
124 if (!mode) \
125 return CTX(strlabel); \
126 LOG_HELP("^3", label, padding, "^7", help);
127
128 switch (label)
129 {
130 SCO_LABEL(_("SCO^bckills"), "bckills", " ", _("Number of ball carrier kills"));
131 SCO_LABEL(_("SCO^bctime"), "bctime", " ", _("Total amount of time holding the ball in Keepaway"));
132 SCO_LABEL(_("SCO^caps"), "caps", " ", _("How often a flag (CTF) or a key (Key Hunt) was captured"));
133 SCO_LABEL(_("SCO^captime"), "captime", " ", _("Time of fastest capture (CTF)"));
134 SCO_LABEL(_("SCO^deaths"), "deaths", " ", _("Number of deaths"));
135 SCO_LABEL(_("SCO^destructions"), "destructions", " ", _("Number of keys destroyed by pushing them into void"));
136 SCO_LABEL(_("SCO^damage dealt"), "dmg", " ", _("The total damage dealt"));
137 SCO_LABEL(_("SCO^damage taken"), "dmgtaken", " ", _("The total damage taken"));
138 SCO_LABEL(_("SCO^drops"), "drops", " ", _("Number of flag drops"));
139 SCO_LABEL(_("SCO^fastest"), "fastest", " ", _("Time of fastest lap (Race/CTS)"));
140 SCO_LABEL(_("SCO^faults"), "faults", " ", _("Number of faults committed"));
141 SCO_LABEL(_("SCO^fckills"), "fckills", " ", _("Number of flag carrier kills"));
142 SCO_LABEL(_("SCO^fps"), "fps", " ", _("FPS"));
143 SCO_LABEL(_("SCO^frags"), "frags", " ", _("Number of kills minus suicides"));
144 SCO_LABEL(_("SCO^generators"), "generators", " ", _("Number of generators destroyed"));
145 SCO_LABEL(_("SCO^goals"), "goals", " ", _("Number of goals scored"));
146 SCO_LABEL(_("SCO^hunts"), "hunts", " ", _("Number of hunts (Survival)"));
147 SCO_LABEL(_("SCO^kckills"), "kckills", " ", _("Number of key carrier kills"));
148 SCO_LABEL(_("SCO^k/d"), "kd", " ", _("The kill-death ratio"));
149 SCO_LABEL(_("SCO^kdr"), "kdr", " ", _("The kill-death ratio"));
150 SCO_LABEL(_("SCO^kdratio"), "kdratio", " ", _("The kill-death ratio"));
151 SCO_LABEL(_("SCO^kills"), "kills", " ", _("Number of kills"));
152 SCO_LABEL(_("SCO^laps"), "laps", " ", _("Number of laps finished (Race/CTS)"));
153 SCO_LABEL(_("SCO^lives"), "lives", " ", _("Number of lives (LMS)"));
154 SCO_LABEL(_("SCO^losses"), "losses", " ", _("Number of times a key was lost"));
155 SCO_LABEL(_("SCO^name"), "name", " ", _("Player name"));
156 SCO_LABEL(_("SCO^nick"), "nick", " ", _("Player name"));
157 SCO_LABEL(_("SCO^objectives"), "objectives", " ", _("Number of objectives destroyed"));
158 SCO_LABEL(_("SCO^pickups"), "pickups", " ", _("How often a flag (CTF), a key (Key Hunt), or a ball (Keepaway) was picked up"));
159 SCO_LABEL(_("SCO^ping"), "ping", " ", _("Ping time"));
160 SCO_LABEL(_("SCO^pl"), "pl", " ", _("Packet loss"));
161 SCO_LABEL(_("SCO^pushes"), "pushes", " ", _("Number of players pushed into void"));
162 SCO_LABEL(_("SCO^rank"), "rank", " ", _("Player rank"));
163 SCO_LABEL(_("SCO^returns"), "returns", " ", _("Number of flag returns"));
164 SCO_LABEL(_("SCO^revivals"), "revivals", " ", _("Number of revivals"));
165 SCO_LABEL(_("SCO^rounds won"), "rounds", " ", _("Number of rounds won"));
166 SCO_LABEL(_("SCO^rounds played"), "rounds_pl", " ", _("Number of rounds played"));
167 SCO_LABEL(_("SCO^score"), "score", " ", _("Total score"));
168 SCO_LABEL(_("SCO^skill"), "skill", " ", _("Player skill"));
169 SCO_LABEL(_("SCO^suicides"), "suicides", " ", _("Number of suicides"));
170 SCO_LABEL(_("SCO^sum"), "sum", " ", _("Number of kills minus deaths"));
171 SCO_LABEL(_("SCO^survivals"), "survivals", " ", _("Number of survivals"));
172 SCO_LABEL(_("SCO^takes"), "takes", " ", _("Number of domination points taken (Domination)"));
173 SCO_LABEL(_("SCO^teamkills"), "teamkills", " ", _("Number of teamkills"));
174 SCO_LABEL(_("SCO^ticks"), "ticks", " ", _("Number of ticks (Domination)"));
175 SCO_LABEL(_("SCO^time"), "time", " ", _("Total time raced (Race/CTS)"));
176 }
177 return label;
178#undef SCO_LABEL
179}
180
183{
185 sb_showscores = false;
186}
187
196
197// mode: 0 normal, 1 team selection
199{
200 if (isdemo())
201 return;
202
203 if (mode == 1)
204 {
206 return;
207
208 // release player's pressed keys as they aren't released elsewhere
209 // in particular jump needs to be released as it may open the team selection
210 // (when server detects jump has been pressed it sends the command to open the team selection)
214 }
215 else
216 {
217 if (scoreboard_ui_enabled == 1)
218 return;
221 }
225}
226
231float HUD_Scoreboard_InputEvent(float bInputType, float nPrimary, float nSecondary)
232{
234 return false;
235
236 if (bInputType == 3)
237 {
238 mousepos.x = nPrimary;
239 mousepos.y = nSecondary;
240 return true;
241 }
242
243 if (bInputType == 2)
244 return false;
245
246 // at this point bInputType can only be 0 or 1 (key pressed or released)
247 bool key_pressed = bInputType == 0;
248
249 // ESC to exit (TAB-ESC works too)
250 if (nPrimary == K_ESCAPE)
251 {
252 if (!key_pressed)
253 return true;
255 return true;
256 }
257
258 // block any input while a menu dialog is fading
260 {
261 hudShiftState = 0;
262 return true;
263 }
264
265 // allow console bind to work
266 string con_keys = findkeysforcommand("toggleconsole", 0);
267 int keys = tokenize(con_keys); // findkeysforcommand returns data for this
268
269 bool hit_con_bind = false;
270 for (int i = 0; i < keys; ++i)
271 if (nPrimary == stof(argv(i)))
272 hit_con_bind = true;
273
274 if (key_pressed)
275 {
276 if (nPrimary == K_ALT) hudShiftState |= S_ALT;
277 if (nPrimary == K_CTRL) hudShiftState |= S_CTRL;
278 if (nPrimary == K_SHIFT) hudShiftState |= S_SHIFT;
279 if (nPrimary == K_TAB) hudShiftState |= S_TAB;
280 }
281 else
282 {
283 if (nPrimary == K_ALT) hudShiftState &= ~S_ALT;
284 if (nPrimary == K_CTRL) hudShiftState &= ~S_CTRL;
285 if (nPrimary == K_SHIFT) hudShiftState &= ~S_SHIFT;
286 if (nPrimary == K_TAB) hudShiftState &= ~S_TAB;
287 }
288
289 if (nPrimary == K_TAB)
290 {
291 if (!key_pressed)
292 return true;
293 if (scoreboard_ui_enabled == 2)
294 {
296 goto uparrow_action;
297 else
298 goto downarrow_action;
299 }
300
302 {
308 }
309 else
310 {
316 }
317
319 }
320 else if (nPrimary == K_DOWNARROW)
321 {
322 if (!key_pressed)
323 return true;
324 LABEL(downarrow_action);
326 {
327 if (scoreboard_ui_enabled == 2)
328 {
329 bool scoreboard_selected_team_found = !scoreboard_selected_team;
330
331 entity curr_team = NULL;
332 for (entity tm = teams.sort_next; tm; tm = tm.sort_next)
333 {
334 if (tm.team == NUM_SPECTATOR)
335 continue;
336 curr_team = tm;
337 if (scoreboard_selected_team_found)
338 goto ok_team;
339 if (scoreboard_selected_team == tm)
340 scoreboard_selected_team_found = true;
341 }
342 LABEL(ok_team);
343 if (curr_team == scoreboard_selected_team) // loop reached the last team
344 curr_team = NULL;
345 scoreboard_selected_team = curr_team;
346 }
347 else
348 {
349 bool scoreboard_selected_player_found = !scoreboard_selected_player;
350
351 entity curr_pl = NULL;
352 for (entity pl, tm = teams.sort_next; tm; tm = tm.sort_next)
353 if (tm.team != NUM_SPECTATOR)
354 for (pl = players.sort_next; pl; pl = pl.sort_next)
355 {
356 if (pl.team != tm.team)
357 continue;
358 curr_pl = pl;
359 if (scoreboard_selected_player_found)
360 goto ok_done;
362 scoreboard_selected_player_found = true;
363 }
364 LABEL(ok_done);
365 if (curr_pl == scoreboard_selected_player) // loop reached the last player
366 curr_pl = NULL;
368 }
369 }
370 }
371 else if (nPrimary == K_UPARROW)
372 {
373 if (!key_pressed)
374 return true;
375 LABEL(uparrow_action);
377 {
378 if (scoreboard_ui_enabled == 2)
379 {
381 for (entity tm = teams.sort_next; tm; tm = tm.sort_next)
382 {
383 if (tm.team == NUM_SPECTATOR)
384 continue;
385 if (tm == scoreboard_selected_team)
386 goto ok_team2;
387 prev_team = tm;
388 }
389 LABEL(ok_team2);
391 }
392 else
393 {
394 entity prev_pl = NULL;
395 for (entity pl, tm = teams.sort_next; tm; tm = tm.sort_next)
396 if (tm.team != NUM_SPECTATOR)
397 for (pl = players.sort_next; pl; pl = pl.sort_next)
398 {
399 if (pl.team != tm.team)
400 continue;
402 goto ok_done2;
403 prev_pl = pl;
404 }
405 LABEL(ok_done2);
407 }
408 }
409 }
410 else if (nPrimary == K_RIGHTARROW)
411 {
412 if (!key_pressed)
413 return true;
416 }
417 else if (nPrimary == K_LEFTARROW)
418 {
419 if (!key_pressed)
420 return true;
423 }
424 else if (nPrimary == K_ENTER || nPrimary == K_SPACE || nPrimary == K_KP_ENTER)
425 {
426 if (!key_pressed)
427 return true;
429 {
430 if (scoreboard_ui_enabled == 2)
431 {
432 string team_name;
434 team_name = "auto";
435 else
437 localcmd(sprintf("cmd selectteam %s; cmd join\n", team_name));
439 }
441 localcmd(sprintf("spectate %d\n", scoreboard_selected_player.sv_entnum + 1));
442 }
443 }
444 else if (nPrimary == 'c' && (hudShiftState & S_CTRL))
445 {
446 if (!key_pressed)
447 return true;
449 {
451 {
452 case 0:
454 {
455 localcmd(sprintf("scoreboard_columns_set\n")); // sets the layout saved in scoreboard_columns
457 break;
458 }
459 // fallthrough
460 case 1:
461 localcmd(sprintf("scoreboard_columns_set default\n"));
463 break;
464 case 2:
465 localcmd(sprintf("scoreboard_columns_set all\n"));
467 break;
468 }
469 }
470 }
471 else if (nPrimary == 'r' && (hudShiftState & S_CTRL))
472 {
473 if (!key_pressed)
474 return true;
476 localcmd("toggle hud_panel_scoreboard_scores_per_round\n");
477 }
478 else if (nPrimary == 't' && (hudShiftState & S_CTRL))
479 {
480 if (!key_pressed)
481 return true;
484 {
485 localcmd(sprintf("commandmode tell \"%s^7\"\n", entcs_GetName(scoreboard_selected_player.sv_entnum)));
487 }
488 }
489 else if (nPrimary == 'k' && (hudShiftState & S_CTRL))
490 {
491 if (!key_pressed)
492 return true;
495 {
496 localcmd(sprintf("vcall kick \"%s^7\"\n", entcs_GetName(scoreboard_selected_player.sv_entnum)));
497 }
498 }
499 else if (hit_con_bind || nPrimary == K_PAUSE)
500 return false;
501
502 return true;
503}
504
509string TranslateScoresLabel(string label)
510{
511 return Label_getInfo(label, 0);
512}
513
515{
518
519 int f;
520 FOREACH(Scores, !(scores_flags(it) & SFL_NOT_SORTABLE),
521 {
523 if (f == SFL_SORT_PRIO_PRIMARY)
524 ps_primary = it;
526 ps_secondary = it;
527 });
528 if (ps_secondary == NULL)
530
531 for (int i = 0; i < MAX_TEAMSCORE; ++i)
532 {
534 if (f == SFL_SORT_PRIO_PRIMARY)
535 ts_primary = i;
537 ts_secondary = i;
538 }
539 if (ts_secondary == -1)
541
543}
544
545//float lastpnum;
547{
548 static float update_time;
549 if (time <= update_time)
550 return;
551 update_time = time;
552
553 numplayers = 0;
554 //int num = 0;
555 for (entity tmp, pl = players.sort_next; pl; pl = pl.sort_next)
556 {
557 numplayers += pl.team != NUM_SPECTATOR;
558 //++num;
559 int Team = entcs_GetScoreTeam(pl.sv_entnum);
560 if (SetTeam(pl, Team))
561 {
562 tmp = pl.sort_prev;
564 if (tmp)
565 pl = tmp;
566 else
567 pl = players.sort_next;
568 }
569 }
570 /*
571 if (num != lastpnum)
572 print(strcat("PNUM: ", ftos(num), "\n"));
573 lastpnum = num;
574 */
575
576 // update the smallest and largest team sizes
577 if (!teamplay || !teams.sort_next)
578 ts_min = ts_max = 0;
579 else
580 {
581 ts_min = 255, ts_max = 0;
582
583 for (entity tm = teams.sort_next; tm; tm = tm.sort_next)
584 {
585 if (tm.team == NUM_SPECTATOR)
586 continue;
587
588 if (ts_min > tm.team_size)
589 ts_min = tm.team_size;
590 if (ts_max < tm.team_size)
591 ts_max = tm.team_size;
592 }
593 }
594}
595
596int Scoreboard_CompareScore(int vl, int vr, int f)
597{
598 TC(int, vl); TC(int, vr); TC(int, f);
599 if (f & SFL_ZERO_IS_WORST)
600 {
601 if (vl == 0 && vr != 0)
602 return 1;
603 if (vl != 0 && vr == 0)
604 return 0;
605 }
606 if (vl > vr)
607 return IS_INCREASING(f);
608 if (vl < vr)
609 return IS_DECREASING(f);
610 return -1;
611}
612
614{
615 int vl = (left.gotscores) ? entcs_GetTeam(left.sv_entnum) : NUM_SPECTATOR;
616 int vr = (right.gotscores) ? entcs_GetTeam(right.sv_entnum) : NUM_SPECTATOR;
617
618 if (vl > vr)
619 return true;
620 if (vl < vr)
621 return false;
622
623 if (vl == NUM_SPECTATOR)
624 {
625 // FIRST the one with scores (spectators), THEN the ones without (downloaders)
626 // no other sorting
627 if (!left.gotscores && right.gotscores)
628 return true;
629 return false;
630 }
631
632 int res = Scoreboard_CompareScore(left.scores(ps_primary), right.scores(ps_primary), scores_flags(ps_primary));
633 if (res >= 0)
634 return res;
635
637 {
639 if (res >= 0)
640 return res;
641 }
642
643 FOREACH(Scores, (it != ps_primary && it != ps_secondary),
644 {
645 res = Scoreboard_CompareScore(left.scores(it), right.scores(it), scores_flags(it));
646 if (res >= 0)
647 return res;
648 });
649
650 if (left.sv_entnum < right.sv_entnum)
651 return true;
652
653 return false;
654}
655
657{
658 entity ent;
659 for (ent = player.sort_next; ent && Scoreboard_ComparePlayerScores(player, ent); ent = player.sort_next)
660 SORT_SWAP(player, ent);
661 for (ent = player.sort_prev; ent != players && Scoreboard_ComparePlayerScores(ent, player); ent = player.sort_prev)
662 SORT_SWAP(ent, player);
663}
664
666{
667 if (left.team == NUM_SPECTATOR)
668 return 1;
669 if (right.team == NUM_SPECTATOR)
670 return 0;
671
672 int fld_idx = -1;
673 for (int r, i = -2; i < MAX_TEAMSCORE; ++i)
674 {
675 if (i < 0)
676 {
677 if (fld_idx == -1)
678 fld_idx = ts_primary;
679 else if (ts_secondary == ts_primary)
680 continue;
681 else
682 fld_idx = ts_secondary;
683 }
684 else
685 {
686 fld_idx = i;
687 if (fld_idx == ts_primary || fld_idx == ts_secondary)
688 continue;
689 }
690
691 r = Scoreboard_CompareScore(left.teamscores(fld_idx), right.teamscores(fld_idx), teamscores_flags(fld_idx));
692 if (r >= 0)
693 return r;
694 }
695
696 if (left.team < right.team)
697 return true;
698
699 return false;
700}
701
703{
704 entity ent;
705 for (ent = Team.sort_next; ent && Scoreboard_CompareTeamScores(Team, ent); ent = Team.sort_next)
706 SORT_SWAP(Team, ent);
707 for (ent = Team.sort_prev; ent != teams && Scoreboard_CompareTeamScores(ent, Team); ent = Team.sort_prev)
708 SORT_SWAP(ent, Team);
709}
710
712{
713 LOG_HELP(_("You can modify the scoreboard using the ^2scoreboard_columns_set command."));
714 LOG_HELP(_("Usage:"));
715 LOG_HELP("^2scoreboard_columns_set ^3default");
716 LOG_HELP(_("^2scoreboard_columns_set ^3field1 field2 ..."));
717 LOG_HELP(_("^2scoreboard_columns_set ^7without arguments reads the arguments from the cvar scoreboard_columns"));
718 LOG_HELP(_(" ^5Note: ^7scoreboard_columns_set without arguments is executed on every map start"));
719 LOG_HELP(_("^2scoreboard_columns_set ^3expand_default ^7loads default layout and expands it into the cvar scoreboard_columns so you can edit it"));
720 LOG_HELP(_("You can use a ^3|^7 to start the right-aligned fields."));
721 LOG_HELP(_("The following field names are recognized (case insensitive):"));
722 LOG_HELP("");
723
725 LOG_HELP("");
726
727 LOG_HELP(_("Before a field you can put a + or - sign, then a comma separated list\n"
728 "of gametypes, then a slash, to make the field show up only in these\n"
729 "or in all but these gametypes. You can also specify 'all' as a\n"
730 "field to show all fields available for the current gametype."));
731 LOG_HELP("");
732
733 LOG_HELP(_("The special gametype names 'teams' and 'noteams' can be used to\n"
734 "include/exclude ALL teams/noteams gametypes."));
735 LOG_HELP("");
736
737 LOG_HELP(_("Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4"));
738 LOG_HELP(_("will display name, ping and pl aligned to the left, and the fields\n"
739 "right of the vertical bar aligned to the right."));
740 LOG_HELP(_("'field3' will only be shown in CTF, and 'field4' will be shown in all\n"
741 "other gametypes except DM."));
742}
743
744// NOTE: adding a gametype with ? to not warn for an optional field
745// make sure it's excluded in a previous exclusive rule, if any
746// otherwise the previous exclusive rule warns anyway
747// e.g. -teams,rc,cts,lms/kills ?+rc/kills
748#define SCOREBOARD_DEFAULT_COLUMNS \
749"ping pl fps skill name |" \
750" -teams,rc,cts,surv,inv,lms/kills +ft,tdm,tmayhem/kills ?+rc,inv/kills" \
751" -teams,surv,lms/deaths +ft,tdm,tmayhem/deaths" \
752" +tdm/sum" \
753" -teams,lms,rc,cts,surv,inv,ka/suicides +ft,tdm,tmayhem/suicides ?+rc,inv/suicides" \
754" -cts,dm,tdm,surv,ka,ft,mayhem,tmayhem/frags" /* tdm already has this in "score" */ \
755" +tdm,ft,dom,ons,as,tmayhem/teamkills"\
756" -rc,cts,surv,nb/dmg -rc,cts,surv,nb/dmgtaken" \
757" +surv/survivals +surv/hunts" \
758" +ctf/pickups +ctf/fckills +ctf/returns +ctf/caps +ons/takes +ons/caps" \
759" +lms/lives +lms/rank" \
760" +kh/kckills +kh/losses +kh/caps" \
761" ?+rc/laps ?+rc/time +rc,cts/fastest" \
762" +as/objectives +nb/faults +nb/goals" \
763" +ka,tka/pickups +ka,tka/bckills +ka,tka/bctime +ft/revivals" \
764" +dom/ticks +dom/takes" \
765" -lms,rc,cts,inv,nb/score"
766
768{
769 TC(int, argc);
770 if (!gametype)
771 return; // do nothing, we don't know gametype and scores yet
772
773 int i;
774 bool have_name = false, have_primary = false, have_secondary = false, have_separator = false;
775
776 // sbt_fields uses strunzone on the titles!
777 if (!sbt_field_title[0])
778 for (i = 0; i < MAX_SBT_FIELDS; ++i)
779 sbt_field_title[i] = strzone("(null)");
780
781 // TODO: re enable with gametype dependant cvars?
782 if (argc < 3) // no arguments provided
784
785 if (argc < 3)
787
788 if (argc == 3)
789 {
790 if (argv(2) == "default" || argv(2) == "expand_default")
791 {
792 if (argv(2) == "expand_default")
793 cvar_set("scoreboard_columns", SCOREBOARD_DEFAULT_COLUMNS);
795 }
796 else if (argv(2) == "all" || argv(2) == "ALL")
797 {
798 string s = "ping pl name |"; // scores without label (not really scores)
799 if (argv(2) == "ALL")
800 {
801 // scores without label
802 s = strcat(s, " ", "sum");
803 s = strcat(s, " ", "kdratio");
804 s = strcat(s, " ", "frags");
805 }
806 FOREACH(Scores, true,
807 {
808 if (it != ps_primary && it != ps_secondary && scores_label(it) != "")
809 s = strcat(s, " ", scores_label(it));
810 });
812 s = strcat(s, " ", scores_label(ps_secondary));
813 s = strcat(s, " ", scores_label(ps_primary));
814 argc = tokenizebyseparator(strcat("0 1 ", s), " ");
815 }
816 }
817
818
819 sbt_num_fields = 0;
820
821 hud_fontsize = HUD_GetFontsize("hud_fontsize");
822
823 string str, pattern;
824 int slash;
825 bool nocomplain;
826 for (i = 1; i < argc - 1; ++i)
827 {
828 str = argv(i+1);
829 nocomplain = false;
830 if (substring(str, 0, 1) == "?")
831 {
832 nocomplain = true;
833 str = substring(str, 1, strlen(str) - 1);
834 }
835
836 slash = strstrofs(str, "/", 0);
837 if (slash >= 0)
838 {
839 pattern = substring(str, 0, slash);
840 str = substring(str, slash + 1, strlen(str) - (slash + 1));
841
842 if (!isGametypeInFilter(gametype, teamplay, false, pattern))
843 continue;
844 }
845
846 str = strtolower(str);
848
850 switch (str)
851 {
852 // fields without a label (not networked via the score system)
853 case "ping": sbt_field[sbt_num_fields] = SP_PING; break;
854 case "pl": sbt_field[sbt_num_fields] = SP_PL; break;
855 case "name":
856 case "nick": sbt_field[sbt_num_fields] = SP_NAME; have_name = true; break;
857 case "|": sbt_field[sbt_num_fields] = SP_SEPARATOR; have_separator = true; break;
858 case "kd":
859 case "kdr":
860 case "kdratio": sbt_field[sbt_num_fields] = SP_KDRATIO; break;
861 case "sum":
862 case "diff":
863 case "k-d": sbt_field[sbt_num_fields] = SP_SUM; break;
864 case "frags": sbt_field[sbt_num_fields] = SP_FRAGS; break;
865 default: // fields with a label
866 {
867 // map alternative labels
868 if (str == "damage")
869 str = "dmg";
870 if (str == "damagetaken")
871 str = "dmgtaken";
872
873 FOREACH(Scores, str == strtolower(scores_label(it)),
874 {
875 j = it;
876 goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
877 });
878
879 // NOTE: can't check STAT(SHOWFPS) here, if checked too early it returns false anyway
880 if (!nocomplain && str != "fps" && str != "skill") // server can disable the fps or skill fields
881 LOG_INFOF("^1Error:^7 Unknown score field: '%s'", str);
882
884 continue;
885
886 LABEL(found)
888 if (j == ps_primary)
889 have_primary = true;
890 if (j == ps_secondary)
891 have_secondary = true;
892
893 }
894 }
897 break;
898 }
899
901 have_primary = true;
903 have_secondary = true;
905 have_secondary = true;
906 int missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
907
908 if (sbt_num_fields + missing < MAX_SBT_FIELDS)
909 {
910 if (!have_name)
911 {
913 for (i = sbt_num_fields; i > 0; --i)
914 {
916 sbt_field[i] = sbt_field[i-1];
917 }
919 sbt_field[0] = SP_NAME;
921 LOG_INFO("fixed missing field 'name'");
922
923 if (!have_separator)
924 {
926 for (i = sbt_num_fields; i > 1; --i)
927 {
929 sbt_field[i] = sbt_field[i-1];
930 }
931 sbt_field_title[1] = strzone("|");
932 sbt_field[1] = SP_SEPARATOR;
934 LOG_INFO("fixed missing field '|'");
935 }
936 }
937 else if (!have_separator)
938 {
940 sbt_field[sbt_num_fields] = SP_SEPARATOR;
942 LOG_INFO("fixed missing field '|'");
943 }
944 if (!have_secondary)
945 {
949 LOG_INFOF("fixed missing field '%s'", scores_label(ps_secondary));
950 }
951 if (!have_primary)
952 {
956 LOG_INFOF("fixed missing field '%s'", scores_label(ps_primary));
957 }
958 }
959
960 sbt_field[sbt_num_fields] = SP_END;
962}
963
964string Scoreboard_AddPlayerId(string pl_name, entity pl)
965{
968}
969
970// MOVEUP::
972// these are drawn overlapping
975
977// these are drawn side-by-side, next to playercolor (if present)
978// 0: player_ready, 1: player_handicap, 2: player_ignored
982{
983 if (!teamplay)
984 {
985 // NOTE: always adding 1024 allows saving the colormap 0 as a value != 0
986 int f = (playerslots[pl.sv_entnum].colormap >= 1024)
987 ? playerslots[pl.sv_entnum].colormap - 1024 // override server-side player colors
988 : entcs_GetClientColors(pl.sv_entnum);
989
990 sbt_field_icon_playercolor[0] = "gfx/scoreboard/playercolor_base";
991 sbt_field_icon_playercolor[1] = "gfx/scoreboard/playercolor_shirt";
992 sbt_field_icon_playercolor[2] = "gfx/scoreboard/playercolor_pants";
996 }
997 if (ready_waiting && pl.ready)
998 {
999 sbt_field_icon_extra[0] = "gfx/scoreboard/player_ready";
1000 sbt_field_icon_extra_rgb[0].y = 1;
1002 }
1003 int handicap_lvl = entcs_GetHandicapLevel(pl.sv_entnum);
1004 if (handicap_lvl != 0)
1005 {
1006 sbt_field_icon_extra[1] = "gfx/scoreboard/player_handicap";
1007 sbt_field_icon_extra_rgb[1] = '1 0 0' + '0 1 1' * ((16 - handicap_lvl) / 15);
1008 // goes from white to red over level 1 to 16
1009 }
1010
1011 if (pl.ignored)
1012 sbt_field_icon_extra[2] = "gfx/scoreboard/player_ignored";
1013
1014 return entcs_GetName(pl.sv_entnum);
1015}
1016
1023#define PING_LOW autocvar_hud_panel_scoreboard_ping_low
1024#define PING_MED autocvar_hud_panel_scoreboard_ping_medium
1025#define PING_HIGH autocvar_hud_panel_scoreboard_ping_high
1026#define COLOR_LOW autocvar_hud_panel_scoreboard_ping_low_color
1027#define COLOR_MED autocvar_hud_panel_scoreboard_ping_medium_color
1028#define COLOR_HIGH autocvar_hud_panel_scoreboard_ping_high_color
1029string Scoreboard_GetField(entity pl, PlayerScoreField field, bool per_round)
1030{
1031 float tmp, num, denom;
1032 int f;
1033 string str;
1034 sbt_field_rgb = '1 1 1';
1038 sbt_field_icon_playercolor_rgb[0] = '1 1 1';
1039 sbt_field_icon_playercolor_rgb[1] = '1 1 1';
1040 sbt_field_icon_playercolor_rgb[2] = '1 1 1';
1041 for (f = 0; f < SBT_FIELD_ICON_EXTRA_COUNT; ++f)
1042 {
1043 sbt_field_icon_extra[f] = "";
1044 sbt_field_icon_extra_rgb[f] = '1 1 1';
1045 }
1046
1047 int rounds_played = 0;
1048 if (per_round)
1049 rounds_played = pl.(scores(SP_ROUNDS_PL));
1050 switch (field)
1051 {
1052 case SP_PING:
1053 if (!pl.gotscores)
1054 return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6 (Black Right-Pointing Triangle)
1055 //str = getplayerkeyvalue(pl.sv_entnum, "ping");
1056 f = pl.ping;
1057 if (f == 0)
1058 return _("N/A");
1059 // this colour selection code should match menuqc XonoticServerList_drawListBoxItem()
1060 if (f < PING_LOW)
1062 else if (f < PING_MED)
1064 else if (f < PING_HIGH)
1066 else
1068 return ftos(f);
1069
1070 case SP_PL:
1071 if (!pl.gotscores)
1072 return _("N/A");
1073 f = pl.ping_packetloss;
1074 tmp = pl.ping_movementloss;
1075 if (f == 0 && tmp == 0)
1076 return "";
1077 str = ftos(ceil(f * 100));
1078 if (tmp != 0)
1079 str = strcat(str, "~", ftos(ceil(tmp * 100)));
1080 tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
1081 sbt_field_rgb = '1 0.5 0.5' - '0 0.5 0.5' * tmp;
1082 return str;
1083
1084 case SP_NAME:
1085 str = Scoreboard_GetName(pl);
1087 str = Scoreboard_AddPlayerId(str, pl);
1088 return str;
1089
1090 case SP_FRAGS:
1091 f = pl.(scores(SP_KILLS)) - pl.(scores(SP_SUICIDES));
1092 if (rounds_played)
1093 return sprintf("%.1f", f / rounds_played);
1094 return ftos(f);
1095
1096 case SP_KDRATIO:
1097 num = pl.(scores(SP_KILLS));
1098 denom = pl.(scores(SP_DEATHS));
1099
1100 if (denom == 0)
1101 {
1102 sbt_field_rgb = '0 1 0';
1103 if (rounds_played)
1104 str = sprintf("%.1f", num / rounds_played);
1105 else
1106 str = sprintf("%d", num);
1107 }
1108 else if (num <= 0)
1109 {
1110 sbt_field_rgb = '1 0 0';
1111 if (rounds_played)
1112 str = sprintf("%.2f", num / (denom * rounds_played));
1113 else
1114 str = sprintf("%.1f", num / denom);
1115 }
1116 else
1117 {
1118 if (rounds_played)
1119 str = sprintf("%.2f", num / (denom * rounds_played));
1120 else
1121 str = sprintf("%.1f", num / denom);
1122 }
1123 return str;
1124
1125 case SP_SUM:
1126 f = pl.(scores(SP_KILLS)) - pl.(scores(SP_DEATHS));
1127
1128 if (f > 0)
1129 sbt_field_rgb = '0 1 0';
1130 else if (f == 0)
1131 sbt_field_rgb = '1 1 1';
1132 else
1133 sbt_field_rgb = '1 0 0';
1134 if (rounds_played)
1135 return sprintf("%.1f", f / rounds_played);
1136 return ftos(f);
1137
1138 case SP_SKILL:
1139 tmp = pl.(scores(SP_SKILL));
1140 switch (tmp)
1141 {
1142 case -1: return "...";
1143 case -2: return _("N/A");
1144 default: return ftos(tmp);
1145 }
1146
1147 case SP_FPS:
1148 tmp = pl.(scores(SP_FPS));
1149 if (tmp == 0)
1150 {
1151 sbt_field_rgb = '1 1 1';
1152 return ((pl.ping == 0) ? _("N/A") : "..."); // if 0 ping, either connecting or bot (either case can't show proper score)
1153 }
1154 // <=32fps full red, 64fps to 96fps (inclusive) full yellow, >=128fps white
1155 // A little opinionated but representative of what's adequate for a competitive experience,
1156 // with consideration to the default and supported ticrates and the minimum USB polling rate.
1157 sbt_field_rgb.x = 1;
1158 sbt_field_rgb.y = bound(0, (tmp - 32) * 0.03125, 1); // 0 at 32fps, 1 at 64fps
1159 sbt_field_rgb.z = bound(0, (tmp - 96) * 0.03125, 1); // 0 at 96fps, 1 at 128fps
1160 return ftos(tmp);
1161
1162 case SP_ROUNDS_PL:
1163 return ftos(pl.(scores(field)));
1164
1165 case SP_DMG: case SP_DMGTAKEN:
1166 if (rounds_played)
1167 return sprintf("%.2f k", pl.(scores(field)) / (1000 * rounds_played));
1168 return sprintf("%.1f k", pl.(scores(field)) / 1000);
1169
1170 default: case SP_SCORE:
1171 tmp = pl.(scores(field));
1172 f = scores_flags(field);
1173 if (field == ps_primary)
1174 sbt_field_rgb = '1 1 0';
1175 else if (field == ps_secondary)
1176 sbt_field_rgb = '0 1 1';
1177 else
1178 sbt_field_rgb = '1 1 1';
1179 return ScoreString(f, tmp, rounds_played);
1180 }
1181 //return "error";
1182}
1183
1188
1189string Scoreboard_FixColumnWidth(int i, string str, bool init)
1190{
1191 TC(int, i);
1192 vector sz;
1193 int j;
1194 float f;
1195
1199
1200 for (j = 0; j < 3; ++j)
1201 if (sbt_field_icon_playercolor[j] != "")
1202 {
1203 sz = draw_getimagesize(sbt_field_icon_playercolor[j]);
1204 f = sz.x / sz.y;
1207 }
1208 // NOTE: extra icons (player_ready and player_handicap) are squares (32x32), so this may not be necessary
1209 // simply setting sbt_fixcolumnwidth_iconlen_extra* = 1 would be equivalent
1210 for (j = 0; j < SBT_FIELD_ICON_EXTRA_COUNT; ++j)
1211 if (sbt_field_icon_extra[j] != "")
1212 {
1213 sz = draw_getimagesize(sbt_field_icon_extra[j]);
1214 // XONRELEASE TODO: xonotic-v0.9.0 after release remove if (sz == 0) case
1215 if (sz == '0 0 0') // it happens in previous engine versions (Xonotic 0.8.6 or lower) when the image is missing
1216 sz = '1 1 0';
1217 sbt_fixcolumnwidth_iconlen_extra[j] = sz.x / sz.y;
1218 }
1219
1222 for (j = 0; j < 2; ++j)
1224 sbt_fixcolumnwidth_iconlen_extra[j] *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect
1225
1229 {
1231 }
1232 else
1234
1235 if (init)
1237
1238 if (sbt_field[i] == SP_NAME) // name gets all remaining space
1239 {
1240 float used_space = 0;
1241 for (j = 0; j < sbt_num_fields; ++j)
1242 if (j != i && sbt_field[i] != SP_SEPARATOR)
1243 used_space += sbt_field_size[j] + hud_fontsize.x;
1244 sbt_field_size[i] = max(sbt_field_title_width[i], panel_size.x - used_space);
1245
1246 used_space += sbt_fixcolumnwidth_marginlen
1250 float namesize = max(sbt_field_title_width[i], panel_size.x - used_space);
1251 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
1253
1255 }
1256 else
1257 {
1258 if (init)
1259 {
1262 if (sbt_field_size[i] && sbt_field_size[i] > title_maxwidth)
1263 sbt_field_size[i] = title_maxwidth;
1264 }
1266 }
1267
1272 if (sbt_field_size[i] < f)
1273 sbt_field_size[i] = f;
1274
1277 {
1278 float real_maxwidth = sbt_field_size[i];
1280 if (sbt_field_title_width[i] > title_maxwidth)
1281 real_maxwidth = max(sbt_field_size[i], title_maxwidth);
1283 }
1284
1285 return str;
1286}
1287
1288void Scoreboard_initFieldSizes(bool compress_more)
1289{
1290 if (compress_more)
1291 {
1292 float sbt_field_title_maxwidth_factor_prev = sbt_field_title_maxwidth_factor;
1295 {
1297 if (sbt_field_title_maxwidth_factor_prev == sbt_field_title_maxwidth_factor)
1298 return;
1299 }
1300 }
1301 else
1303
1304 for (int i = 0; i < sbt_num_fields; ++i)
1305 {
1306 if (sbt_field[i] == SP_NAME)
1307 {
1308 name_field_index = i;
1309 continue;
1310 }
1311
1312 Scoreboard_FixColumnWidth(i, "", true);
1313 }
1314
1315 // update name field size in the end as it takes remaining space
1317}
1318
1319vector Scoreboard_DrawHeader(vector pos, vector rgb, bool other_players)
1320{
1321 vector column_dim = eY * panel_size.y;
1322 if (other_players)
1323 column_dim.y -= 1.25 * hud_fontsize.y;
1324 vector text_offset = eY * (1.25 - 1) / 2 * hud_fontsize.y;
1325 pos.x += hud_fontsize.x * 0.5;
1326 int i;
1327 for (i = 0; i < sbt_num_fields; ++i)
1328 {
1329 if (sbt_field[i] == SP_SEPARATOR)
1330 break;
1331 column_dim.x = sbt_field_size[i] + hud_fontsize.x;
1332 if (sbt_highlight && (i % 2))
1333 drawfill(pos - eX * hud_fontsize.x * 0.5, column_dim, '0 0 0', sbt_highlight_alpha, DRAWFLAG_NORMAL);
1334 vector prev_drawfontscale = drawfontscale;
1337 drawstring(pos + text_offset, sbt_field_title[i], hud_fontsize, rgb * 1.5, sbt_fg_alpha, DRAWFLAG_NORMAL);
1339 {
1341 drawfontscale = prev_drawfontscale;
1342 }
1343 pos.x += column_dim.x;
1344 }
1345
1346 if (sbt_field[i] == SP_SEPARATOR)
1347 {
1348 pos.x = panel_pos.x + panel_size.x - hud_fontsize.x * 0.5;
1349 vector prev_drawfontscale;
1350 float titlewidth;
1351 for (i = sbt_num_fields - 1; i > 0; --i)
1352 {
1353 if (sbt_field[i] == SP_SEPARATOR)
1354 break;
1355
1356 pos.x -= sbt_field_size[i];
1357
1358 if (sbt_highlight && !(i % 2))
1359 {
1360 column_dim.x = sbt_field_size[i] + hud_fontsize.x;
1361 drawfill(pos - eX * hud_fontsize.x * 0.5, column_dim, '0 0 0', sbt_highlight_alpha, DRAWFLAG_NORMAL);
1362 }
1363
1364 prev_drawfontscale = drawfontscale;
1365 titlewidth = stringwidth(sbt_field_title[i], false, hud_fontsize);
1367 {
1369 text_offset.x = sbt_field_size[i] - titlewidth * sbt_field_title_condense_factor[i];
1370 }
1371 else
1372 text_offset.x = sbt_field_size[i] - titlewidth;
1373 drawstring(pos + text_offset, sbt_field_title[i], hud_fontsize, rgb * 1.5, sbt_fg_alpha, DRAWFLAG_NORMAL);
1375 {
1377 drawfontscale = prev_drawfontscale;
1378 }
1379 pos.x -= hud_fontsize.x;
1380 }
1381 }
1382
1383 pos.x = panel_pos.x;
1384 pos.y += 1.25 * hud_fontsize.y;
1385 return pos;
1386}
1387
1388void Scoreboard_DrawItem(vector item_pos, vector rgb, entity pl, bool is_self, int pl_number)
1389{
1390 TC(bool, is_self); TC(int, pl_number);
1391 string str;
1392 bool is_spec = (entcs_GetSpecState(pl.sv_entnum) == ENTCS_SPEC_PURE);
1393
1394 vector h_pos = item_pos;
1395 vector h_size = vec2(panel_size.x, hud_fontsize.y * 1.25);
1396 // alternated rows highlighting
1398 {
1400 drawfill(h_pos, h_size, rgb, 0.44 * panel_fg_alpha, DRAWFLAG_NORMAL);
1401 }
1402 else if (is_self)
1404 else if (sbt_highlight && !(pl_number % 2))
1405 drawfill(h_pos, h_size, rgb, sbt_highlight_alpha, DRAWFLAG_NORMAL);
1406
1407 float fg_alpha = (is_self ? sbt_fg_alpha_self : sbt_fg_alpha);
1408
1409 vector pos = item_pos;
1410 // put a "self indicator" beside the self row, unicode U+25C0 (black left-pointing triangle)
1411 if (is_self)
1412 drawstring(pos + eX * (panel_size.x + 0.5 * hud_fontsize.x) + eY, "\xE2\x97\x80", hud_fontsize, rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
1413
1414 pos.x += hud_fontsize.x * 0.5;
1415 pos.y += (1.25 - 1) * 0.5 * hud_fontsize.y; // center text vertically
1416 vector tmp = '0 0 0';
1417 vector icon_sz;
1418 int i, j;
1419 PlayerScoreField field;
1420 for (i = 0; i < sbt_num_fields; ++i)
1421 {
1422 field = sbt_field[i];
1423 if (field == SP_SEPARATOR)
1424 break;
1425
1426 if (is_spec && field != SP_NAME && field != SP_PING)
1427 {
1428 pos.x += sbt_field_size[i] + hud_fontsize.x;
1429 continue;
1430 }
1432 str = Scoreboard_FixColumnWidth(i, str, false);
1433
1434 pos.x += sbt_field_size[i] + hud_fontsize.x;
1435
1436 if (field == SP_NAME)
1437 {
1442 if (teamplay)
1443 {
1444 int wants_join = entcs_GetWantsJoin(pl.sv_entnum);
1445 if (wants_join)
1446 {
1447 vector tmcolor = Team_ColorRGB(Team_IndexToTeam(wants_join)) * min(1, 1 + sin(2*M_PI * time));
1448 drawstring(pos - tmp, "(Q)", hud_fontsize, tmcolor, sbt_fg_alpha, DRAWFLAG_NORMAL);
1449 tmp.x -= stringwidth("(Q) ", false, hud_fontsize);
1450 }
1451 }
1452 drawcolorcodedstring(pos - tmp, str, hud_fontsize, fg_alpha, DRAWFLAG_NORMAL);
1453 }
1454 else
1455 {
1457 drawstring(pos - tmp, str, hud_fontsize, sbt_field_rgb, fg_alpha, DRAWFLAG_NORMAL);
1458 }
1459
1460 tmp.x = sbt_field_size[i] + hud_fontsize.x;
1462 for (j = 0; j < 3; ++j)
1463 if (sbt_field_icon_playercolor[j] != "")
1465 for (j = 0; j < SBT_FIELD_ICON_EXTRA_COUNT; ++j)
1466 if (sbt_field_icon_extra[j] != "")
1467 {
1468 tmp.x -= icon_sz.x;
1470 drawpic(pos - tmp, sbt_field_icon_extra[j], icon_sz, sbt_field_icon_extra_rgb[j], fg_alpha, DRAWFLAG_NORMAL);
1471 }
1472 }
1473
1474 if (sbt_field[i] == SP_SEPARATOR)
1475 {
1476 pos.x = item_pos.x + panel_size.x - hud_fontsize.x * 0.5;
1477 for (i = sbt_num_fields-1; i > 0; --i)
1478 {
1479 field = sbt_field[i];
1480 if (field == SP_SEPARATOR)
1481 break;
1482
1483 if (is_spec && field != SP_NAME && field != SP_PING)
1484 {
1485 pos.x -= sbt_field_size[i] + hud_fontsize.x;
1486 continue;
1487 }
1488
1490 str = Scoreboard_FixColumnWidth(i, str, false);
1491
1492 if (field == SP_NAME)
1493 {
1494 tmp.x = sbt_fixcolumnwidth_len; // left or right aligned? let's put it right...
1495 drawcolorcodedstring(pos - tmp, str, hud_fontsize, fg_alpha, DRAWFLAG_NORMAL);
1496 }
1497 else
1498 {
1499 tmp.x = sbt_fixcolumnwidth_len;
1500 drawstring(pos - tmp, str, hud_fontsize, sbt_field_rgb, fg_alpha, DRAWFLAG_NORMAL);
1501 }
1502
1503 tmp.x = sbt_field_size[i];
1505 for (j = 0; j < 3; ++j)
1506 if (sbt_field_icon_playercolor[j] != "")
1508 for (j = 0; j < SBT_FIELD_ICON_EXTRA_COUNT; ++j)
1509 if (sbt_field_icon_extra[j] != "")
1510 {
1511 tmp.x -= icon_sz.x;
1513 drawpic(pos - tmp, sbt_field_icon_extra[j], icon_sz, sbt_field_icon_extra_rgb[j], fg_alpha, DRAWFLAG_NORMAL);
1514 }
1515 pos.x -= sbt_field_size[i] + hud_fontsize.x;
1516 }
1517 }
1518
1519 if (pl.eliminated)
1521}
1522
1523vector Scoreboard_DrawOthers(vector item_pos, vector rgb, int this_team, entity excluded_pl, entity pl, int pl_number)
1524{
1525 int i = 0;
1526 vector h_pos = item_pos;
1527 vector h_size = vec2(panel_size.x, hud_fontsize.y * 1.25);
1528
1529 bool complete = (this_team == NUM_SPECTATOR);
1530
1531 if (!complete && sbt_highlight && !(pl_number % 2))
1532 drawfill(h_pos, h_size, rgb, sbt_highlight_alpha, DRAWFLAG_NORMAL);
1533
1534 vector pos = item_pos;
1535 pos.x += hud_fontsize.x * 0.5;
1536 pos.y += (1.25 - 1) / 2 * hud_fontsize.y; // center text vertically
1537
1538 float width_limit = item_pos.x + panel_size.x - hud_fontsize.x;
1539 if (!complete)
1540 width_limit -= stringwidth("...", false, hud_fontsize);
1542 static float max_name_width = 0;
1543 string field = "";
1544 float fieldsize = 0;
1545 float min_fieldsize = 0;
1546 float fieldpadding = hud_fontsize.x * 0.25;
1547 if (this_team == NUM_SPECTATOR)
1548 {
1550 min_fieldsize = stringwidth("999", false, hud_fontsize);
1551 }
1553 min_fieldsize = stringwidth("99", false, hud_fontsize);
1554 for (i = 0; pl; pl = pl.sort_next)
1555 {
1556 if (pl.team != this_team)
1557 continue;
1558 if (pl == excluded_pl)
1559 continue;
1560
1561 field = "";
1562 if (this_team == NUM_SPECTATOR)
1563 {
1566 }
1569
1570 string str = entcs_GetName(pl.sv_entnum);
1572 str = Scoreboard_AddPlayerId(str, pl);
1573
1574 // ignored_size includes icon and padding
1575 float ignored_size = (pl.ignored ? hud_fontsize.y + hud_fontsize.x * 0.25 : 0);
1576 int wants_join = (teamplay ? entcs_GetWantsJoin(pl.sv_entnum) : 0);
1577 float queued_size = (wants_join ? stringwidth("(Q) ", false, hud_fontsize) : 0);
1578 str = textShortenToWidth(str, namesize - ignored_size - queued_size, hud_fontsize, stringwidth_colors);
1579 float column_width = stringwidth(str, true, hud_fontsize) + ignored_size + queued_size;
1581 {
1582 if (column_width > max_name_width)
1583 max_name_width = column_width;
1584 column_width = max_name_width;
1585 }
1586 if (field != "")
1587 {
1588 fieldsize = stringwidth(field, false, hud_fontsize);
1589 column_width += hud_fontsize.x * 0.25 + max(fieldsize, min_fieldsize) + 2 * fieldpadding;
1590 }
1591
1592 if (pos.x + column_width > width_limit)
1593 {
1594 ++i;
1595 if (!complete)
1596 {
1597 drawstring(pos, "...", hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
1598 break;
1599 }
1600 else
1601 {
1602 pos.x = item_pos.x + hud_fontsize.x * 0.5;
1603 pos.y += hud_fontsize.y * 1.25;
1604 }
1605 }
1606
1608 {
1610 {
1611 h_size.x = column_width + hud_fontsize.x * 0.25;
1612 h_size.y = hud_fontsize.y;
1613 drawfill(pos - hud_fontsize.x * 0.25 * eX, h_size, rgb, 0.44 * panel_fg_alpha, DRAWFLAG_NORMAL);
1614 }
1615 }
1616
1617 vector name_pos = pos;
1619 name_pos.x += max(fieldsize, min_fieldsize) + 2 * fieldpadding + hud_fontsize.x * 0.25;
1620 if (pl.ignored)
1621 {
1622 drawpic(name_pos, "gfx/scoreboard/player_ignored", '1 1 0' * hud_fontsize.y, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
1623 name_pos.x += ignored_size;
1624 }
1625 if (wants_join)
1626 {
1627 vector tmcolor = Team_ColorRGB(Team_IndexToTeam(wants_join)) * min(1, 1 + sin(2*M_PI * time));
1628 drawstring(name_pos, "(Q)", hud_fontsize, tmcolor, sbt_fg_alpha, DRAWFLAG_NORMAL);
1629 name_pos.x += queued_size;
1630 }
1632
1633 if (field != "")
1634 {
1635 h_size.x = max(fieldsize, min_fieldsize) + 2 * fieldpadding;
1636 h_size.y = hud_fontsize.y;
1637 vector field_pos = pos;
1639 field_pos.x += column_width - h_size.x;
1640 if (sbt_highlight)
1641 drawfill(field_pos, h_size, '1 1 1', sbt_highlight_alpha, DRAWFLAG_NORMAL);
1642 field_pos.x += fieldpadding + (max(fieldsize, min_fieldsize) - fieldsize) * 0.5;
1644 }
1645 if (pl.eliminated)
1646 {
1647 h_size.x = column_width + hud_fontsize.x * 0.25;
1648 h_size.y = hud_fontsize.y;
1649 drawfill(pos - hud_fontsize.x * 0.25 * eX, h_size, '0 0 0', sbt_highlight_alpha_eliminated, DRAWFLAG_NORMAL);
1650 }
1651 pos.x += column_width;
1652 pos.x += hud_fontsize.x;
1653 }
1654 return vec2(item_pos.x, item_pos.y + i * hud_fontsize.y * 1.25);
1655}
1656
1658{
1659 int max_players = 999;
1661 {
1663 if (teamplay)
1664 {
1665 height -= (panel_bg_padding * 2 + hud_fontsize.y * 1.25) * team_count; // - padding and header
1666 height -= hud_fontsize.y * (team_count - 1); // - spacing between tables
1667 height /= team_count;
1668 }
1669 else
1670 height -= panel_bg_padding * 2; // - padding
1671 max_players = floor(height / (hud_fontsize.y * 1.25));
1672 if (max_players <= 1)
1673 max_players = 1;
1674 if (max_players == tm.team_size)
1675 max_players = 999;
1676 }
1677
1678 panel_pos = pos;
1679 panel_size.y = 1.25 * hud_fontsize.y * (1 + bound(1, tm.team_size, max_players));
1681
1682 vector scoreboard_selected_hl_pos = pos;
1683 vector scoreboard_selected_hl_size = '0 0 0';
1684 scoreboard_selected_hl_size.x = scoreboard_right - scoreboard_left;
1685 scoreboard_selected_hl_size.y = panel_size.y;
1686
1688
1689 vector end_pos = panel_pos + eY * (panel_size.y + 0.5 * hud_fontsize.y);
1690 if (panel.current_panel_bg != "0")
1691 end_pos.y += panel_bg_border * 2;
1692
1693 if (panel_bg_padding)
1694 {
1695 panel_pos += '1 1 0' * panel_bg_padding;
1696 panel_size -= '2 2 0' * panel_bg_padding;
1697 }
1698
1699 pos = panel_pos;
1700 vector tmp = vec2(panel_size.x, 1.25 * hud_fontsize.y);
1701
1702 // rounded header
1703 if (sbt_bg_alpha)
1704 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', sbt_bg_alpha, DRAWFLAG_NORMAL);
1705
1706 pos.y += 1.25 * hud_fontsize.y;
1707
1708 // table background
1709 tmp.y = panel_size.y - 1.25 * hud_fontsize.y;
1710 if (sbt_bg_alpha)
1711 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL);
1712
1713
1714 // print header row and highlight columns
1715 pos = Scoreboard_DrawHeader(panel_pos, rgb, (max_players < tm.team_size));
1716
1717 // fill the table and draw the rows
1718 bool is_self = false;
1719 bool self_shown = false;
1720 int i = 0;
1722 for (entity pl = players.sort_next; pl; pl = pl.sort_next)
1723 {
1724 if (pl.team != tm.team)
1725 continue;
1726 if (i == max_players - 2 && pl != me)
1727 {
1728 if (!self_shown && me.team == tm.team)
1729 {
1730 Scoreboard_DrawItem(pos, rgb, me, true, i);
1731 self_shown = true;
1732 pos.y += 1.25 * hud_fontsize.y;
1733 ++i;
1734 }
1735 }
1736 if (i >= max_players - 1)
1737 {
1738 pos = Scoreboard_DrawOthers(pos, rgb, tm.team, (self_shown ? me : NULL), pl, i);
1739 break;
1740 }
1741 is_self = (pl.sv_entnum == current_player);
1742 Scoreboard_DrawItem(pos, rgb, pl, is_self, i);
1743 if (is_self)
1744 self_shown = true;
1745 pos.y += 1.25 * hud_fontsize.y;
1746 ++i;
1747 }
1748
1750 {
1751 if (scoreboard_ui_enabled == 1 || (tm && scoreboard_selected_team == tm))
1752 {
1753 float _alpha = (scoreboard_ui_enabled == 2) ? 0.2 : 0.3 * max(0, (1 - (time - scoreboard_selected_panel_time) * 2));
1754 _alpha *= panel_fg_alpha;
1755 if (_alpha)
1756 drawfill(scoreboard_selected_hl_pos, scoreboard_selected_hl_size, '1 1 1', _alpha, DRAWFLAG_NORMAL);
1757 }
1758 }
1759
1760 panel_size.x += panel_bg_padding * 2; // restore initial width
1761 return end_pos;
1762}
1763
1765{
1767 {
1769 {
1770 if (scoreboard_fade_alpha == 0)
1772 return false;
1773 }
1775 {
1777 return false;
1778 }
1779 return true;
1780 }
1781 else if (MUTATOR_CALLHOOK(DrawScoreboard))
1782 return false;
1783 else if (QuickMenu_IsOpened())
1784 return false;
1785 else if (HUD_Radar_Clickable())
1786 return false;
1787 else if (sb_showscores) // set by +showscores engine command
1788 return true;
1789 else if (intermission == 1)
1790 return true;
1791 else if (intermission == 2)
1792 return false;
1793 else if (spectatee_status != -1 && STAT(HEALTH) <= 0 && autocvar_cl_deathscoreboard && !MUTATOR_CALLHOOK(DrawDeathScoreboard)
1796 {
1797 return true;
1798 }
1799 else if (scoreboard_showscores_force || MUTATOR_CALLHOOK(DrawScoreboard_Force))
1800 return true;
1801 return false;
1802}
1803
1806{
1808
1809 WepSet weapons_stat = WepSet_GetFromStat();
1810 WepSet weapons_inmap = WepSet_GetFromStat_InMap();
1811 int disownedcnt = 0;
1812 int nHidden = 0;
1813 FOREACH(Weapons, it != WEP_Null,
1814 {
1815 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1816
1817 WepSet set = it.m_wepset;
1818 if (it.spawnflags & WEP_TYPE_OTHER)
1819 {
1820 ++nHidden;
1821 continue;
1822 }
1823 if (weapon_stats < 0 && !((weapons_stat & set) || (weapons_inmap & set)))
1824 {
1825 if (it.spawnflags & (WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED))
1826 ++nHidden;
1827 else
1828 ++disownedcnt;
1829 }
1830 });
1831
1832 int weapon_cnt = (REGISTRY_COUNT(Weapons) - 1) - disownedcnt - nHidden;
1833 if (weapon_cnt <= 0)
1834 return pos;
1835
1836 int rows = 1;
1837 if (autocvar_hud_panel_scoreboard_accuracy_doublerows && weapon_cnt >= floor((REGISTRY_COUNT(Weapons) - nHidden - 1) * 0.5))
1838 rows = 2;
1839 int columns = ceil(weapon_cnt / rows);
1840
1841 float aspect = max(0.001, autocvar_hud_panel_weapons_aspect);
1842 float weapon_height = hud_fontsize.y * 2.3 / aspect;
1843 float height = weapon_height + hud_fontsize.y;
1844
1845 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);
1846 pos.y += 1.25 * hud_fontsize.y;
1847 if (panel.current_panel_bg != "0")
1848 pos.y += panel_bg_border;
1849
1850 panel_pos = pos;
1851 panel_size.y = height * rows;
1853
1854 float panel_bg_alpha_save = panel_bg_alpha;
1857 panel_bg_alpha = panel_bg_alpha_save;
1858
1859 vector end_pos = panel_pos + eY * (panel_size.y + 0.5 * hud_fontsize.y);
1860 if (panel.current_panel_bg != "0")
1861 end_pos.y += panel_bg_border * 2;
1862
1863 if (panel_bg_padding)
1864 {
1865 panel_pos += '1 1 0' * panel_bg_padding;
1866 panel_size -= '2 2 0' * panel_bg_padding;
1867 }
1868
1869 pos = panel_pos;
1870 vector tmp = panel_size;
1871
1872 float weapon_width = tmp.x / columns / rows;
1873
1874 if (sbt_bg_alpha)
1875 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, sbt_bg_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL);
1876
1877 if (sbt_highlight)
1878 {
1879 // column highlighting
1880 for (int i = 0; i < columns; ++i)
1881 if ((i % 2) == 0)
1882 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);
1883
1884 // row highlighting
1885 for (int i = 0; i < rows; ++i)
1886 drawfill(pos + eY * (weapon_height + height * i), vec2(tmp.x, hud_fontsize.y), rgb, sbt_highlight_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL);
1887 }
1888
1889 average_accuracy = 0;
1890 int weapons_with_stats = 0;
1891 if (rows == 2)
1892 pos.x += weapon_width / 2;
1893
1895 rgb = '1 1 1';
1896 else
1898
1899 float oldposx = pos.x;
1900 vector tmpos = pos;
1901
1902 int column = 0;
1903 FOREACH(Weapons, it != WEP_Null,
1904 {
1905 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
1906
1907 WepSet set = it.m_wepset;
1908 if (weapon_stats < 0 && !((weapons_stat & set) || (weapons_inmap & set)))
1909 continue;
1910 if (it.spawnflags & WEP_TYPE_OTHER)
1911 continue;
1912
1913 float weapon_alpha;
1914 if (weapon_stats >= 0)
1915 weapon_alpha = sbt_fg_alpha;
1916 else
1917 weapon_alpha = 0.2 * sbt_fg_alpha;
1918
1919 // weapon icon
1920 drawpic_aspect_skin(tmpos, it.model2, vec2(weapon_width, weapon_height), '1 1 1', weapon_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL);
1921 // the accuracy
1922 if (weapon_stats >= 0)
1923 {
1924 ++weapons_with_stats;
1925 average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy
1926
1927 string s = ftos_decimals_percentage(weapon_stats, 0);
1928 float padding = (weapon_width - stringwidth(s, false, hud_fontsize)) / 2;
1929
1931 rgb = Accuracy_GetColor(weapon_stats);
1932
1933 drawstring(tmpos + vec2(padding, weapon_height), s, hud_fontsize, rgb, sbt_fg_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL);
1934 }
1935 tmpos.x += weapon_width * rows;
1936 pos.x += weapon_width * rows;
1937 if (rows == 2 && column == columns - 1)
1938 {
1939 tmpos.x = oldposx;
1940 tmpos.y += height;
1941 pos.y += height;
1942 }
1943 ++column;
1944 });
1945
1946 if (weapons_with_stats)
1947 average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5);
1948
1949 panel_size.x += panel_bg_padding * 2; // restore initial width
1950
1951 return end_pos;
1952}
1953
1955{
1957 return false;
1959 if (mask <= 0)
1960 return false;
1961 if (it.instanceOfArmor || it.instanceOfHealth)
1962 {
1963 switch (floor(mask) % 10) // health-armor mask
1964 {
1965 default: return false;
1966 case 4: if (it == ITEM_HealthMega || it == ITEM_ArmorMega) return true; // else fallthrough
1967 case 3: if (it == ITEM_HealthBig || it == ITEM_ArmorBig) return true; // else fallthrough
1968 case 2: if (it == ITEM_HealthMedium || it == ITEM_ArmorMedium) return true; // else fallthrough
1969 case 1: if (it == ITEM_HealthSmall || it == ITEM_ArmorSmall) return true; // else fallthrough
1970 }
1971 }
1972 if (it.instanceOfAmmo)
1973 return (floor(mask / 10) % 10) == 1; // ammo mask
1974
1975 return false;
1976}
1977
1979{
1981
1982 int disowned_cnt = 0;
1983 int uninteresting_cnt = 0;
1985 {
1986 int q = g_inventory.inv_items[it.m_id];
1987 //q = 1; // debug: display all items
1988 if (is_item_filtered(it))
1989 ++uninteresting_cnt;
1990 else if (!q)
1991 ++disowned_cnt;
1992 });
1993 int items_cnt = REGISTRY_COUNT(Items) - uninteresting_cnt;
1994 int n = items_cnt - disowned_cnt;
1995 if (n <= 0)
1996 return pos;
1997
1998 int rows = (autocvar_hud_panel_scoreboard_itemstats_doublerows && n >= floor(REGISTRY_COUNT(Items) / 2)) ? 2 : 1;
1999 int columns = max(6, ceil(n / rows));
2000
2001 float item_height = hud_fontsize.y * 2.3;
2002 float height = item_height + hud_fontsize.y;
2003
2005 pos.y += 1.25 * hud_fontsize.y;
2006 if (panel.current_panel_bg != "0")
2007 pos.y += panel_bg_border;
2008
2009 panel_pos = pos;
2010 panel_size.y = height * rows;
2012
2013 float panel_bg_alpha_save = panel_bg_alpha;
2016 panel_bg_alpha = panel_bg_alpha_save;
2017
2018 vector end_pos = panel_pos + eY * (panel_size.y + 0.5 * hud_fontsize.y);
2019 if (panel.current_panel_bg != "0")
2020 end_pos.y += panel_bg_border * 2;
2021
2022 if (panel_bg_padding)
2023 {
2024 panel_pos += '1 1 0' * panel_bg_padding;
2025 panel_size -= '2 2 0' * panel_bg_padding;
2026 }
2027
2028 pos = panel_pos;
2029 vector tmp = panel_size;
2030
2031 float item_width = tmp.x / columns / rows;
2032
2033 if (sbt_bg_alpha)
2034 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, sbt_bg_alpha * scoreboard_itemstats_fade_alpha, DRAWFLAG_NORMAL);
2035
2036 if (sbt_highlight)
2037 {
2038 // column highlighting
2039 for (int i = 0; i < columns; ++i)
2040 if ((i % 2) == 0)
2041 drawfill(pos + eX * item_width * rows * i, vec2(item_width * rows, height * rows), '0 0 0', sbt_highlight_alpha * scoreboard_itemstats_fade_alpha, DRAWFLAG_NORMAL);
2042
2043 // row highlighting
2044 for (int i = 0; i < rows; ++i)
2046 }
2047
2048 if (rows == 2)
2049 pos.x += item_width * 0.5;
2050
2051 float oldposx = pos.x;
2052 vector tmpos = pos;
2053
2054 int column = 0;
2056 {
2057 int n = g_inventory.inv_items[it.m_id];
2058 //n = 1 + floor(i * 3 + 4.8) % 7; // debug: display a value for each item
2059 if (n <= 0)
2060 continue;
2061 drawpic_aspect_skin(tmpos, it.m_icon, eX * item_width + eY * item_height, '1 1 1', panel_fg_alpha * scoreboard_itemstats_fade_alpha, DRAWFLAG_NORMAL);
2062 string s = ftos(n);
2063 float padding = (item_width - stringwidth(s, false, hud_fontsize)) / 2;
2064 drawstring(tmpos + vec2(padding, item_height), s, hud_fontsize, '1 1 1', panel_fg_alpha * scoreboard_itemstats_fade_alpha, DRAWFLAG_NORMAL);
2065 tmpos.x += item_width * rows;
2066 pos.x += item_width * rows;
2067 if (rows == 2 && column == columns - 1)
2068 {
2069 tmpos.x = oldposx;
2070 tmpos.y += height;
2071 pos.y += height;
2072 }
2073 ++column;
2074 });
2075
2076 panel_size.x += panel_bg_padding * 2; // restore initial width
2077
2078 return end_pos;
2079}
2080
2081vector MapStats_DrawKeyValue(vector pos, string key, string value)
2082{
2083 float px = pos.x;
2084 pos.x += hud_fontsize.x * 0.25;
2086 pos.x = panel_pos.x + panel_size.x - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
2087 drawstring(pos, value, hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
2088 pos.x = px;
2089 pos.y += hud_fontsize.y;
2090
2091 return pos;
2092}
2093
2095{
2096 float stat_secrets_found, stat_secrets_total;
2097 float stat_monsters_killed, stat_monsters_total;
2098 float rows = 0;
2099 string val;
2100
2101 // get monster stats
2102 stat_monsters_killed = STAT(MONSTERS_KILLED);
2103 stat_monsters_total = STAT(MONSTERS_TOTAL);
2104
2105 // get secrets stats
2106 stat_secrets_found = STAT(SECRETS_FOUND);
2107 stat_secrets_total = STAT(SECRETS_TOTAL);
2108
2109 // get number of rows
2110 if (stat_secrets_total)
2111 rows += 1;
2112 if (stat_monsters_total)
2113 rows += 1;
2114
2115 // if no rows, return
2116 if (!rows)
2117 return pos;
2118
2119 // draw table header
2120 drawstring(pos + eX * panel_bg_padding, _("Map stats:"), hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
2121 pos.y += 1.25 * hud_fontsize.y;
2122 if (panel.current_panel_bg != "0")
2123 pos.y += panel_bg_border;
2124
2125 panel_pos = pos;
2126 panel_size.y = hud_fontsize.y * rows;
2129
2130 vector end_pos = panel_pos + eY * (panel_size.y + 0.5 * hud_fontsize.y);
2131 if (panel.current_panel_bg != "0")
2132 end_pos.y += panel_bg_border * 2;
2133
2134 if (panel_bg_padding)
2135 {
2136 panel_pos += '1 1 0' * panel_bg_padding;
2137 panel_size -= '2 2 0' * panel_bg_padding;
2138 }
2139
2140 pos = panel_pos;
2141 vector tmp = panel_size;
2142
2143 if (sbt_bg_alpha)
2144 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL);
2145
2146 // draw monsters
2147 if (stat_monsters_total)
2148 {
2149 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
2150 pos = MapStats_DrawKeyValue(pos, _("Monsters killed:"), val);
2151 }
2152
2153 // draw secrets
2154 if (stat_secrets_total)
2155 {
2156 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
2157 pos = MapStats_DrawKeyValue(pos, _("Secrets found:"), val);
2158 }
2159
2160 panel_size.x += panel_bg_padding * 2; // restore initial width
2161 return end_pos;
2162}
2163
2164vector Scoreboard_Rankings_Draw(vector pos, string ranktitle, entity pl, vector rgb, vector bg_size)
2165{
2166 int i;
2168 for (i = RANKINGS_CNT - 1; i >= 0; --i)
2169 if (grecordtime[i])
2171
2172 if (RANKINGS_RECEIVED_CNT == 0)
2173 return pos;
2174
2175 vector hl_rgb = rgb + '0.5 0.5 0.5';
2176
2177 vector scoreboard_selected_hl_pos = pos;
2178
2180 pos.y += 1.25 * hud_fontsize.y;
2181 if (panel.current_panel_bg != "0")
2182 pos.y += panel_bg_border;
2183
2184 vector scoreboard_selected_hl_size = '0 0 0';
2185 scoreboard_selected_hl_size.x = scoreboard_right - scoreboard_left;
2186 scoreboard_selected_hl_size.y = pos.y - scoreboard_selected_hl_pos.y;
2187
2188 panel_pos = pos;
2189
2190 float namesize = 0;
2191 for (i = 0; i < RANKINGS_RECEIVED_CNT; ++i)
2192 {
2194 if (f > namesize)
2195 namesize = f;
2196 }
2197 bool cut;
2199 {
2201 cut = true;
2202 }
2203 else
2204 cut = false;
2205
2206 float ranksize = 3 * hud_fontsize.x;
2207 float timesize = 5 * hud_fontsize.x;
2208 vector columnsize = vec2(ranksize + timesize + namesize + hud_fontsize.x, 1.25 * hud_fontsize.y);
2209 rankings_columns = max(1, floor((panel_size.x - 2 * panel_bg_padding) / columnsize.x));
2211 if (!rankings_cnt)
2212 {
2215 }
2216
2217 // expand name column to fill the entire row
2218 float available_space = (panel_size.x - 2 * panel_bg_padding - columnsize.x * rankings_columns) / rankings_columns;
2219 namesize += available_space;
2220 columnsize.x += available_space;
2221
2222 panel_size.y = rankings_rows * 1.25 * hud_fontsize.y;
2224 scoreboard_selected_hl_size.y += panel_size.y;
2225
2227
2228 vector end_pos = panel_pos + eY * (panel_size.y + 0.5 * hud_fontsize.y);
2229 if (panel.current_panel_bg != "0")
2230 end_pos.y += panel_bg_border * 2;
2231
2232 if (panel_bg_padding)
2233 {
2234 panel_pos += '1 1 0' * panel_bg_padding;
2235 panel_size -= '2 2 0' * panel_bg_padding;
2236 }
2237
2238 pos = panel_pos;
2239
2240 if (sbt_bg_alpha)
2241 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, panel_size, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL);
2242
2243 vector text_ofs = vec2(0.5 * hud_fontsize.x, (1.25 - 1) * 0.5 * hud_fontsize.y); // center text vertically
2244 string str = "";
2245 int column = 0, j = 0;
2246 string zoned_name_self = strzone(strdecolorize(entcs_GetName(player_localnum)));
2247 int start_item = rankings_start_column * rankings_rows;
2248 for (i = start_item; i < start_item + rankings_cnt; ++i)
2249 {
2250 int t = grecordtime[i];
2251 if (t == 0)
2252 continue;
2253
2254 if (strdecolorize(grecordholder[i]) == zoned_name_self)
2255 drawfill(pos, columnsize, hl_rgb, sbt_highlight_alpha_self, DRAWFLAG_NORMAL);
2256 else if (!((j + rankings_start_column + column) & 1) && sbt_highlight)
2257 drawfill(pos, columnsize, hl_rgb, sbt_highlight_alpha, DRAWFLAG_NORMAL);
2258
2259 str = count_ordinal(i+1);
2260 vector rank_color;
2261 switch (i)
2262 {
2263 case 0: rank_color = '0.933 0.733 0.200'; break; // 1st; gold
2264 case 1: rank_color = '0.667 0.667 0.667'; break; // 2nd; silver
2265 case 2: rank_color = '0.800 0.467 0.267'; break; // 3rd; bronze
2266 default: rank_color = '1 1 1';
2267 }
2268 drawstring(pos + text_ofs, str, hud_fontsize, rank_color, sbt_fg_alpha, DRAWFLAG_NORMAL);
2269 drawstring(pos + text_ofs + eX * ranksize, TIME_ENCODED_TOSTRING(t, true), hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
2271 if (cut)
2272 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
2273 drawcolorcodedstring(pos + text_ofs + eX * (ranksize + timesize), str, hud_fontsize, sbt_fg_alpha, DRAWFLAG_NORMAL);
2274
2275 pos.y += 1.25 * hud_fontsize.y;
2276 ++j;
2277 if (j >= rankings_rows)
2278 {
2279 ++column;
2280 j = 0;
2281 pos.x += panel_size.x / rankings_columns;
2282 pos.y = panel_pos.y;
2283 }
2284 }
2285 strfree(zoned_name_self);
2286
2288 {
2289 float fade = max(0, (1 - (time - scoreboard_selected_panel_time) * 2));
2290 drawfill(scoreboard_selected_hl_pos, scoreboard_selected_hl_size, '1 1 1', fade * 0.44, DRAWFLAG_NORMAL);
2291 }
2292
2293 panel_size.x += panel_bg_padding * 2; // restore initial width
2294 return end_pos;
2295}
2296
2299{
2300 if (MUTATOR_CALLHOOK(DrawScoreboardAccuracy))
2301 return false;
2303 return false;
2304
2307 && !intermission)
2308 {
2309 return false;
2310 }
2311
2312 if (!have_weapon_stats)
2313 {
2314 FOREACH(Weapons, it != WEP_Null,
2315 {
2316 int weapon_stats = weapon_accuracy[i - WEP_FIRST];
2317 if (weapon_stats >= 0)
2318 {
2319 have_weapon_stats = true;
2320 break;
2321 }
2322 });
2323 if (!have_weapon_stats)
2324 return false;
2325 }
2326
2327 return true;
2328}
2329
2332{
2333 if (MUTATOR_CALLHOOK(DrawScoreboardItemStats))
2334 return false;
2336 return false;
2337
2340 && !intermission)
2341 {
2342 return false;
2343 }
2344
2345 if (!have_item_stats)
2346 {
2348 {
2349 int q = g_inventory.inv_items[it.m_id];
2350 //q = 1; // debug: display all items
2351 if (q)
2352 {
2353 have_item_stats = true;
2354 break;
2355 }
2356 });
2357 if (!have_item_stats)
2358 return false;
2359 }
2360
2361 return true;
2362}
2363
2365{
2366 string str = "";
2367
2368 for (entity tm, pl = players.sort_next; pl; pl = pl.sort_next)
2369 if (pl.team == NUM_SPECTATOR)
2370 {
2371 for (tm = teams.sort_next; tm; tm = tm.sort_next)
2372 if (tm.team == NUM_SPECTATOR)
2373 break;
2374 str = sprintf("%s (%d)", _("Spectators"), tm.team_size);
2378 pos.y += 1.25 * hud_fontsize.y;
2379
2380 pos = Scoreboard_DrawOthers(pos, '0 0 0', pl.team, NULL, pl, 0);
2381 pos.y += 1.25 * hud_fontsize.y;
2382
2383 break;
2384 }
2385
2386 if (str != "") // if there's at least one spectator
2387 pos.y += 0.5 * hud_fontsize.y;
2388
2389 return pos;
2390}
2391
2392string Scoreboard_Fraglimit_Draw(float limit, bool is_leadlimit)
2393{
2396 return sprintf((is_leadlimit ? _("^2+%s %s") : _("^5%s %s")), ScoreString(s_flags, limit, 0),
2397 (s_label == "score") ? CTX(_("SCO^points")) :
2398 (s_label == "fastest") ? "" : TranslateScoresLabel(s_label));
2399}
2400
2402{
2404 {
2405 if (!hud_draw_maximized)
2406 return;
2407
2408 // frametime checks allow to toggle the scoreboard even when the game is paused
2410 {
2411 if (scoreboard_fade_alpha == 0)
2413 if (hud_configure_menu_open == 1)
2415 float scoreboard_fadeinspeed = autocvar_hud_panel_scoreboard_fadeinspeed;
2416 if (scoreboard_fadeinspeed && frametime)
2417 scoreboard_fade_alpha = min(1, scoreboard_fade_alpha + frametime * scoreboard_fadeinspeed);
2418 else
2420
2421 static string hud_fontsize_str;
2422 if (hud_fontsize_str != autocvar_hud_fontsize)
2423 {
2424 hud_fontsize = HUD_GetFontsize("hud_fontsize");
2425 strcpy(hud_fontsize_str, autocvar_hud_fontsize);
2427 }
2428
2429 static float scoreboard_table_fieldtitle_maxwidth_prev;
2430 if (scoreboard_table_fieldtitle_maxwidth_prev != autocvar_hud_panel_scoreboard_table_fieldtitle_maxwidth)
2431 {
2432 scoreboard_table_fieldtitle_maxwidth_prev = autocvar_hud_panel_scoreboard_table_fieldtitle_maxwidth;
2436 }
2437 }
2438 else
2439 {
2440 float scoreboard_fadeoutspeed = autocvar_hud_panel_scoreboard_fadeoutspeed;
2441 if (scoreboard_fadeoutspeed && frametime)
2442 scoreboard_fade_alpha = max(0, scoreboard_fade_alpha - frametime * scoreboard_fadeoutspeed);
2443 else
2445 }
2446
2448 {
2451 return;
2452 }
2453 }
2454 else
2456
2459 else
2461
2462 if (scoreboard_fade_alpha <= 0)
2463 return;
2466
2474
2475 // don't overlap with con_notify
2478
2480 float fixed_scoreboard_width = bound(vid_conwidth * autocvar_hud_panel_scoreboard_minwidth, vid_conwidth - excess, vid_conwidth * 0.93);
2481 scoreboard_left = 0.5 * (vid_conwidth - fixed_scoreboard_width);
2482 scoreboard_right = scoreboard_left + fixed_scoreboard_width;
2484 panel_size.x = fixed_scoreboard_width;
2485
2486 // field sizes can be initialized now after panel_size.x calculation
2488 {
2489 bool compress = (sb_field_sizes_init == 2);
2490 Scoreboard_initFieldSizes(compress);
2492 }
2493
2495
2497 vector pos = panel_pos;
2498
2500 drawfill('0 0 0', eX * vid_conwidth + eY * vid_conheight, '0 0 0', 0.7 * panel_fade_alpha, DRAWFLAG_NORMAL);
2501
2502 vector sb_gameinfo_type_fontsize, sb_gameinfo_detail_fontsize;
2503
2504 // Begin of Game Info Section
2505 sb_gameinfo_type_fontsize = hud_fontsize * 2.5;
2506 sb_gameinfo_detail_fontsize = hud_fontsize * 1.3;
2507
2508 string str;
2509 if (GET_NEXTMAP() != "")
2510 {
2511 // NOTE: nextmap is drawn before title to avoid covering title in case of long map name
2512 str = strcat("^7", _("Next map:"), " ^9", GET_NEXTMAP());
2513 drawcolorcodedstring(pos + vec2(hud_fontsize.x * 0.5, sb_gameinfo_type_fontsize.y - hud_fontsize.y * 1.25),
2515 }
2516
2517 // Game Info: Gametype
2518 if (scoreboard_ui_enabled == 2)
2519 str = _("Team Selection");
2520 else if (gametype_custom_name != "")
2522 else
2525 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);
2527
2528 pos.y += sb_gameinfo_type_fontsize.y;
2529 // Game Info: Game Detail
2530 if (scoreboard_ui_enabled == 2)
2531 {
2533 str = sprintf(_("^7Press ^3%s^7 to join the selected team"), translate_key("SPACE"));
2534 else
2535 str = sprintf(_("^7Press ^3%s^7 to auto-select a team and join"), translate_key("SPACE"));
2536 drawcolorcodedstring(pos + '0.5 0 0' * (panel_size.x - stringwidth(str, true, sb_gameinfo_detail_fontsize)), str, sb_gameinfo_detail_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
2537
2538 pos.y += sb_gameinfo_detail_fontsize.y + hud_fontsize.y * 0.3;
2539 str = sprintf(_("^7Press ^3%s ^7to select a specific team"), translate_key("TAB"));
2540 drawcolorcodedstring(pos + '0.5 0 0' * (panel_size.x - stringwidth(str, true, sb_gameinfo_detail_fontsize)), str, sb_gameinfo_detail_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL);
2541 }
2542 else
2543 {
2544 float tl = STAT(TIMELIMIT);
2545 float fl = STAT(FRAGLIMIT);
2546 float ll = STAT(LEADLIMIT);
2547 float ll_and_fl = STAT(LEADLIMIT_AND_FRAGLIMIT);
2548 str = "";
2549 if (tl > 0)
2550 str = strcat(str, "^3", count_minutes(floor(tl)));
2551 if (!gametype.m_hidelimits)
2552 {
2553 if (fl > 0)
2554 {
2555 if (tl > 0)
2556 str = strcat(str, "^7 / "); // delimiter
2557 str = strcat(str, Scoreboard_Fraglimit_Draw(fl, false));
2558 }
2559 if (ll > 0 && (ll < fl || fl <= 0))
2560 {
2561 if (tl > 0 || fl > 0)
2562 {
2563 // delimiter
2564 if (ll_and_fl && fl > 0)
2565 str = strcat(str, "^7 & ");
2566 else
2567 str = strcat(str, "^7 / ");
2568 }
2569 str = strcat(str, Scoreboard_Fraglimit_Draw(ll, true));
2570 }
2571 }
2572 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
2573 // map name and player count
2574 if (campaign)
2575 str = "";
2576 else
2577 str = sprintf(_("^5%d^7/^5%d ^7players"), numplayers, srv_maxplayers ? srv_maxplayers : maxclients);
2578 str = strcat("^7", _("Map:"), " ^2", mi_shortname, " ", str); // reusing "Map:" translatable string
2579 drawcolorcodedstring(pos, str, sb_gameinfo_detail_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL); // align left
2580 }
2581 // End of Game Info Section
2582
2583 pos.y += sb_gameinfo_detail_fontsize.y + hud_fontsize.y * 0.3; // space between Game Info Section and score table
2584 if (panel.current_panel_bg != "0")
2585 pos.y += panel_bg_border;
2586
2587 // Draw the scoreboard
2589 if (scale <= 0)
2590 scale = 0.25;
2591 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * scale;
2592
2593 entity tm;
2594 if (teamplay)
2595 {
2596 vector panel_bg_color_save = panel_bg_color;
2597 vector team_score_baseoffset;
2598 vector team_size_baseoffset;
2599 if (autocvar_hud_panel_scoreboard_team_size_position != 1) // team size not on left
2600 {
2601 // put team score to the left of scoreboard (and team size to the right)
2602 team_score_baseoffset = eY * hud_fontsize.y - eX * hud_fontsize.x * 0.5;
2603 team_size_baseoffset = eY * hud_fontsize.y + eX * hud_fontsize.x * 0.5;
2604 if (panel.current_panel_bg != "0")
2605 {
2606 team_score_baseoffset.x -= panel_bg_border;
2607 team_size_baseoffset.x += panel_bg_border;
2608 }
2609 }
2610 else
2611 {
2612 // put team score to the right of scoreboard (and team size to the left)
2613 team_score_baseoffset = eY * hud_fontsize.y + eX * hud_fontsize.x * 0.5;
2614 team_size_baseoffset = eY * hud_fontsize.y - eX * hud_fontsize.x * 0.5;
2615 if (panel.current_panel_bg != "0")
2616 {
2617 team_score_baseoffset.x += panel_bg_border;
2618 team_size_baseoffset.x -= panel_bg_border;
2619 }
2620 }
2621
2622 int team_size_total = 0;
2623 if (autocvar_hud_panel_scoreboard_team_size_position != 0) // team size not off
2624 {
2625 // calculate team size total (sum of all team sizes)
2626 for (tm = teams.sort_next; tm; tm = tm.sort_next)
2627 if (tm.team != NUM_SPECTATOR)
2628 team_size_total += tm.team_size;
2629 }
2630
2631 vector str_pos;
2632 for (tm = teams.sort_next; tm; tm = tm.sort_next)
2633 {
2634 if (tm.team == NUM_SPECTATOR)
2635 continue;
2636 if (!tm.team)
2637 continue;
2638
2640 vector rgb = Team_ColorRGB(tm.team);
2641 str = ftos(tm.(teamscores(ts_primary)));
2642 if (autocvar_hud_panel_scoreboard_team_size_position != 1) // team size not on left
2643 {
2644 // team score on the left (default)
2645 str_pos = pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5);
2646 }
2647 else
2648 {
2649 // team score on the right
2650 str_pos = pos + team_score_baseoffset + eX * (panel_size.x + hud_fontsize.x * 1.5);
2651 }
2652 drawstring(str_pos, str, hud_fontsize * 1.5, rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
2653
2654 // team size (if set to show on the side)
2655 if (autocvar_hud_panel_scoreboard_team_size_position != 0) // team size not off
2656 {
2657 // calculate the starting position for the whole team size info string
2658 str = sprintf("%d/%d", tm.team_size, team_size_total);
2660 // team size on the left
2661 str_pos = pos + team_size_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5);
2662 else
2663 // team size on the right
2664 str_pos = pos + team_size_baseoffset + eX * (panel_size.x + hud_fontsize.x * 1.5);
2665
2666 str = sprintf("%d", tm.team_size);
2667 drawstring(str_pos, str, hud_fontsize * 1.5, rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
2668 str_pos += eX * stringwidth(str, true, hud_fontsize * 1.5) + eY * hud_fontsize.y * 0.5;
2669 str = sprintf("/%d", team_size_total);
2670 drawstring(str_pos, str, hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
2671 }
2672
2673
2674 // secondary score, e.g. keyhunt
2675 if (ts_primary != ts_secondary)
2676 {
2677 str = ftos(tm.(teamscores(ts_secondary)));
2678 if (autocvar_hud_panel_scoreboard_team_size_position != 1) // team size not on left
2679 // left
2680 str_pos = pos + team_score_baseoffset - vec2(stringwidth(str, false, hud_fontsize), hud_fontsize.y * -1.5);
2681 else
2682 // right
2683 str_pos = pos + team_score_baseoffset + vec2(panel_size.x + hud_fontsize.x * 1.5, hud_fontsize.y * 1.5);
2684
2686 }
2690 else if (panel_bg_color_team > 0)
2692 else
2693 panel_bg_color = rgb;
2694 pos = Scoreboard_MakeTable(pos, tm, panel_bg_color, bg_size);
2695 }
2696 panel_bg_color = panel_bg_color_save;
2697 }
2698 else
2699 {
2700 for (tm = teams.sort_next; tm; tm = tm.sort_next)
2701 if (tm.team != NUM_SPECTATOR)
2702 break;
2703
2704 // display it anyway
2705 pos = Scoreboard_MakeTable(pos, tm, panel_bg_color, bg_size);
2706 }
2707
2708 // if the name column is too small, try to compress all other field titles
2711
2712 // draw scoreboard spectators before accuracy and item stats
2714 pos = Scoreboard_Spectators_Draw(pos);
2715
2716 // draw accuracy and item stats
2718 pos = Scoreboard_AccuracyStats_Draw(pos, panel_bg_color, bg_size);
2720 pos = Scoreboard_ItemStats_Draw(pos, panel_bg_color, bg_size);
2721
2722 // draw scoreboard spectators after accuracy and item stats and before rankings
2724 pos = Scoreboard_Spectators_Draw(pos);
2725
2726 if (MUTATOR_CALLHOOK(ShowRankings))
2727 {
2728 string ranktitle = M_ARGV(0, string);
2730 float conversion_factor = GetSpeedUnitFactor(autocvar_hud_speed_unit);
2732 {
2733 string name;
2735 str = "";
2736 if (race_speedaward)
2737 {
2739 str = sprintf(_("Speed award: %d%s ^7(%s^7)"), race_speedaward * conversion_factor, unit, name);
2740 str = strcat(str, " / ");
2741 }
2743 str = strcat(str, sprintf(_("All-time fastest: %d%s ^7(%s^7)"), race_speedaward_alltimebest * conversion_factor, unit, name));
2745 pos.y += 1.25 * hud_fontsize.y; // line height + line spacing
2746 }
2747 pos = Scoreboard_Rankings_Draw(pos, ranktitle, playerslots[player_localnum], panel_bg_color, bg_size);
2748 }
2749 else
2750 rankings_cnt = 0;
2751
2752 // draw scoreboard spectators after rankings
2754 pos = Scoreboard_Spectators_Draw(pos);
2755
2756 pos = Scoreboard_MapStats_Draw(pos, panel_bg_color, bg_size);
2757
2758 // draw scoreboard spectators after mapstats
2760 pos = Scoreboard_Spectators_Draw(pos);
2761
2762
2763 // print information about respawn status
2764 float respawn_time = STAT(RESPAWN_TIME);
2765 if (!intermission && respawn_time)
2766 {
2767 if (respawn_time < 0)
2768 {
2769 // a negative number means we are awaiting respawn, time value is still the same
2770 respawn_time *= -1; // remove mark now that we checked it
2771
2772 if (respawn_time < time) // it happens for a few frames when server is respawning the player
2773 str = ""; // draw an empty string to not change suddenly scoreboard_bottom
2774 else
2775 str = sprintf(_("^1Respawning in ^3%s^1..."),
2778 :
2780 )
2781 );
2782 }
2783 else if (time < respawn_time)
2784 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"),
2787 :
2789 )
2790 );
2791 else if (time >= respawn_time)
2792 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey(_("jump"), "+jump"));
2793
2794 pos.y += 1.2 * hud_fontsize.y;
2796 }
2797
2798 pos.y += hud_fontsize.y;
2799 if (scoreboard_fade_alpha < 1)
2801 else if (pos.y != scoreboard_bottom)
2802 {
2803 if (pos.y > scoreboard_bottom)
2804 scoreboard_bottom = min(pos.y, scoreboard_bottom + frametime * 10 * (pos.y - scoreboard_top));
2805 else
2806 scoreboard_bottom = max(pos.y, scoreboard_bottom - frametime * 10 * (pos.y - scoreboard_top));
2807 }
2808
2809 if (rankings_cnt)
2810 {
2811 if (scoreboard_fade_alpha == 1)
2812 {
2813 if (scoreboard_bottom > 0.95 * vid_conheight)
2815 else if (scoreboard_bottom + 1.25 * hud_fontsize.y < 0.95 * vid_conheight)
2817 }
2819 }
2820}
#define MUTATOR_CALLHOOK(id,...)
Definition base.qh:143
float height
Definition bobbing.qc:3
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
entity active_minigame
bool HUD_MinigameMenu_IsOpened()
void drawpic_tiled(vector pos, string pic, vector sz, vector area, vector color, float theAlpha, float drawflag)
Definition draw.qc:24
#define drawcolorcodedstring(position, text, scale, alpha, flag)
Definition draw.qh:30
#define drawstring(position, text, scale, rgb, alpha, flag)
Definition draw.qh:27
#define drawpic_aspect_skin(pos, pic, sz, color, theAlpha, drawflag)
Definition draw.qh:78
#define drawpic(position, pic, size, rgb, alpha, flag)
Definition draw.qh:21
#define draw_beginBoldFont()
Definition draw.qh:4
vector drawfontscale
Definition draw.qh:3
#define drawfill(position, size, rgb, alpha, flag)
Definition draw.qh:36
#define draw_endBoldFont()
Definition draw.qh:5
float autocvar_con_notifysize
Definition chat.qh:10
float autocvar_con_notify
Definition chat.qh:9
string GetSpeedUnit(int speed_unit)
Definition main.qc:1122
void Release_Common_Keys()
Definition main.qc:477
bool SetTeam(entity o, int Team)
Definition main.qc:348
float GetSpeedUnitFactor(int speed_unit)
Definition main.qc:1109
entity playerslots[255]
Definition main.qh:84
int srv_maxplayers
Definition main.qh:126
float grecordtime[RANKINGS_CNT]
Definition main.qh:82
float RANKINGS_RECEIVED_CNT
Definition main.qh:79
bool ready_waiting
Definition main.qh:143
entity players
Definition main.qh:57
vector hud_fontsize
Definition main.qh:77
string gametype_custom_name
Definition main.qh:44
#define getcommandkey(cmd_name, command)
Definition main.qh:137
int spectatee_status
the -1 disables HUD panels before CSQC receives necessary data
Definition main.qh:197
string grecordholder[RANKINGS_CNT]
Definition main.qh:81
bool warmup_stage
Definition main.qh:120
entity gametype
Definition main.qh:43
float team_count
Definition main.qh:59
entity teams
Definition main.qh:58
bool campaign
Definition main.qh:122
#define colormapPaletteColor(c, isPants)
Definition color.qh:5
#define M_ARGV(x, type)
Definition events.qh:17
#define scores_label(this)
Definition scores.qh:146
entity PlayerScoreField
Definition scores.qh:140
#define MAX_TEAMSCORE
Definition scores.qh:149
#define IS_DECREASING(x)
Definition scores.qh:138
#define teamscores_flags(i)
Definition scores.qh:156
#define teamscores_label(i)
Definition scores.qh:154
#define scores_flags(this)
Definition scores.qh:147
const int SFL_ALLOW_HIDE
Allow a column to be hidden (do not automatically add it even if it is a sorting key)
Definition scores.qh:112
#define MAX_SCORE
Definition scores.qh:3
const int SFL_NOT_SORTABLE
Definition scores.qh:124
const int SFL_SORT_PRIO_MASK
Definition scores.qh:135
const int SFL_SORT_PRIO_SECONDARY
Scoring priority (NOTE: PRIMARY is used for fraglimit) NOTE: SFL_SORT_PRIO_SECONDARY value must be lo...
Definition scores.qh:133
#define IS_INCREASING(x)
Definition scores.qh:137
#define SFL_ZERO_IS_WORST
Definition scores.qh:127
const int SFL_SORT_PRIO_PRIMARY
Definition scores.qh:134
int rounds_played
Definition stats.qh:377
float isGametypeInFilter(Gametype gt, float tp, float ts, string pattern)
Definition util.qc:1087
string textShortenToWidth(string theText, float maxWidth, vector theFontSize, textLengthUpToWidth_widthFunction_t tw)
Definition util.qc:1071
string ScoreString(int pFlags, float pValue, int rounds_played)
Definition util.qc:423
string translate_key(string key)
Definition util.qc:1512
string mi_shortname
Definition util.qh:126
string strtolower(string s)
#define TIME_ENCODED_TOSTRING(n, compact)
Definition util.qh:96
#define LABEL(id)
Definition compiler.qh:34
const int RANKINGS_CNT
Definition constants.qh:31
ERASEABLE string count_ordinal(int interval)
Definition counting.qh:66
#define count_seconds_decs(time, decs)
Definition counting.qh:55
#define count_seconds(time)
Definition counting.qh:56
#define count_minutes(time)
Definition counting.qh:47
const float DRAWFLAG_NORMAL
float maxclients
float frametime
float player_localnum
float time
float player_localentnum
float intermission
#define stringwidth
float death_time
#define strstrofs
#define strlen
#define tokenizebyseparator
int entcs_GetHandicapLevel(int i)
Definition ent_cs.qh:97
string entcs_GetName(int i)
Definition ent_cs.qh:151
const int ENTCS_SPEC_PURE
Definition ent_cs.qh:74
int wants_join
Definition ent_cs.qh:73
int entcs_GetTeam(int i)
Definition ent_cs.qh:133
int entcs_GetScoreTeam(int i)
Same as entcs_GetTeam, but returns -1 for no team in teamplay.
Definition ent_cs.qh:141
entity CSQCModel_server2csqc(int i)
Definition cl_model.qc:314
int entcs_GetWantsJoin(int i)
Definition ent_cs.qh:106
int entcs_GetClientColors(int i)
Definition ent_cs.qh:115
int entcs_GetSpecState(int i)
Definition ent_cs.qh:81
Weapons
Definition guide.qh:113
void HUD_Panel_LoadCvars()
Definition hud.qc:215
vector HUD_GetFontsize(string cvarname)
Definition hud.qc:112
void HUD_Scale_Enable()
Definition hud.qc:91
void HUD_Scale_Disable()
Definition hud.qc:84
float panel_fade_alpha
Definition hud.qh:421
vector panel_size
Definition hud.qh:163
float panel_fg_alpha
Definition hud.qh:169
const int S_SHIFT
Definition hud.qh:129
int hudShiftState
Definition hud.qh:128
const int S_ALT
Definition hud.qh:131
float autocvar__menu_alpha
Definition hud.qh:187
float panel_bg_padding
Definition hud.qh:174
bool hud_draw_maximized
Definition hud.qh:69
float panel_bg_border
Definition hud.qh:172
float panel_bg_color_team
Definition hud.qh:167
bool QuickMenu_IsOpened()
Definition quickmenu.qc:291
int weapon_accuracy[REGISTRY_MAX(Weapons)]
Definition hud.qh:111
int autocvar_hud_speed_unit
Definition hud.qh:207
#define HUD_Panel_DrawBg()
Definition hud.qh:55
PlayerScoreField ps_secondary
Definition hud.qh:117
float current_player
Definition hud.qh:185
PlayerScoreField ps_primary
Definition hud.qh:117
int ts_secondary
Definition hud.qh:118
vector panel_pos
Definition hud.qh:162
float panel_bg_alpha
Definition hud.qh:170
vector panel_bg_color
Definition hud.qh:165
vector mousepos
Definition hud.qh:103
int ts_primary
Definition hud.qh:118
const int S_TAB
Definition hud.qh:132
entity panel
Definition hud.qh:147
const int S_CTRL
Definition hud.qh:130
bool HUD_Radar_Clickable()
Definition radar.qc:26
#define HUD_Write_Cvar(cvar)
Definition hud_config.qh:40
bool autocvar__hud_configure
Definition hud_config.qh:3
float hud_configure_menu_open
Definition hud_config.qh:22
ERASEABLE string CTX(string s)
Definition i18n.qh:45
#define itos(i)
Definition int.qh:6
#define IL_EACH(this, cond, body)
Inventory g_inventory
Definition inventory.qh:42
IntrusiveList default_order_items
Definition all.qh:18
#define FOREACH(list, cond, body)
Definition iter.qh:19
float K_SHIFT
Definition keycodes.qc:22
float K_UPARROW
Definition keycodes.qc:15
float K_DOWNARROW
Definition keycodes.qc:16
float K_CTRL
Definition keycodes.qc:21
float K_RIGHTARROW
Definition keycodes.qc:18
float K_SPACE
Definition keycodes.qc:10
float K_ALT
Definition keycodes.qc:20
float K_ENTER
Definition keycodes.qc:8
float K_LEFTARROW
Definition keycodes.qc:17
float K_KP_ENTER
Definition keycodes.qc:74
float K_TAB
Definition keycodes.qc:7
float K_ESCAPE
Definition keycodes.qc:9
float K_PAUSE
Definition keycodes.qc:78
#define TC(T, sym)
Definition _all.inc:82
noref float vid_conwidth
Definition draw.qh:8
noref float vid_conheight
Definition draw.qh:9
#define STAT(...)
Definition stats.qh:82
#define LOG_HELP(...)
Definition log.qh:85
#define LOG_INFO(...)
Definition log.qh:65
#define LOG_INFOF(...)
Definition log.qh:66
string MapInfo_Type_ToText(Gametype t)
Definition mapinfo.qc:661
#define M_PI
Definition mathlib.qh:108
string name
Definition menu.qh:30
void localcmd(string command,...)
void cvar_set(string name, string value)
float isdemo()
float ceil(float f)
float stof(string val,...)
float bound(float min, float value, float max)
string substring(string s, float start, float length)
float sin(float f)
float min(float f,...)
string ftos(float f)
float floor(float f)
float tokenize(string s)
string strzone(string s)
string argv(float n)
float max(float f,...)
string string_null
Definition nil.qh:9
strcat(_("^F4Countdown stopped!"), "\n^BG", _("Teams are too unbalanced."))
#define NULL
Definition post.qh:14
float scale
Definition projectile.qc:14
float race_speedaward_alltimebest
Definition racetimer.qh:32
string race_speedaward_holder
Definition racetimer.qh:31
string race_speedaward_alltimebest_holder
Definition racetimer.qh:33
bool scoreboard_showscores_force
Definition racetimer.qh:46
float race_speedaward
Definition racetimer.qh:30
#define REGISTRY_COUNT(id)
Definition registry.qh:18
int autocvar_hud_panel_scoreboard_ping_high
float sbt_fixcolumnwidth_iconlen_playercolor
bool have_item_stats
vector Scoreboard_DrawHeader(vector pos, vector rgb, bool other_players)
float autocvar_hud_panel_scoreboard_table_fg_alpha
Definition scoreboard.qc:71
vector sbt_field_rgb
bool autocvar_hud_panel_scoreboard_itemstats
Definition scoreboard.qc:88
float sbt_field_size[MAX_SBT_FIELDS+1]
Definition scoreboard.qc:45
float autocvar_hud_panel_scoreboard_table_fieldtitle_maxwidth
Definition scoreboard.qc:73
bool scoreboard_ui_disabling
bool Scoreboard_WouldDraw()
void Cmd_Scoreboard_Help()
float autocvar_hud_panel_scoreboard_bg_teams_color_team
Definition scoreboard.qc:78
#define COLOR_MED
int sbt_num_fields
Definition scoreboard.qc:49
string Scoreboard_FixColumnWidth(int i, string str, bool init)
float sbt_highlight_alpha_self
Definition scoreboard.qc:63
float name_field_index
Definition scoreboard.qc:55
float sbt_fixcolumnwidth_iconlen_extra[2]
float scoreboard_time
float sbt_fixcolumnwidth_marginlen
bool autocvar_hud_panel_scoreboard_playerid
bool have_weapon_stats
int autocvar_hud_panel_scoreboard_itemstats_filter_mask
Definition scoreboard.qc:91
string TranslateScoresLabel(string label)
int rankings_cnt
float autocvar_hud_panel_scoreboard_team_size_position
Definition scoreboard.qc:79
float sbt_bg_alpha
Definition scoreboard.qc:58
float sbt_highlight_alpha_eliminated
Definition scoreboard.qc:64
void Scoreboard_initFieldSizes(bool compress_more)
bool autocvar_hud_panel_scoreboard_dynamichud
Definition scoreboard.qc:95
float max_namesize
Definition scoreboard.qc:54
#define COLOR_HIGH
string Scoreboard_AddPlayerId(string pl_name, entity pl)
void Scoreboard_DrawItem(vector item_pos, vector rgb, entity pl, bool is_self, int pl_number)
bool autocvar_hud_panel_scoreboard_spectators_showping
Definition scoreboard.qc:99
string autocvar_hud_fontsize
Definition scoreboard.qc:53
#define COLOR_LOW
bool autocvar_hud_panel_scoreboard_accuracy_nocolors
Definition scoreboard.qc:84
void PrintScoresLabels()
bool autocvar_hud_panel_scoreboard_others_showscore
Definition scoreboard.qc:98
float autocvar_hud_panel_scoreboard_minwidth
vector autocvar_hud_panel_scoreboard_ping_low_color
PlayerScoreField sbt_field[MAX_SBT_FIELDS+1]
Definition scoreboard.qc:44
bool autocvar_hud_panel_scoreboard_scores_per_round
int Scoreboard_CompareScore(int vl, int vr, int f)
int rankings_start_column
void Scoreboard_UpdatePlayerPos(entity player)
void Scoreboard_InitScores()
int rankings_columns
int rankings_rows
float HUD_Scoreboard_InputEvent(float bInputType, float nPrimary, float nSecondary)
float autocvar_hud_panel_scoreboard_spectators_position
Definition scoreboard.qc:80
bool autocvar_hud_panel_scoreboard_table_highlight
Definition scoreboard.qc:74
float autocvar_hud_panel_scoreboard_itemstats_showdelay_minpos
Definition scoreboard.qc:93
bool autocvar_hud_panel_scoreboard_spectators_aligned
float autocvar_hud_panel_scoreboard_table_bg_alpha
Definition scoreboard.qc:69
void Scoreboard_UI_Enable(int mode)
string autocvar_hud_panel_scoreboard_playerid_prefix
const int SBT_FIELD_ICON_EXTRA_COUNT
void Cmd_Scoreboard_SetFields(int argc)
float Scoreboard_ComparePlayerScores(entity left, entity right)
float autocvar_hud_panel_scoreboard_table_highlight_alpha
Definition scoreboard.qc:75
void Scoreboard_Draw()
float sbt_field_title_maxwidth_factor
Definition scoreboard.qc:51
void Scoreboard_UpdatePlayerTeams()
int autocvar_hud_panel_scoreboard_ping_low
#define PING_LOW
vector Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size)
string sbt_field_icon_extra[SBT_FIELD_ICON_EXTRA_COUNT]
#define SCOREBOARD_DEFAULT_COLUMNS
float autocvar_hud_panel_scoreboard_respawntime_decimals
Definition scoreboard.qc:68
vector Scoreboard_MapStats_Draw(vector pos, vector rgb, vector bg_size)
int autocvar_hud_panel_scoreboard_itemstats_filter
Definition scoreboard.qc:90
vector sbt_field_icon_playercolor_rgb[3]
float autocvar_hud_panel_scoreboard_table_bg_scale
Definition scoreboard.qc:70
vector MapStats_DrawKeyValue(vector pos, string key, string value)
float Scoreboard_CompareTeamScores(entity left, entity right)
float sbt_highlight_alpha
Definition scoreboard.qc:62
bool autocvar_hud_panel_scoreboard_itemstats_doublerows
Definition scoreboard.qc:89
vector sbt_field_icon_extra_rgb[SBT_FIELD_ICON_EXTRA_COUNT]
#define PING_HIGH
const int MAX_SBT_FIELDS
Definition scoreboard.qc:42
float sbt_fg_alpha_self
Definition scoreboard.qc:60
float autocvar_hud_panel_scoreboard_fadeoutspeed
Definition scoreboard.qc:67
bool Scoreboard_ItemStats_WouldDraw(float ypos)
float sbt_field_title_condense_factor[MAX_SBT_FIELDS+1]
Definition scoreboard.qc:47
int sb_field_sizes_init
Definition scoreboard.qc:56
float sbt_fg_alpha
Definition scoreboard.qc:59
vector Scoreboard_Spectators_Draw(vector pos)
vector autocvar_hud_panel_scoreboard_ping_medium_color
float average_accuracy
void HUD_Scoreboard_UI_Disable()
float sbt_fixcolumnwidth_len
float autocvar_hud_panel_scoreboard_table_highlight_alpha_self
Definition scoreboard.qc:76
vector autocvar_hud_panel_scoreboard_ping_high_color
void Scoreboard_Draw_Export(int fh)
Definition scoreboard.qc:22
bool is_item_filtered(entity it)
vector Scoreboard_AccuracyStats_Draw(vector pos, vector rgb, vector bg_size)
void HUD_Scoreboard_UI_Disable_Instantly()
float autocvar_hud_panel_scoreboard_fadeinspeed
Definition scoreboard.qc:66
float autocvar_hud_panel_scoreboard_table_fg_alpha_self
Definition scoreboard.qc:72
bool autocvar_hud_panel_scoreboard_accuracy
Definition scoreboard.qc:82
float autocvar_hud_panel_scoreboard_accuracy_showdelay_minpos
Definition scoreboard.qc:86
void Scoreboard_UpdateTeamPos(entity Team)
#define PING_MED
string Scoreboard_Fraglimit_Draw(float limit, bool is_leadlimit)
bool autocvar_hud_panel_scoreboard_accuracy_doublerows
Definition scoreboard.qc:83
#define SCO_LABEL(strlabel, label, padding, help)
float sbt_field_title_maxwidth
Definition scoreboard.qc:50
float autocvar_hud_panel_scoreboard_maxheight
Definition scoreboard.qc:97
string autocvar_hud_panel_scoreboard_playerid_suffix
string Label_getInfo(string label, int mode)
vector Scoreboard_ItemStats_Draw(vector pos, vector rgb, vector bg_size)
int autocvar_hud_panel_scoreboard_ping_medium
float autocvar_hud_panel_scoreboard_table_highlight_alpha_eliminated
Definition scoreboard.qc:77
float sbt_field_title_width[MAX_SBT_FIELDS+1]
Definition scoreboard.qc:48
bool sbt_highlight
Definition scoreboard.qc:61
bool Scoreboard_AccuracyStats_WouldDraw(float ypos)
string Scoreboard_GetField(entity pl, PlayerScoreField field, bool per_round)
string Scoreboard_GetName(entity pl)
string sbt_field_title[MAX_SBT_FIELDS+1]
Definition scoreboard.qc:46
float autocvar_hud_panel_scoreboard_accuracy_showdelay
Definition scoreboard.qc:85
vector Scoreboard_DrawOthers(vector item_pos, vector rgb, int this_team, entity excluded_pl, entity pl, int pl_number)
vector Scoreboard_Rankings_Draw(vector pos, string ranktitle, entity pl, vector rgb, vector bg_size)
string sbt_field_icon_playercolor[3]
float autocvar_hud_panel_scoreboard_itemstats_showdelay
Definition scoreboard.qc:92
int scoreboard_selected_panel
Definition scoreboard.qh:44
float scoreboard_right
Definition scoreboard.qh:24
#define GET_NEXTMAP()
Definition scoreboard.qh:12
int SB_PANEL_FIRST
Definition scoreboard.qh:51
entity scoreboard_selected_player
Definition scoreboard.qh:46
float scoreboard_acc_fade_alpha
Definition scoreboard.qh:18
string autocvar_scoreboard_columns
Definition scoreboard.qh:6
bool autocvar_cl_deathscoreboard
Definition scoreboard.qh:4
int scoreboard_selected_columns_layout
Definition scoreboard.qh:48
int ts_min
Definition scoreboard.qh:27
int scoreboard_ui_enabled
Definition scoreboard.qh:40
entity scoreboard_selected_team
Definition scoreboard.qh:47
float scoreboard_top
Definition scoreboard.qh:21
float scoreboard_left
Definition scoreboard.qh:23
int SB_PANEL_SCOREBOARD
Definition scoreboard.qh:52
bool scoreboard_active
Definition scoreboard.qh:16
float scoreboard_fade_alpha
Definition scoreboard.qh:17
bool sb_showscores
Definition scoreboard.qh:14
int SB_PANEL_MAX
Definition scoreboard.qh:54
int SB_PANEL_RANKINGS
Definition scoreboard.qh:53
int numplayers
Definition scoreboard.qh:26
float scoreboard_bottom
Definition scoreboard.qh:22
float autocvar_hud_panel_scoreboard_namesize
Definition scoreboard.qh:7
float scoreboard_selected_panel_time
Definition scoreboard.qh:45
int ts_max
team size
Definition scoreboard.qh:27
float scoreboard_itemstats_fade_alpha
Definition scoreboard.qh:19
float autocvar_cl_deathscoreboard_delay
Definition scoreboard.qh:5
vector
Definition self.qh:92
float respawn_time
Definition client.qh:321
#define SORT_SWAP(a, b)
Swap two neighbours in a sortlist.
Definition sortlist.qh:14
#define SHUTDOWN(func)
before shutdown
Definition static.qh:49
ERASEABLE string ColorTranslateRGB(string s)
Definition string.qh:196
#define strfree(this)
Definition string.qh:59
#define strcpy(this, s)
Definition string.qh:52
ERASEABLE string ftos_decimals_percentage(float number, int decimals)
converts a percentage to a string with the indicated number of decimals
Definition string.qh:480
float stringwidth_colors(string s, vector theSize)
Definition string.qh:30
int prev_team
string Static_Team_ColorName(int teamid)
Definition teams.qh:103
const int NUM_SPECTATOR
Definition teams.qh:23
vector Team_ColorRGB(int teamid)
Definition teams.qh:76
int Team_IndexToTeam(int index)
Converts team index into team value.
Definition teams.qh:169
bool teamplay
Definition teams.qh:59
const vector eY
Definition vector.qh:45
const vector eX
Definition vector.qh:44
#define vec2(...)
Definition vector.qh:90
WepSet WepSet_GetFromStat_InMap()
Definition all.qc:83
WepSet WepSet_GetFromStat()
Definition all.qc:79
const int WEP_FIRST
Definition all.qh:326
const int WEP_FLAG_MUTATORBLOCKED
Definition weapon.qh:219
vector WepSet
Definition weapon.qh:14
const int WEP_FLAG_HIDDEN
Definition weapon.qh:216
const int WEP_TYPE_OTHER
Definition weapon.qh:211
vector Accuracy_GetColor(float accuracy)
Definition weapons.qc:64
void Accuracy_LoadColors()
Definition weapons.qc:54
float autocvar_hud_panel_weapons_aspect
Definition weapons.qh:14