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>
13#include <common/mapinfo.qh>
15#include <common/net_linked.qh>
16#include <common/scores.qh>
17#include <common/stats.qh>
18#include <common/teams.qh>
20
21// Scoreboard (#24)
22
24{
25 // allow saving cvars that aesthetically change the panel into hud skin files
26 HUD_Write_Cvar("hud_panel_scoreboard_fadeinspeed");
27 HUD_Write_Cvar("hud_panel_scoreboard_fadeoutspeed");
28 HUD_Write_Cvar("hud_panel_scoreboard_minimal");
29 HUD_Write_Cvar("hud_panel_scoreboard_respawntime_decimals");
30 HUD_Write_Cvar("hud_panel_scoreboard_table_bg_alpha");
31 HUD_Write_Cvar("hud_panel_scoreboard_table_bg_scale");
32 HUD_Write_Cvar("hud_panel_scoreboard_table_fg_alpha");
33 HUD_Write_Cvar("hud_panel_scoreboard_table_fg_alpha_self");
34 HUD_Write_Cvar("hud_panel_scoreboard_table_highlight");
35 HUD_Write_Cvar("hud_panel_scoreboard_table_highlight_alpha");
36 HUD_Write_Cvar("hud_panel_scoreboard_table_highlight_alpha_self");
37 HUD_Write_Cvar("hud_panel_scoreboard_table_highlight_alpha_eliminated");
38 HUD_Write_Cvar("hud_panel_scoreboard_bg_teams_color_team");
39 HUD_Write_Cvar("hud_panel_scoreboard_accuracy_min_aspect");
40 HUD_Write_Cvar("hud_panel_scoreboard_accuracy_nocolors");
41 HUD_Write_Cvar("hud_panel_scoreboard_itemstats_min_aspect");
42 HUD_Write_Cvar("hud_panel_scoreboard_spectators_position");
43}
44
46
55
60
68
84
90
95float autocvar_hud_panel_scoreboard_itemstats_showdelay = 2.2; // slightly more delayed than accuracy
97
99
109
111
113
114SHUTDOWN(scoreboard)
115{
117 cvar_set("hud_panel_scoreboard_scores_per_round", "0");
118}
119
120// log_all false: returns translated label
121// log_all true: prints name and description of all the labels
122string Label_getInfo(string label, bool log_all)
123{
124 switch (label)
125 {
126 #define SCO_LABEL(strlabel, label, padding, help) \
127 case label: \
128 if (!log_all) \
129 return CTX(strlabel); \
130 LOG_HELP("^3", label, padding, "^7", help); \
131 /* fallthrough when logging all labels */
132
133 default:
134 if (!log_all)
135 break;
136 // else fallthrough
137 SCO_LABEL(_("SCO^bckills"), "bckills", " ", _("Number of ball carrier kills"));
138 SCO_LABEL(_("SCO^bctime"), "bctime", " ", _("Total amount of time holding the ball in Keepaway"));
139 SCO_LABEL(_("SCO^caps"), "caps", " ", _("How often a flag (CTF) or a key (Key Hunt) was captured"));
140 SCO_LABEL(_("SCO^captime"), "captime", " ", _("Time of fastest capture (CTF)"));
141 SCO_LABEL(_("SCO^deaths"), "deaths", " ", _("Number of deaths"));
142 SCO_LABEL(_("SCO^destructions"), "destructions", " ", _("Number of keys destroyed by pushing them into the void"));
143 SCO_LABEL(_("SCO^damage dealt"), "dmg", " ", _("The total damage dealt"));
144 SCO_LABEL(_("SCO^damage taken"), "dmgtaken", " ", _("The total damage taken"));
145 SCO_LABEL(_("SCO^drops"), "drops", " ", _("Number of flag drops"));
146 SCO_LABEL(_("SCO^fastest"), "fastest", " ", _("Time of fastest lap (Race/CTS)"));
147 SCO_LABEL(_("SCO^faults"), "faults", " ", _("Number of faults committed"));
148 SCO_LABEL(_("SCO^fckills"), "fckills", " ", _("Number of flag carrier kills"));
149 SCO_LABEL(_("SCO^fps"), "fps", " ", _("Frames per second"));
150 SCO_LABEL(_("SCO^frags"), "frags", " ", _("Number of kills minus suicides"));
151 SCO_LABEL(_("SCO^generators"), "generators", " ", _("Number of generators destroyed"));
152 SCO_LABEL(_("SCO^goals"), "goals", " ", _("Number of goals scored"));
153 SCO_LABEL(_("SCO^hunts"), "hunts", " ", _("Number of hunts (Survival)"));
154 SCO_LABEL(_("SCO^kckills"), "kckills", " ", _("Number of key carrier kills"));
155 SCO_LABEL(_("SCO^k/d"), "kd", " ", _("The kill-death ratio"));
156 SCO_LABEL(_("SCO^kdr"), "kdr", " ", _("The kill-death ratio"));
157 SCO_LABEL(_("SCO^kdratio"), "kdratio", " ", _("The kill-death ratio"));
158 SCO_LABEL(_("SCO^kills"), "kills", " ", _("Number of kills"));
159 SCO_LABEL(_("SCO^laps"), "laps", " ", _("Number of laps finished (Race/CTS)"));
160 SCO_LABEL(_("SCO^lives"), "lives", " ", _("Number of lives (LMS)"));
161 SCO_LABEL(_("SCO^losses"), "losses", " ", _("Number of times a key was lost"));
162 SCO_LABEL(_("SCO^name"), "name", " ", _("Player name"));
163 SCO_LABEL(_("SCO^nick"), "nick", " ", _("Player name"));
164 SCO_LABEL(_("SCO^objectives"), "objectives", " ", _("Number of objectives destroyed"));
165 SCO_LABEL(_("SCO^pickups"), "pickups", " ", _("How often a flag (CTF), a key (Key Hunt), or a ball (Keepaway) was picked up"));
166 SCO_LABEL(_("SCO^ping"), "ping", " ", _("Ping time"));
167 SCO_LABEL(_("SCO^pl"), "pl", " ", _("Packet loss"));
168 SCO_LABEL(_("SCO^pushes"), "pushes", " ", _("Number of players pushed into the void"));
169 SCO_LABEL(_("SCO^rank"), "rank", " ", _("Player rank"));
170 SCO_LABEL(_("SCO^returns"), "returns", " ", _("Number of flag returns"));
171 SCO_LABEL(_("SCO^revivals"), "revivals", " ", _("Number of revivals"));
172 SCO_LABEL(_("SCO^rounds won"), "rounds", " ", _("Number of rounds won"));
173 SCO_LABEL(_("SCO^rounds played"), "rounds_pl", " ", _("Number of rounds played"));
174 SCO_LABEL(_("SCO^score"), "score", " ", _("Total score"));
175 SCO_LABEL(_("SCO^skill"), "skill", " ", _("Player skill"));
176 SCO_LABEL(_("SCO^suicides"), "suicides", " ", _("Number of suicides"));
177 SCO_LABEL(_("SCO^sum"), "sum", " ", _("Number of kills minus deaths"));
178 SCO_LABEL(_("SCO^survivals"), "survivals", " ", _("Number of survivals"));
179 SCO_LABEL(_("SCO^takes"), "takes", " ", _("Number of domination points taken (Domination)"));
180 SCO_LABEL(_("SCO^teamkills"), "teamkills", " ", _("Number of teamkills"));
181 SCO_LABEL(_("SCO^ticks"), "ticks", " ", _("Number of ticks (Domination)"));
182 SCO_LABEL(_("SCO^time"), "time", " ", _("Total time raced (Race/CTS)"));
183 #undef SCO_LABEL
184 }
185 return label;
186}
187
190{
192 sb_showscores = false;
193}
194
203
204// mode: 0 normal, 1 team selection
206{
207 if (isdemo())
208 return;
209
210 if (mode == 1)
211 {
213 return;
214
215 // release player's pressed keys as they aren't released elsewhere
216 // in particular jump needs to be released as it may open the team selection
217 // (when server detects jump has been pressed it sends the command to open the team selection)
221 }
222 else
223 {
224 if (scoreboard_ui_enabled == 1)
225 return;
228 }
232}
233
238float HUD_Scoreboard_InputEvent(float bInputType, float nPrimary, float nSecondary)
239{
241 return false;
242
243 if (bInputType == 3)
244 {
245 mousepos.x = nPrimary;
246 mousepos.y = nSecondary;
247 return true;
248 }
249
250 if (bInputType == 2)
251 return false;
252
253 // at this point bInputType can only be 0 or 1 (key pressed or released)
254 bool key_pressed = bInputType == 0;
255
256 // ESC to exit (TAB-ESC works too)
257 if (nPrimary == K_ESCAPE)
258 {
259 if (!key_pressed)
260 return true;
262 return true;
263 }
264
265 // block any input while a menu dialog is fading
267 {
268 hudShiftState = 0;
269 return true;
270 }
271
272 // allow console bind to work
273 string con_keys = findkeysforcommand("toggleconsole", 0);
274 int keys = tokenize(con_keys); // findkeysforcommand returns data for this
275
276 bool hit_con_bind = false;
277 for (int i = 0; i < keys; ++i)
278 if (nPrimary == stof(argv(i)))
279 hit_con_bind = true;
280
281 if (key_pressed)
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 else
289 {
290 if (nPrimary == K_ALT) hudShiftState &= ~S_ALT;
291 if (nPrimary == K_CTRL) hudShiftState &= ~S_CTRL;
292 if (nPrimary == K_SHIFT) hudShiftState &= ~S_SHIFT;
293 if (nPrimary == K_TAB) hudShiftState &= ~S_TAB;
294 }
295
296 if (nPrimary == K_TAB)
297 {
298 if (!key_pressed)
299 return true;
300 if (scoreboard_ui_enabled == 2)
301 {
303 goto uparrow_action;
304 else
305 goto downarrow_action;
306 }
307
309 {
315 }
316 else
317 {
323 }
324
326 }
327 else if (nPrimary == K_DOWNARROW)
328 {
329 if (!key_pressed)
330 return true;
331 LABEL(downarrow_action);
333 {
334 if (scoreboard_ui_enabled == 2)
335 {
336 bool scoreboard_selected_team_found = !scoreboard_selected_team;
337
338 entity curr_team = NULL;
339 for (entity tm = teams.sort_next; tm; tm = tm.sort_next)
340 {
341 if (tm.team == NUM_SPECTATOR)
342 continue;
343 curr_team = tm;
344 if (scoreboard_selected_team_found)
345 goto ok_team;
346 if (scoreboard_selected_team == tm)
347 scoreboard_selected_team_found = true;
348 }
349 LABEL(ok_team);
350 if (curr_team == scoreboard_selected_team) // loop reached the last team
351 curr_team = NULL;
352 scoreboard_selected_team = curr_team;
353 }
354 else
355 {
356 bool scoreboard_selected_player_found = !scoreboard_selected_player;
357
358 entity curr_pl = NULL;
359 for (entity pl, tm = teams.sort_next; tm; tm = tm.sort_next)
360 if (tm.team != NUM_SPECTATOR)
361 for (pl = players.sort_next; pl; pl = pl.sort_next)
362 {
363 if (pl.team != tm.team)
364 continue;
365 curr_pl = pl;
366 if (scoreboard_selected_player_found)
367 goto ok_done;
369 scoreboard_selected_player_found = true;
370 }
371 LABEL(ok_done);
372 if (curr_pl == scoreboard_selected_player) // loop reached the last player
373 curr_pl = NULL;
375 }
376 }
377 }
378 else if (nPrimary == K_UPARROW)
379 {
380 if (!key_pressed)
381 return true;
382 LABEL(uparrow_action);
384 {
385 if (scoreboard_ui_enabled == 2)
386 {
388 for (entity tm = teams.sort_next; tm; tm = tm.sort_next)
389 {
390 if (tm.team == NUM_SPECTATOR)
391 continue;
392 if (tm == scoreboard_selected_team)
393 goto ok_team2;
394 prev_team = tm;
395 }
396 LABEL(ok_team2);
398 }
399 else
400 {
401 entity prev_pl = NULL;
402 for (entity pl, tm = teams.sort_next; tm; tm = tm.sort_next)
403 if (tm.team != NUM_SPECTATOR)
404 for (pl = players.sort_next; pl; pl = pl.sort_next)
405 {
406 if (pl.team != tm.team)
407 continue;
409 goto ok_done2;
410 prev_pl = pl;
411 }
412 LABEL(ok_done2);
414 }
415 }
416 }
417 else if (nPrimary == K_RIGHTARROW)
418 {
419 if (!key_pressed)
420 return true;
423 }
424 else if (nPrimary == K_LEFTARROW)
425 {
426 if (!key_pressed)
427 return true;
430 }
431 else if (nPrimary == K_ENTER || nPrimary == K_SPACE || nPrimary == K_KP_ENTER)
432 {
433 if (!key_pressed)
434 return true;
436 {
437 if (scoreboard_ui_enabled == 2)
438 {
439 string team_name;
441 team_name = "auto";
442 else
444 localcmd(strcat("cmd join ", team_name, "\n"));
446 }
448 localcmd(sprintf("spectate %d\n", scoreboard_selected_player.sv_entnum + 1));
449 }
450 }
451 else if (nPrimary == 'c' && (hudShiftState & S_CTRL))
452 {
453 if (!key_pressed)
454 return true;
456 {
458 {
459 case 0:
462 && autocvar_scoreboard_columns != "default")
463 {
464 localcmd("scoreboard_columns_set\n"); // sets the layout saved in scoreboard_columns
466 break;
467 }
468 // fallthrough
469 case 1:
470 localcmd("scoreboard_columns_set default\n");
472 break;
473 case 2:
474 localcmd("scoreboard_columns_set all\n");
476 break;
477 }
478 }
479 }
480 else if (nPrimary == 'r' && (hudShiftState & S_CTRL))
481 {
482 if (!key_pressed)
483 return true;
485 localcmd("toggle hud_panel_scoreboard_scores_per_round\n");
486 }
487 else if (nPrimary == 't' && (hudShiftState & S_CTRL))
488 {
489 if (!key_pressed)
490 return true;
493 {
494 localcmd(strcat("commandmode tell \"", entcs_GetName(scoreboard_selected_player.sv_entnum), "^7\"\n"));
496 }
497 }
498 else if (nPrimary == 'k' && (hudShiftState & S_CTRL))
499 {
500 if (!key_pressed)
501 return true;
504 localcmd(strcat("vcall kick \"", entcs_GetName(scoreboard_selected_player.sv_entnum), "^7\"\n"));
505 }
506 else if (hit_con_bind || nPrimary == K_PAUSE)
507 return false;
508
509 return true;
510}
511
513{
515}
516string TranslateScoresLabel(string label)
517{
518 return Label_getInfo(label, false);
519}
520
522{
525
526 int f;
527 FOREACH(Scores, !(scores_flags(it) & SFL_NOT_SORTABLE),
528 {
530 if (f == SFL_SORT_PRIO_PRIMARY)
531 ps_primary = it;
533 ps_secondary = it;
534 });
535 if (ps_secondary == NULL)
537
538 for (int i = 0; i < MAX_TEAMSCORE; ++i)
539 {
541 if (f == SFL_SORT_PRIO_PRIMARY)
542 ts_primary = i;
544 ts_secondary = i;
545 }
546 if (ts_secondary == -1)
548
550}
551
552//float lastpnum;
554{
555 static float update_time;
556 if (time <= update_time)
557 return;
559
560 numplayers = 0;
561 //int num = 0;
562 for (entity tmp, pl = players.sort_next; pl; pl = pl.sort_next)
563 {
564 numplayers += pl.team != NUM_SPECTATOR;
565 //++num;
566 int Team = entcs_GetScoreTeam(pl.sv_entnum);
567 if (SetTeam(pl, Team))
568 {
569 tmp = pl.sort_prev;
571 if (tmp)
572 pl = tmp;
573 else
574 pl = players.sort_next;
575 }
576 }
577 /*
578 if (num != lastpnum)
579 print(strcat("PNUM: ", itos(num), "\n"));
580 lastpnum = num;
581 */
582
583 // update the smallest and largest team sizes
584 if (!teamplay || !teams.sort_next)
585 ts_min = ts_max = 0;
586 else
587 {
588 ts_min = 255, ts_max = 0;
589
590 for (entity tm = teams.sort_next; tm; tm = tm.sort_next)
591 {
592 if (tm.team == NUM_SPECTATOR)
593 continue;
594
595 if (ts_min > tm.team_size)
596 ts_min = tm.team_size;
597 if (ts_max < tm.team_size)
598 ts_max = tm.team_size;
599 }
600 }
601}
602
603int Scoreboard_CompareScore(int vl, int vr, int f)
604{
605 TC(int, vl); TC(int, vr); TC(int, f);
606 if (f & SFL_ZERO_IS_WORST)
607 {
608 if (vl == 0 && vr != 0)
609 return 1;
610 if (vl != 0 && vr == 0)
611 return 0;
612 }
613 if (vl > vr)
614 return IS_INCREASING(f);
615 if (vl < vr)
616 return IS_DECREASING(f);
617 return -1;
618}
619
621{
622 int vl = (left.gotscores) ? entcs_GetTeam(left.sv_entnum) : NUM_SPECTATOR;
623 int vr = (right.gotscores) ? entcs_GetTeam(right.sv_entnum) : NUM_SPECTATOR;
624
625 if (vl > vr)
626 return true;
627 if (vl < vr)
628 return false;
629
630 if (vl == NUM_SPECTATOR)
631 {
632 // FIRST the one with scores (spectators), THEN the ones without (downloaders)
633 // no other sorting
634 if (!left.gotscores && right.gotscores)
635 return true;
636 return false;
637 }
638
639 int res = Scoreboard_CompareScore(left.scores(ps_primary), right.scores(ps_primary), scores_flags(ps_primary));
640 if (res >= 0)
641 return res;
642
644 {
646 if (res >= 0)
647 return res;
648 }
649
650 FOREACH(Scores, (it != ps_primary && it != ps_secondary),
651 {
652 res = Scoreboard_CompareScore(left.scores(it), right.scores(it), scores_flags(it));
653 if (res >= 0)
654 return res;
655 });
656
657 if (left.sv_entnum < right.sv_entnum)
658 return true;
659
660 return false;
661}
662
664{
665 entity ent;
666 for (ent = player.sort_next; ent && Scoreboard_ComparePlayerScores(player, ent); ent = player.sort_next)
667 SORT_SWAP(player, ent);
668 for (ent = player.sort_prev; ent != players && Scoreboard_ComparePlayerScores(ent, player); ent = player.sort_prev)
669 SORT_SWAP(ent, player);
670}
671
673{
674 if (left.team == NUM_SPECTATOR)
675 return 1;
676 if (right.team == NUM_SPECTATOR)
677 return 0;
678
679 int fld_idx = -1;
680 for (int r, i = -2; i < MAX_TEAMSCORE; ++i)
681 {
682 if (i < 0)
683 {
684 if (fld_idx == -1)
685 fld_idx = ts_primary;
686 else if (ts_secondary == ts_primary)
687 continue;
688 else
689 fld_idx = ts_secondary;
690 }
691 else
692 {
693 fld_idx = i;
694 if (fld_idx == ts_primary || fld_idx == ts_secondary)
695 continue;
696 }
697
698 r = Scoreboard_CompareScore(left.teamscores(fld_idx), right.teamscores(fld_idx), teamscores_flags(fld_idx));
699 if (r >= 0)
700 return r;
701 }
702
703 if (left.team < right.team)
704 return true;
705
706 return false;
707}
708
710{
711 entity ent;
712 for (ent = Team.sort_next; ent && Scoreboard_CompareTeamScores(Team, ent); ent = Team.sort_next)
713 SORT_SWAP(Team, ent);
714 for (ent = Team.sort_prev; ent != teams && Scoreboard_CompareTeamScores(ent, Team); ent = Team.sort_prev)
715 SORT_SWAP(ent, Team);
716}
717
719{
720 LOG_HELP(_("You can modify the scoreboard using the ^2scoreboard_columns_set command."));
721 LOG_HELP(_("Usage:"));
722 LOG_HELP("^2scoreboard_columns_set ^3default");
723 LOG_HELP(_("^2scoreboard_columns_set ^3field1 field2 ..."));
724 LOG_HELP(_("^2scoreboard_columns_set ^7without arguments reads the arguments from the cvar scoreboard_columns"));
725 LOG_HELP(_(" ^5Note: ^7scoreboard_columns_set without arguments is executed on every map start"));
726 LOG_HELP(_("^2scoreboard_columns_set ^3expand_default ^7loads default layout and expands it into the cvar scoreboard_columns so you can edit it"));
727 LOG_HELP(_("You can use a ^3|^7 to start the right-aligned fields."));
728 LOG_HELP(_("The following field names are recognized (case insensitive):"));
729
730 LOG_HELP("");
732
733 LOG_HELP("");
734 LOG_HELP(_("Before a field you can put a + or - sign, then a comma separated list\n"
735 "of gametypes, then a slash, to make the field show up only in these\n"
736 "or in all but these gametypes. You can also specify 'all' as a\n"
737 "field to show all fields available for the current gametype."));
738
739 LOG_HELP("");
740 LOG_HELP(_("The special gametype names 'teams' and 'noteams' can be used to\n"
741 "include/exclude ALL teams/noteams gametypes."));
742
743 LOG_HELP("");
744 LOG_HELP(_("Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4"));
745 LOG_HELP(_("will display name, ping and pl aligned to the left, and the fields\n"
746 "right of the vertical bar aligned to the right."));
747 LOG_HELP(_("'field3' will only be shown in CTF, and 'field4' will be shown in all\n"
748 "other gametypes except DM."));
749}
750
751// NOTE: adding a gametype with ? to not warn for an optional field
752// make sure it's excluded in a previous exclusive rule, if any
753// otherwise the previous exclusive rule warns anyway
754// e.g. -teams,rc,cts,lms/kills ?+rc/kills
755#define SCOREBOARD_DEFAULT_COLUMNS \
756"ping pl fps skill name |" \
757" -teams,rc,cts,surv,inv,lms/kills +ft,tdm,tmayhem/kills ?+rc,inv/kills" \
758" -teams,surv,lms/deaths +ft,tdm,tmayhem/deaths" \
759" +tdm/sum" \
760" -teams,lms,rc,cts,surv,inv,ka/suicides +ft,tdm,tmayhem/suicides ?+rc,inv/suicides" \
761" -cts,dm,tdm,surv,ka,ft,mayhem,tmayhem/frags" /* tdm already has this in "score" */ \
762" +tdm,ft,dom,ons,as,tmayhem/teamkills"\
763" -rc,cts,surv,nb/dmg -rc,cts,surv,nb/dmgtaken" \
764" +surv/survivals +surv/hunts" \
765" +ctf/pickups +ctf/fckills +ctf/returns +ctf/caps +ons/takes +ons/caps" \
766" +lms/lives +lms/rank" \
767" +kh/kckills +kh/losses +kh/caps" \
768" ?+rc/laps ?+rc/time +rc,cts/fastest" \
769" +as/objectives +nb/faults +nb/goals" \
770" +ka,tka/pickups +ka,tka/bckills +ka,tka/bctime +ft/revivals" \
771" +dom/ticks +dom/takes" \
772" -lms,rc,cts,inv,nb/score"
773
775{
776 TC(int, argc);
777 if (!gametype)
778 return; // do nothing, we don't know gametype and scores yet
779
780 int i;
781 bool have_name = false, have_primary = false, have_secondary = false, have_separator = false;
782
783 // sbt_fields uses strunzone on the titles!
784 if (!sbt_field_title[0])
785 for (i = 0; i < MAX_SBT_FIELDS; ++i)
786 sbt_field_title[i] = strzone("(null)");
787
788 // TODO: re-enable with gametype-dependent cvars?
789 if (argc < 3) // no arguments provided
791
792 if (argc < 3)
794
795 if (argc == 3)
796 {
797 if (argv(2) == "default" || argv(2) == "expand_default")
798 {
799 if (argv(2) == "expand_default")
800 cvar_set("scoreboard_columns", SCOREBOARD_DEFAULT_COLUMNS);
802 }
803 else if (argv(2) == "all" || argv(2) == "ALL")
804 {
805 string s = "ping pl name |"; // scores without label (not really scores)
806 if (argv(2) == "ALL")
807 {
808 // scores without label
809 s = strcat(s, " ", "sum");
810 s = strcat(s, " ", "kdratio");
811 s = strcat(s, " ", "frags");
812 }
813 FOREACH(Scores, it != ps_primary && it != ps_secondary && scores_label(it) != "",
814 s = strcat(s, " ", scores_label(it)));
816 s = strcat(s, " ", scores_label(ps_secondary));
817 s = strcat(s, " ", scores_label(ps_primary));
818 argc = tokenizebyseparator(strcat("0 1 ", s), " ");
819 }
820 }
821
822
823 sbt_num_fields = 0;
824
825 hud_fontsize = HUD_GetFontsize("hud_fontsize");
826
827 string str, pattern;
828 int slash;
829 bool nocomplain;
830 for (i = 1; i < argc - 1; ++i)
831 {
832 str = argv(i+1);
833 nocomplain = false;
834 if (substring(str, 0, 1) == "?")
835 {
836 nocomplain = true;
837 str = substring(str, 1, strlen(str) - 1);
838 }
839
840 slash = strstrofs(str, "/", 0);
841 if (slash >= 0)
842 {
843 pattern = substring(str, 0, slash);
844 str = substring(str, slash + 1, strlen(str) - (slash + 1));
845
846 if (!isGametypeInFilter(gametype, teamplay, false, pattern))
847 continue;
848 }
849
850 str = strtolower(str);
852
854 switch (str)
855 {
856 // fields without a label (not networked via the score system)
857 case "ping": sbt_field[sbt_num_fields] = SP_PING; break;
858 case "pl": sbt_field[sbt_num_fields] = SP_PL; break;
859 case "name":
860 case "nick": sbt_field[sbt_num_fields] = SP_NAME; have_name = true; break;
861 case "|": sbt_field[sbt_num_fields] = SP_SEPARATOR; have_separator = true; break;
862 case "kd":
863 case "kdr":
864 case "kdratio": sbt_field[sbt_num_fields] = SP_KDRATIO; break;
865 case "sum":
866 case "diff":
867 case "k-d": sbt_field[sbt_num_fields] = SP_SUM; break;
868 case "frags": sbt_field[sbt_num_fields] = SP_FRAGS; break;
869 default: // fields with a label
870 {
871 // map alternative labels
872 if (str == "damage")
873 str = "dmg";
874 if (str == "damagetaken")
875 str = "dmgtaken";
876
877 FOREACH(Scores, str == strtolower(scores_label(it)),
878 {
879 j = it;
880 goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code"
881 });
882
883 // NOTE: can't check STAT(SHOWFPS) here, if checked too early it returns false anyway
884 if (!nocomplain && str != "fps" && str != "skill") // server can disable the fps or skill fields
885 LOG_INFOF("^1Error:^7 Unknown score field: '%s'", str);
886
888 continue;
889
890 LABEL(found)
892 if (j == ps_primary)
893 have_primary = true;
894 if (j == ps_secondary)
895 have_secondary = true;
896
897 }
898 }
901 break;
902 }
903
905 have_primary = true;
907 have_secondary = true;
909 have_secondary = true;
910 int missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name);
911
912 if (sbt_num_fields + missing < MAX_SBT_FIELDS)
913 {
914 if (!have_name)
915 {
917 for (i = sbt_num_fields; i > 0; --i)
918 {
920 sbt_field[i] = sbt_field[i-1];
921 }
923 sbt_field[0] = SP_NAME;
925 LOG_INFO("fixed missing field 'name'");
926
927 if (!have_separator)
928 {
930 for (i = sbt_num_fields; i > 1; --i)
931 {
933 sbt_field[i] = sbt_field[i-1];
934 }
935 sbt_field_title[1] = strzone("|");
936 sbt_field[1] = SP_SEPARATOR;
938 LOG_INFO("fixed missing field '|'");
939 }
940 }
941 else if (!have_separator)
942 {
944 sbt_field[sbt_num_fields] = SP_SEPARATOR;
946 LOG_INFO("fixed missing field '|'");
947 }
948 if (!have_secondary)
949 {
953 LOG_INFOF("fixed missing field '%s'", scores_label(ps_secondary));
954 }
955 if (!have_primary)
956 {
960 LOG_INFOF("fixed missing field '%s'", scores_label(ps_primary));
961 }
962 }
963
964 sbt_field[sbt_num_fields] = SP_END;
966}
967#undef SCOREBOARD_DEFAULT_COLUMNS
968
969string Scoreboard_AddPlayerId(string pl_name, entity pl)
970{
973}
974
975// MOVEUP::
977// These are drawn overlapping
978const string sbt_field_icon_playercolor[3] = { "gfx/scoreboard/playercolor_base", "gfx/scoreboard/playercolor_shirt", "gfx/scoreboard/playercolor_pants" };
981
982// These are drawn side-by-side, next to playercolor (if present),
983// filled in this order: player_ready, player_handicap, player_ignored
989{
990 int i;
991 if (!teamplay)
992 {
993 // NOTE: always adding 1024 allows saving the colormap 0 as a value != 0
994 i = (playerslots[pl.sv_entnum].colormap >= 1024)
995 ? playerslots[pl.sv_entnum].colormap - 1024 // override server-side player colors
996 : entcs_GetClientColors(pl.sv_entnum);
997
1002 }
1003 else
1005
1006 float f;
1008 if (ready_waiting && pl.ready)
1009 {
1010 f = min(1, 1 + sin(2*M_PI * time));
1011 sbt_field_icon_extra_rgb[sbt_field_icon_extra_set] = vec3(f, 1, f); // pulses from white to green
1012 sbt_field_icon_extra[sbt_field_icon_extra_set] = "gfx/scoreboard/player_ready";
1014 }
1015 if ((i = entcs_GetHandicapLevel(pl.sv_entnum)) != 0)
1016 {
1017 f = (16 - i) / 15;
1018 sbt_field_icon_extra_rgb[sbt_field_icon_extra_set] = vec3(1, f, f); // goes from white to red over level 1 to 16
1019 sbt_field_icon_extra[sbt_field_icon_extra_set] = "gfx/scoreboard/player_handicap";
1021 }
1022 if (pl.ignored)
1023 {
1025 sbt_field_icon_extra[sbt_field_icon_extra_set] = "gfx/scoreboard/player_ignored";
1027 }
1028
1029 return entcs_GetName(pl.sv_entnum);
1030}
1031
1038string Scoreboard_GetField(entity pl, PlayerScoreField field, bool per_round)
1039{
1040 float tmp, num, denom;
1041 int f;
1042 string str;
1043 sbt_field_rgb = '1 1 1';
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 #define PING_LOW autocvar_hud_panel_scoreboard_ping_low
1054 #define PING_MED autocvar_hud_panel_scoreboard_ping_medium
1055 #define PING_HIGH autocvar_hud_panel_scoreboard_ping_high
1056 #define COLOR_LOW autocvar_hud_panel_scoreboard_ping_low_color
1057 #define COLOR_MED autocvar_hud_panel_scoreboard_ping_medium_color
1058 #define COLOR_HIGH autocvar_hud_panel_scoreboard_ping_high_color
1059 if (!pl.gotscores)
1060 return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6 (Black Right-Pointing Triangle)
1061 //str = getplayerkeyvalue(pl.sv_entnum, "ping");
1062 f = pl.ping;
1063 if (f == 0)
1064 return _("N/A");
1065 // this colour selection code should match menuqc XonoticServerList_drawListBoxItem()
1066 if (f < PING_LOW)
1068 else if (f < PING_MED)
1070 else if (f < PING_HIGH)
1072 else
1074 return ftos(f);
1075 #undef PING_LOW
1076 #undef PING_MED
1077 #undef PING_HIGH
1078 #undef COLOR_LOW
1079 #undef COLOR_MED
1080 #undef COLOR_HIGH
1081
1082 case SP_PL:
1083 if (!pl.gotscores)
1084 return _("N/A");
1085 f = pl.ping_packetloss;
1086 tmp = pl.ping_movementloss;
1087 if (f == 0 && tmp == 0)
1088 return "";
1089 str = ftos(ceil(f * 100));
1090 if (tmp != 0)
1091 str = strcat(str, "~", ftos(ceil(tmp * 100)));
1092 tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl
1093 sbt_field_rgb = '1 0.5 0.5' - '0 0.5 0.5' * tmp;
1094 return str;
1095
1096 case SP_NAME:
1097 str = Scoreboard_GetName(pl);
1099 str = Scoreboard_AddPlayerId(str, pl);
1100 return str;
1101
1102 case SP_FRAGS:
1103 f = pl.(scores(SP_KILLS)) - pl.(scores(SP_SUICIDES));
1104 if (rounds_played)
1105 return sprintf("%.1f", f / rounds_played);
1106 return ftos(f);
1107
1108 case SP_KDRATIO:
1109 num = pl.(scores(SP_KILLS));
1110 denom = pl.(scores(SP_DEATHS));
1111
1112 if (denom == 0)
1113 {
1114 sbt_field_rgb = '0 1 0';
1115 if (rounds_played)
1116 str = sprintf("%.1f", num / rounds_played);
1117 else
1118 str = sprintf("%d", num);
1119 }
1120 else if (num <= 0)
1121 {
1122 sbt_field_rgb = '1 0 0';
1123 if (rounds_played)
1124 str = sprintf("%.2f", num / (denom * rounds_played));
1125 else
1126 str = sprintf("%.1f", num / denom);
1127 }
1128 else
1129 {
1130 if (rounds_played)
1131 str = sprintf("%.2f", num / (denom * rounds_played));
1132 else
1133 str = sprintf("%.1f", num / denom);
1134 }
1135 return str;
1136
1137 case SP_SUM:
1138 f = pl.(scores(SP_KILLS)) - pl.(scores(SP_DEATHS));
1139
1140 if (f > 0)
1141 sbt_field_rgb = '0 1 0';
1142 else if (f == 0)
1143 sbt_field_rgb = '1 1 1';
1144 else
1145 sbt_field_rgb = '1 0 0';
1146 if (rounds_played)
1147 return sprintf("%.1f", f / rounds_played);
1148 return ftos(f);
1149
1150 case SP_SKILL:
1151 tmp = pl.(scores(SP_SKILL));
1152 switch (tmp)
1153 {
1154 case -1: return "...";
1155 case -2: return _("N/A");
1156 default: return ftos(tmp);
1157 }
1158
1159 case SP_FPS:
1160 tmp = pl.(scores(SP_FPS));
1161 if (tmp == 0)
1162 {
1163 sbt_field_rgb = '1 1 1';
1164 return ((pl.ping == 0) ? _("N/A") : "..."); // if 0 ping, either connecting or bot (either case can't show proper score)
1165 }
1166 // <=32fps full red, 64fps to 96fps (inclusive) full yellow, >=128fps white
1167 // A little opinionated but representative of what's adequate for a competitive experience,
1168 // with consideration to the default and supported ticrates and the minimum USB polling rate.
1169 sbt_field_rgb.x = 1;
1170 sbt_field_rgb.y = bound(0, (tmp - 32) * 0.03125, 1); // 0 at 32fps, 1 at 64fps
1171 sbt_field_rgb.z = bound(0, (tmp - 96) * 0.03125, 1); // 0 at 96fps, 1 at 128fps
1172 return ftos(tmp);
1173
1174 case SP_ROUNDS_PL:
1175 return ftos(pl.(scores(field)));
1176
1177 case SP_DMG: case SP_DMGTAKEN:
1178 if (rounds_played)
1179 return sprintf("%.2f k", pl.(scores(field)) / (1000 * rounds_played));
1180 return sprintf("%.1f k", pl.(scores(field)) / 1000);
1181
1182 default: case SP_SCORE:
1183 tmp = pl.(scores(field));
1184 f = scores_flags(field);
1185 if (field == ps_primary)
1186 sbt_field_rgb = '1 1 0';
1187 else if (field == ps_secondary)
1188 sbt_field_rgb = '0 1 1';
1189 else
1190 sbt_field_rgb = '1 1 1';
1191 return ScoreString(f, tmp, rounds_played);
1192 }
1193 //return "error";
1194}
1195
1200string Scoreboard_FixColumnWidth(int i, string str, bool init)
1201{
1202 TC(int, i);
1203 vector sz;
1204 int j;
1205 float f;
1206
1208 for (j = 0; j < sbt_field_icon_extra_set; ++j)
1210
1211 for (j = 0; j < sbt_field_icon_playercolor_set; ++j)
1212 {
1213 sz = draw_getimagesize(sbt_field_icon_playercolor[j]);
1214 f = sz.x / sz.y;
1217 }
1218 // NOTE: extra icons (player_ready and player_handicap) are squares (32x32), so this may not be necessary
1219 // simply setting sbt_fixcolumnwidth_iconlen_extra* = 1 would be equivalent
1220 for (j = 0; j < sbt_field_icon_extra_set; ++j)
1221 {
1222 sz = draw_getimagesize(sbt_field_icon_extra[j]);
1223 // XONRELEASE TODO: xonotic-v0.9.0 after release remove if (sz == 0) case
1224 if (sz == '0 0 0') // it happens in previous engine versions (Xonotic 0.8.6 or lower) when the image is missing
1225 sz = '1 1 0';
1226 sbt_fixcolumnwidth_iconlen_extra[j] = sz.x / sz.y;
1227 }
1228
1231 for (j = 0; j < sbt_field_icon_extra_set; ++j)
1232 sbt_fixcolumnwidth_iconlen_extra[j] *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect
1233
1237 else
1239
1240 if (init)
1242
1243#define ADD_ICON_PADDING(val) \
1244 val += sbt_fixcolumnwidth_marginlen; \
1245 val += hud_fontsize.x * sbt_fixcolumnwidth_iconlen_playercolor; \
1246 for (j = 0; j < sbt_field_icon_extra_set; ++j) \
1247 val += hud_fontsize.x * sbt_fixcolumnwidth_iconlen_extra[j];
1248
1249 if (sbt_field[i] == SP_NAME) // name gets all remaining space
1250 {
1251 float used_space = 0;
1252 for (j = 0; j < sbt_num_fields; ++j)
1253 if (j != i && sbt_field[i] != SP_SEPARATOR)
1254 used_space += sbt_field_size[j] + hud_fontsize.x;
1255 sbt_field_size[i] = max(sbt_field_title_width[i], panel_size.x - used_space);
1256
1257 ADD_ICON_PADDING(used_space);
1258 float namesize = max(sbt_field_title_width[i], panel_size.x - used_space);
1259 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
1261
1263 }
1264 else
1265 {
1266 if (init)
1267 {
1270 if (sbt_field_size[i] && sbt_field_size[i] > title_maxwidth)
1271 sbt_field_size[i] = title_maxwidth;
1272 }
1274 }
1275
1277 if (sbt_field[i] == SP_NAME)
1279#undef ADD_ICON_PADDING
1280 if (sbt_field_size[i] < f)
1281 sbt_field_size[i] = f;
1282
1285 {
1286 float real_maxwidth = sbt_field_size[i];
1288 if (sbt_field_title_width[i] > title_maxwidth)
1289 real_maxwidth = max(sbt_field_size[i], title_maxwidth);
1291 }
1292
1293 return str;
1294}
1295
1296void Scoreboard_initFieldSizes(bool compress_more)
1297{
1298 if (compress_more)
1299 {
1300 float sbt_field_title_maxwidth_factor_prev = sbt_field_title_maxwidth_factor;
1303 {
1305 if (sbt_field_title_maxwidth_factor_prev == sbt_field_title_maxwidth_factor)
1306 return;
1307 }
1308 }
1309 else
1311
1312 for (int i = 0; i < sbt_num_fields; ++i)
1313 {
1314 if (sbt_field[i] == SP_NAME)
1315 {
1316 name_field_index = i;
1317 continue;
1318 }
1319
1320 Scoreboard_FixColumnWidth(i, "", true);
1321 }
1322
1323 // update name field size in the end as it takes remaining space
1325}
1326
1327vector Scoreboard_DrawHeader(vector pos, vector rgb, bool other_players, bool draw_text)
1328{
1329 vector column_dim = eY * panel_size.y;
1330 if (other_players)
1331 column_dim.y -= 1.25 * hud_fontsize.y;
1332 vector text_offset = eY * (1.25 - 1) / 2 * hud_fontsize.y;
1333 pos.x += hud_fontsize.x * 0.5;
1334 int i;
1335 for (i = 0; i < sbt_num_fields; ++i)
1336 {
1337 if (sbt_field[i] == SP_SEPARATOR)
1338 break;
1339 column_dim.x = sbt_field_size[i] + hud_fontsize.x;
1340 if (sbt_highlight && (i % 2))
1341 drawfill(pos - eX * hud_fontsize.x * 0.5, column_dim, '0 0 0', sbt_highlight_alpha, DRAWFLAG_NORMAL);
1342
1343 if (draw_text)
1344 {
1345 vector prev_drawfontscale = drawfontscale;
1348 drawstring(pos + text_offset, sbt_field_title[i], hud_fontsize, rgb * 1.5, sbt_fg_alpha, DRAWFLAG_NORMAL);
1350 {
1352 drawfontscale = prev_drawfontscale;
1353 }
1354 }
1355 pos.x += column_dim.x;
1356 }
1357
1358 if (sbt_field[i] == SP_SEPARATOR)
1359 {
1360 pos.x = panel_pos.x + panel_size.x - hud_fontsize.x * 0.5;
1361 vector prev_drawfontscale;
1362 float titlewidth;
1363 for (i = sbt_num_fields - 1; i > 0; --i)
1364 {
1365 if (sbt_field[i] == SP_SEPARATOR)
1366 break;
1367
1368 pos.x -= sbt_field_size[i];
1369
1370 if (sbt_highlight && !(i % 2))
1371 {
1372 column_dim.x = sbt_field_size[i] + hud_fontsize.x;
1373 drawfill(pos - eX * hud_fontsize.x * 0.5, column_dim, '0 0 0', sbt_highlight_alpha, DRAWFLAG_NORMAL);
1374 }
1375
1376 if (draw_text)
1377 {
1378 prev_drawfontscale = drawfontscale;
1379 titlewidth = stringwidth(sbt_field_title[i], false, hud_fontsize);
1381 {
1383 text_offset.x = sbt_field_size[i] - titlewidth * sbt_field_title_condense_factor[i];
1384 }
1385 else
1386 text_offset.x = sbt_field_size[i] - titlewidth;
1387 drawstring(pos + text_offset, sbt_field_title[i], hud_fontsize, rgb * 1.5, sbt_fg_alpha, DRAWFLAG_NORMAL);
1389 {
1391 drawfontscale = prev_drawfontscale;
1392 }
1393 }
1394 pos.x -= hud_fontsize.x;
1395 }
1396 }
1397
1398 pos.x = panel_pos.x;
1399 if (draw_text)
1400 pos.y += 1.25 * hud_fontsize.y;
1401 return pos;
1402}
1403
1404void Scoreboard_DrawItem(vector item_pos, vector rgb, entity pl, bool is_self, int pl_number)
1405{
1406 TC(bool, is_self); TC(int, pl_number);
1407 string str;
1408 bool is_spec = (entcs_GetSpecState(pl.sv_entnum) == ENTCS_SPEC_PURE);
1409
1410 vector h_pos = item_pos;
1411 vector h_size = vec2(panel_size.x, hud_fontsize.y * 1.25);
1412 // alternated rows highlighting
1414 {
1416 drawfill(h_pos, h_size, rgb, 0.44 * panel_fg_alpha, DRAWFLAG_NORMAL);
1417 }
1418 else if (is_self)
1420 else if (sbt_highlight && !(pl_number % 2))
1421 drawfill(h_pos, h_size, rgb, sbt_highlight_alpha, DRAWFLAG_NORMAL);
1422
1423 float fg_alpha = (is_self ? sbt_fg_alpha_self : sbt_fg_alpha);
1424
1425 vector pos = item_pos;
1426 // put a "self indicator" beside the self row, unicode U+25C0 (black left-pointing triangle)
1427 if (is_self)
1428 drawstring(pos + eX * (panel_size.x + 0.5 * hud_fontsize.x) + eY, "\xE2\x97\x80", hud_fontsize, rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
1429
1430 pos.x += hud_fontsize.x * 0.5;
1431 pos.y += (1.25 - 1) * 0.5 * hud_fontsize.y; // center text vertically
1432 vector tmp = '0 0 0';
1433 vector icon_sz;
1434 int i, j;
1435 PlayerScoreField field;
1436 for (i = 0; i < sbt_num_fields; ++i)
1437 {
1438 field = sbt_field[i];
1439 if (field == SP_SEPARATOR)
1440 break;
1441
1442 if (is_spec && field != SP_NAME && field != SP_PING)
1443 {
1444 pos.x += sbt_field_size[i] + hud_fontsize.x;
1445 continue;
1446 }
1448 str = Scoreboard_FixColumnWidth(i, str, false);
1449
1450 pos.x += sbt_field_size[i] + hud_fontsize.x;
1451
1452 if (field == SP_NAME)
1453 {
1456 for (j = 0; j < sbt_field_icon_extra_set; ++j)
1458 if (teamplay)
1459 {
1460 int wants_join = entcs_GetWantsJoin(pl.sv_entnum);
1461 if (wants_join)
1462 {
1463 vector tmcolor = Team_ColorRGB(Team_IndexToTeam(wants_join)) * min(1, 1 + sin(2*M_PI * time));
1464 drawstring(pos - tmp, "(Q)", hud_fontsize, tmcolor, sbt_fg_alpha, DRAWFLAG_NORMAL);
1465 tmp.x -= stringwidth("(Q) ", false, hud_fontsize);
1466 }
1467 }
1468 drawcolorcodedstring(pos - tmp, str, hud_fontsize, fg_alpha, DRAWFLAG_NORMAL);
1469 }
1470 else
1471 {
1473 drawstring(pos - tmp, str, hud_fontsize, sbt_field_rgb, fg_alpha, DRAWFLAG_NORMAL);
1474 }
1475
1476 tmp.x = sbt_field_size[i] + hud_fontsize.x;
1478 for (j = 0; j < sbt_field_icon_playercolor_set; ++j)
1480 for (j = 0; j < sbt_field_icon_extra_set; ++j)
1481 {
1482 tmp.x -= icon_sz.x;
1484 drawpic(pos - tmp, sbt_field_icon_extra[j], icon_sz, sbt_field_icon_extra_rgb[j], fg_alpha, DRAWFLAG_NORMAL);
1485 }
1486 }
1487
1488 if (sbt_field[i] == SP_SEPARATOR)
1489 {
1490 pos.x = item_pos.x + panel_size.x - hud_fontsize.x * 0.5;
1491 for (i = sbt_num_fields-1; i > 0; --i)
1492 {
1493 field = sbt_field[i];
1494 if (field == SP_SEPARATOR)
1495 break;
1496
1497 if (is_spec && field != SP_NAME && field != SP_PING)
1498 {
1499 pos.x -= sbt_field_size[i] + hud_fontsize.x;
1500 continue;
1501 }
1502
1504 str = Scoreboard_FixColumnWidth(i, str, false);
1505
1506 if (field == SP_NAME)
1507 {
1508 tmp.x = sbt_fixcolumnwidth_len; // left or right aligned? let's put it right...
1509 drawcolorcodedstring(pos - tmp, str, hud_fontsize, fg_alpha, DRAWFLAG_NORMAL);
1510 }
1511 else
1512 {
1513 tmp.x = sbt_fixcolumnwidth_len;
1514 drawstring(pos - tmp, str, hud_fontsize, sbt_field_rgb, fg_alpha, DRAWFLAG_NORMAL);
1515 }
1516
1517 tmp.x = sbt_field_size[i];
1519 for (j = 0; j < sbt_field_icon_playercolor_set; ++j)
1521 for (j = 0; j < sbt_field_icon_extra_set; ++j)
1522 {
1523 tmp.x -= icon_sz.x;
1525 drawpic(pos - tmp, sbt_field_icon_extra[j], icon_sz, sbt_field_icon_extra_rgb[j], fg_alpha, DRAWFLAG_NORMAL);
1526 }
1527 pos.x -= sbt_field_size[i] + hud_fontsize.x;
1528 }
1529 }
1530
1531 if (pl.eliminated)
1533}
1534
1535vector Scoreboard_DrawOthers(vector item_pos, vector rgb, int this_team, entity excluded_pl, entity pl, int pl_number)
1536{
1537 int i = 0;
1538 vector h_pos = item_pos;
1539 vector h_size = vec2(panel_size.x, hud_fontsize.y * 1.25);
1540
1541 bool complete = (this_team == NUM_SPECTATOR);
1542
1543 if (!complete && sbt_highlight && !(pl_number % 2))
1544 drawfill(h_pos, h_size, rgb, sbt_highlight_alpha, DRAWFLAG_NORMAL);
1545
1546 vector pos = item_pos;
1547 pos.x += hud_fontsize.x * 0.5;
1548 pos.y += (1.25 - 1) / 2 * hud_fontsize.y; // center text vertically
1549
1550 float width_limit = item_pos.x + panel_size.x - hud_fontsize.x;
1551 if (!complete)
1552 width_limit -= stringwidth("...", false, hud_fontsize);
1554 static float max_name_width = 0;
1555 string field = "";
1556 float fieldsize = 0;
1557 float min_fieldsize = 0;
1558 float fieldpadding = hud_fontsize.x * 0.25;
1559 if (this_team == NUM_SPECTATOR)
1560 {
1562 min_fieldsize = stringwidth("999", false, hud_fontsize);
1563 }
1565 min_fieldsize = stringwidth("99", false, hud_fontsize);
1566 for (i = 0; pl; pl = pl.sort_next)
1567 {
1568 if (pl.team != this_team)
1569 continue;
1570 if (pl == excluded_pl)
1571 continue;
1572
1573 field = "";
1574 if (this_team == NUM_SPECTATOR)
1575 {
1578 }
1581
1582 string str = entcs_GetName(pl.sv_entnum);
1584 str = Scoreboard_AddPlayerId(str, pl);
1585
1586 // ignored_size includes icon and padding
1587 float ignored_size = (pl.ignored ? hud_fontsize.y + hud_fontsize.x * 0.25 : 0);
1588 int wants_join = (teamplay ? entcs_GetWantsJoin(pl.sv_entnum) : 0);
1589 float queued_size = (wants_join ? stringwidth("(Q) ", false, hud_fontsize) : 0);
1590 str = textShortenToWidth(str, namesize - ignored_size - queued_size, hud_fontsize, stringwidth_colors);
1591 float column_width = stringwidth(str, true, hud_fontsize) + ignored_size + queued_size;
1593 {
1594 if (column_width > max_name_width)
1595 max_name_width = column_width;
1596 column_width = max_name_width;
1597 }
1598 if (field != "")
1599 {
1600 fieldsize = stringwidth(field, false, hud_fontsize);
1601 column_width += hud_fontsize.x * 0.25 + max(fieldsize, min_fieldsize) + 2 * fieldpadding;
1602 }
1603
1604 if (pos.x + column_width > width_limit)
1605 {
1606 ++i;
1607 if (!complete)
1608 {
1609 drawstring(pos, "...", hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
1610 break;
1611 }
1612 else
1613 {
1614 pos.x = item_pos.x + hud_fontsize.x * 0.5;
1615 pos.y += hud_fontsize.y * 1.25;
1616 }
1617 }
1618
1620 {
1622 {
1623 h_size.x = column_width + hud_fontsize.x * 0.25;
1624 h_size.y = hud_fontsize.y;
1625 drawfill(pos - hud_fontsize.x * 0.25 * eX, h_size, rgb, 0.44 * panel_fg_alpha, DRAWFLAG_NORMAL);
1626 }
1627 }
1628
1629 vector name_pos = pos;
1631 name_pos.x += max(fieldsize, min_fieldsize) + 2 * fieldpadding + hud_fontsize.x * 0.25;
1632 if (pl.ignored)
1633 {
1634 drawpic(name_pos, "gfx/scoreboard/player_ignored", '1 1 0' * hud_fontsize.y, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
1635 name_pos.x += ignored_size;
1636 }
1637 if (wants_join)
1638 {
1639 vector tmcolor = Team_ColorRGB(Team_IndexToTeam(wants_join)) * min(1, 1 + sin(2*M_PI * time));
1640 drawstring(name_pos, "(Q)", hud_fontsize, tmcolor, sbt_fg_alpha, DRAWFLAG_NORMAL);
1641 name_pos.x += queued_size;
1642 }
1644
1645 if (field != "")
1646 {
1647 h_size.x = max(fieldsize, min_fieldsize) + 2 * fieldpadding;
1648 h_size.y = hud_fontsize.y;
1649 vector field_pos = pos;
1651 field_pos.x += column_width - h_size.x;
1652 if (sbt_highlight)
1653 drawfill(field_pos, h_size, '1 1 1', sbt_highlight_alpha, DRAWFLAG_NORMAL);
1654 field_pos.x += fieldpadding + (max(fieldsize, min_fieldsize) - fieldsize) * 0.5;
1656 }
1657 if (pl.eliminated)
1658 {
1659 h_size.x = column_width + hud_fontsize.x * 0.25;
1660 h_size.y = hud_fontsize.y;
1661 drawfill(pos - hud_fontsize.x * 0.25 * eX, h_size, '0 0 0', sbt_highlight_alpha_eliminated, DRAWFLAG_NORMAL);
1662 }
1663 pos.x += column_width;
1664 pos.x += hud_fontsize.x;
1665 }
1666 return vec2(item_pos.x, item_pos.y + i * hud_fontsize.y * 1.25);
1667}
1668
1670{
1671 // if tm == NULL, we're drawing the header table (minimal scoreboard)
1672 int max_players = 999;
1673 int tm_size = (tm ? tm.team_size : 0);
1675 {
1677 if (teamplay)
1678 {
1680 height -= (panel_bg_padding * 2) * (team_count + 1) // - padding
1681 + hud_fontsize.y * 1.25; // - header
1682 else
1683 height -= (panel_bg_padding * 2 + hud_fontsize.y * 1.25) * team_count // - padding and header
1684 + hud_fontsize.y * (team_count - 1); // - spacing between tables
1685 height /= team_count;
1686 }
1687 else
1688 height -= panel_bg_padding * 2; // - padding
1689 max_players = floor(height / (hud_fontsize.y * 1.25));
1690 if (max_players <= 1)
1691 max_players = 1;
1692 if (max_players == tm_size)
1693 max_players = 999;
1694 }
1695
1696 bool draw_header = !(autocvar_hud_panel_scoreboard_minimal && teamplay && tm);
1697
1698 panel_pos = pos;
1699 panel_size.y = 1.25 * hud_fontsize.y * (draw_header + bound((tm ? 1 : 0), tm_size, max_players));
1701
1702 vector scoreboard_selected_hl_pos = pos;
1703 vector scoreboard_selected_hl_size = '0 0 0';
1704 scoreboard_selected_hl_size.x = scoreboard_right - scoreboard_left;
1705 scoreboard_selected_hl_size.y = panel_size.y;
1706
1708
1709 vector end_pos = panel_pos + eY * panel_size.y;
1711 end_pos.y += 0.5 * hud_fontsize.y;
1712 if (panel.current_panel_bg != "0")
1713 end_pos.y += panel_bg_border * 2;
1714
1715 if (panel_bg_padding)
1716 {
1717 panel_pos += '1 1 0' * panel_bg_padding;
1718 panel_size -= '2 2 0' * panel_bg_padding;
1719 }
1720
1721 pos = panel_pos;
1722 vector tmp = vec2(panel_size.x, 1.25 * hud_fontsize.y);
1723
1724 // rounded header
1725 if (draw_header)
1726 {
1727 if (sbt_bg_alpha)
1728 drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', sbt_bg_alpha, DRAWFLAG_NORMAL);
1729 pos.y += 1.25 * hud_fontsize.y;
1730 tmp.y = panel_size.y - 1.25 * hud_fontsize.y;
1731 }
1732
1733 // table background
1734 if (sbt_bg_alpha && tm)
1735 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL);
1736
1737 // highlight columns, and print header row if draw_header
1738 pos = Scoreboard_DrawHeader(panel_pos, rgb, (max_players < tm_size), draw_header);
1739
1740 if (tm)
1741 {
1742 // fill the table and draw the rows
1743 bool is_self = false;
1744 bool self_shown = false;
1745 int i = 0;
1747 for (entity pl = players.sort_next; pl; pl = pl.sort_next)
1748 {
1749 if (pl.team != tm.team)
1750 continue;
1751 if (i == max_players - 2 && pl != me
1752 && !self_shown && me.team == tm.team)
1753 {
1754 Scoreboard_DrawItem(pos, rgb, me, true, i);
1755 self_shown = true;
1756 pos.y += 1.25 * hud_fontsize.y;
1757 ++i;
1758 }
1759 if (i >= max_players - 1)
1760 {
1761 pos = Scoreboard_DrawOthers(pos, rgb, tm.team, (self_shown ? me : NULL), pl, i);
1762 break;
1763 }
1764 is_self = (pl.sv_entnum == current_player);
1765 Scoreboard_DrawItem(pos, rgb, pl, is_self, i);
1766 if (is_self)
1767 self_shown = true;
1768 pos.y += 1.25 * hud_fontsize.y;
1769 ++i;
1770 }
1771
1774 {
1775 float hl_alpha = (scoreboard_ui_enabled == 2) ? 0.2 : 0.3 * max(0, (1 - (time - scoreboard_selected_panel_time) * 2));
1776 hl_alpha *= panel_fg_alpha;
1777 if (hl_alpha)
1778 drawfill(scoreboard_selected_hl_pos, scoreboard_selected_hl_size, '0.75 0.75 0.75' + 0.25 * rgb, hl_alpha, DRAWFLAG_NORMAL);
1779 }
1780 }
1781
1782 panel_size.x += panel_bg_padding * 2; // restore initial width
1783 return end_pos;
1784}
1785
1787{
1789 return false;
1791 if (mask <= 0)
1792 return false;
1793 if (it.instanceOfArmor || it.instanceOfHealth)
1794 {
1795 switch (floor(mask) % 10) // health-armor mask
1796 {
1797 default: return false;
1798 case 4: if (it == ITEM_HealthMega || it == ITEM_ArmorMega) return true; // else fallthrough
1799 case 3: if (it == ITEM_HealthBig || it == ITEM_ArmorBig) return true; // else fallthrough
1800 case 2: if (it == ITEM_HealthMedium || it == ITEM_ArmorMedium) return true; // else fallthrough
1801 case 1: if (it == ITEM_HealthSmall || it == ITEM_ArmorSmall) return true; // else fallthrough
1802 }
1803 }
1804 if (it.instanceOfAmmo)
1805 return (floor(mask / 10) % 10) == 1; // ammo mask
1806
1807 return false;
1808}
1809
1812{
1813 if (MUTATOR_CALLHOOK(DrawScoreboardItemStats))
1814 return false;
1816 return false;
1817
1820 && !intermission)
1821 return false;
1822
1823 if (!have_item_stats)
1824 {
1825 int q;
1827 {
1828 q = g_inventory.inv_items[it.m_id];
1829 //q = 1; // debug: display all items
1830 if (q)
1831 {
1832 have_item_stats = true;
1833 break;
1834 }
1835 });
1836 if (!have_item_stats)
1837 return false;
1838 }
1839
1840 return true;
1841}
1842
1845{
1846 if (MUTATOR_CALLHOOK(DrawScoreboardAccuracy))
1847 return false;
1849 return false;
1850
1853 && !intermission)
1854 return false;
1855
1856 if (!have_weapon_stats)
1857 {
1858 FOREACH(Weapons, it != WEP_Null,
1859 {
1860 if (weapon_accuracy[i - WEP_FIRST] >= 0)
1861 {
1862 have_weapon_stats = true;
1863 break;
1864 }
1865 });
1866 if (!have_weapon_stats)
1867 return false;
1868 }
1869
1870 return true;
1871}
1872
1875{
1876 deathscoreboard = false;
1878 {
1880 {
1881 if (scoreboard_fade_alpha == 0)
1883 return false;
1884 }
1886 {
1888 return false;
1889 }
1890 return true;
1891 }
1892 else if (MUTATOR_CALLHOOK(DrawScoreboard))
1893 return false;
1894 else if (QuickMenu_IsOpened())
1895 return false;
1896 else if (HUD_Radar_Clickable())
1897 return false;
1898 else if (sb_showscores) // set by +showscores engine command
1899 return true;
1900 else if (intermission == 1)
1901 return true;
1902 else if (intermission == 2)
1903 return false;
1904 else if (spectatee_status != -1 && STAT(HEALTH) <= 0 && autocvar_cl_deathscoreboard && !MUTATOR_CALLHOOK(DrawDeathScoreboard)
1907 {
1908 if (gametype.m_1v1) // display all info if 1v1 even if dead - only 2 players, lots of room
1909 deathscoreboard = true;
1910 return true;
1911 }
1912 else if (scoreboard_showscores_force || MUTATOR_CALLHOOK(DrawScoreboard_Force))
1913 return true;
1914 return false;
1915}
1916
1917vector Scoreboard_ItemStats_Draw(vector pos, vector rgb, vector bg_size, int item_rows, int item_columns, float item_height, float item_height_just_item, bool merged)
1918{
1920
1921 if (!autocvar_hud_panel_scoreboard_minimal) // don't draw label if minimal
1922 {
1924 pos.y += 1.25 * hud_fontsize.y;
1925 if (panel.current_panel_bg != "0")
1926 pos.y += panel_bg_border;
1927 }
1928
1929 panel_pos = pos;
1930 panel_size.y = item_height * item_rows;
1932
1933 if (!merged) // don't draw background, accuracy stats already did (and made it large enough)
1934 {
1935 float panel_bg_alpha_save = panel_bg_alpha;
1938 panel_bg_alpha = panel_bg_alpha_save;
1939 }
1940
1941 vector end_pos = panel_pos + eY * (panel_size.y + 0.5 * hud_fontsize.y);
1942 if (panel.current_panel_bg != "0") // add irrespective of `merged` - need to end a table regardless
1943 end_pos.y += panel_bg_border * 2;
1944
1945 if (panel_bg_padding)
1946 {
1947 panel_pos += '1 1 0' * panel_bg_padding;
1948 panel_size -= '2 2 0' * panel_bg_padding;
1949 }
1950
1951 pos = panel_pos;
1952 float item_width = panel_size.x / item_columns;
1953
1954 if (sbt_bg_alpha)
1955 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, panel_size, rgb, sbt_bg_alpha * scoreboard_itemstats_fade_alpha, DRAWFLAG_NORMAL);
1956
1957 if (sbt_highlight)
1958 {
1959 int i;
1960 // column highlighting
1961 for (i = 0; i < item_columns; ++i)
1962 if (i % 2 == 0)
1963 drawfill(pos + eX * item_width * i, vec2(item_width, item_height * item_rows), '0 0 0', sbt_highlight_alpha * scoreboard_itemstats_fade_alpha, DRAWFLAG_NORMAL);
1964 // row highlighting
1965 for (i = 0; i < item_rows; ++i)
1966 drawfill(pos + eY * (item_height_just_item + item_height * i), vec2(panel_size.x, hud_fontsize.y), rgb, sbt_highlight_alpha * scoreboard_itemstats_fade_alpha, DRAWFLAG_NORMAL);
1967 }
1968
1969 float padding, oldposx = pos.x;
1970
1971 string s;
1972 int n, column = 0;
1974 {
1975 n = g_inventory.inv_items[it.m_id];
1976 //n = 1 + floor(i * 3 + 4.8) % 7; // debug: display a value for each item
1977 if (n <= 0)
1978 continue;
1979 drawpic_aspect_skin(pos, it.m_icon, vec2(item_width, item_height_just_item), '1 1 1', sbt_fg_alpha * scoreboard_itemstats_fade_alpha, DRAWFLAG_NORMAL);
1980 s = itos(n);
1981 padding = (item_width - stringwidth(s, false, hud_fontsize)) * 0.5;
1982 drawstring(pos + vec2(padding, item_height_just_item), s, hud_fontsize, '1 1 1', sbt_fg_alpha * scoreboard_itemstats_fade_alpha, DRAWFLAG_NORMAL);
1983
1984 if (column == item_columns - 1)
1985 {
1986 pos.x = oldposx;
1987 pos.y += item_height;
1988 column = 0;
1989 }
1990 else
1991 {
1992 pos.x += item_width;
1993 ++column;
1994 }
1995 });
1996
1997 panel_size.x += panel_bg_padding * 2; // restore initial width
1998 return end_pos;
1999}
2000
2002vector Scoreboard_AccuracyStats_Draw(vector pos, vector rgb, vector bg_size, WepSet all_weps, int wep_rows, int wep_columns, float wep_height, float wep_height_just_weapon, int item_rows, float item_height, bool merged)
2003{
2005
2006 if (autocvar_hud_panel_scoreboard_minimal) // draw average accuracy % next to the table, code copied from team score code
2007 {
2009 vector accuracy_fontsize = hud_fontsize * 1.15;
2010 string str = sprintf(_("%.*f%%"), 0, average_accuracy); // reuse transifex string
2011 vector str_pos;
2012 float y_offset = panel_bg_padding + 0.5 * hud_fontsize.y;
2013 if (autocvar_hud_panel_scoreboard_team_size_position != 1) // team size not on left
2014 {
2015 // team score on the left (default)
2016 str_pos = pos + vec2(-0.5 * hud_fontsize.x - stringwidth(str, false, accuracy_fontsize), y_offset);
2017 if (panel.current_panel_bg != "0")
2018 str_pos.x -= panel_bg_border;
2019 }
2020 else
2021 {
2022 // team score on the right
2023 str_pos = pos + vec2(0.5 * hud_fontsize.x + (panel_size.x + accuracy_fontsize.x), y_offset);
2024 if (panel.current_panel_bg != "0")
2025 str_pos.x += panel_bg_border;
2026 }
2027 str_pos.y += panel_bg_border;
2028 drawstring(str_pos, str, accuracy_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
2030 }
2031 else
2032 {
2033 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);
2034 pos.y += 1.25 * hud_fontsize.y;
2035 }
2036 if (panel.current_panel_bg != "0")
2037 pos.y += panel_bg_border;
2038
2039 panel_pos = pos;
2040 panel_size.y = wep_height * wep_rows + item_height * (merged ? item_rows : 0); // make it large enough to include ItemStats
2042
2043 {
2044 float panel_bg_alpha_save = panel_bg_alpha;
2047 panel_bg_alpha = panel_bg_alpha_save;
2048 }
2049
2050 vector end_pos;
2051 if (merged) // only add the height the weapons will take up
2052 end_pos = panel_pos + eY * (wep_height * wep_rows);
2053 else
2054 {
2055 end_pos = panel_pos + eY * (panel_size.y + 0.5 * hud_fontsize.y);
2056 if (panel.current_panel_bg != "0")
2057 end_pos.y += panel_bg_border * 2;
2058 }
2059
2060 if (panel_bg_padding)
2061 {
2062 panel_pos += '1 1 0' * panel_bg_padding;
2063 panel_size -= '2 2 0' * panel_bg_padding;
2064 }
2065
2066 pos = panel_pos;
2067 float wep_width = panel_size.x / wep_columns;
2068
2069 if (sbt_bg_alpha)
2070 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, panel_size, rgb, sbt_bg_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL);
2071
2072 if (sbt_highlight)
2073 {
2074 int i;
2075 // column highlighting
2076 for (i = 0; i < wep_columns; ++i)
2077 if (i % 2 == 0)
2078 drawfill(pos + eX * wep_width * i, vec2(wep_width, wep_height * wep_rows), '0 0 0', sbt_highlight_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL);
2079 // row highlighting
2080 for (i = 0; i < wep_rows; ++i)
2081 drawfill(pos + eY * (wep_height_just_weapon + wep_height * i), vec2(panel_size.x, hud_fontsize.y), rgb, sbt_highlight_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL);
2082 }
2083
2084 vector wep_rgb = '1 1 1';
2087
2088 float wep_alpha, padding, oldposx = pos.x;
2089
2090 string s;
2091 WepSet set;
2092 int acc, weps_with_stats = 0;
2093 int column = 0;
2094 average_accuracy = 0;
2095 FOREACH(Weapons, it != WEP_Null,
2096 {
2097 acc = weapon_accuracy[i - WEP_FIRST];
2098 set = it.m_wepset;
2099 if (acc < 0 && !(all_weps & set))
2100 continue;
2101 if (it.spawnflags & WEP_TYPE_OTHER)
2102 continue;
2103
2104 wep_alpha = (acc >= 0)
2105 ? sbt_fg_alpha
2106 : 0.2 * sbt_fg_alpha;
2107
2108 // weapon icon
2109 drawpic_aspect_skin(pos, it.model2, vec2(wep_width, wep_height_just_weapon), '1 1 1', wep_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL);
2110 // the accuracy
2111 if (acc >= 0)
2112 {
2113 ++weps_with_stats;
2114 average_accuracy += acc; // store sum of all accuracies in average_accuracy
2115
2116 s = ftos_decimals_percentage(acc, 0);
2117 padding = (wep_width - stringwidth(s, false, hud_fontsize)) * 0.5;
2119 wep_rgb = Accuracy_GetColor(acc);
2120
2121 drawstring(pos + vec2(padding, wep_height_just_weapon), s, hud_fontsize, wep_rgb, sbt_fg_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL);
2122 }
2123
2124 if (column == wep_columns - 1)
2125 {
2126 pos.x = oldposx;
2127 pos.y += wep_height;
2128 column = 0;
2129 }
2130 else
2131 {
2132 pos.x += wep_width;
2133 ++column;
2134 }
2135 });
2136
2137 if (weps_with_stats)
2138 average_accuracy = floor((average_accuracy * 100 / weps_with_stats) + 0.5);
2139
2140 panel_size.x += panel_bg_padding * 2; // restore initial width
2141 return end_pos;
2142}
2143
2145{
2146 // pre-calculate number of weapons
2148 int disowned_cnt = 0;
2149 int wep_hidden = 0;
2150 int acc;
2151 FOREACH(Weapons, it != WEP_Null,
2152 {
2153 set = it.m_wepset;
2154 if (it.spawnflags & WEP_TYPE_OTHER)
2155 {
2156 ++wep_hidden;
2157 continue;
2158 }
2159 acc = weapon_accuracy[i - WEP_FIRST];
2160 if (acc < 0 && !(all_weps & set))
2161 {
2162 if (it.spawnflags & (WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED))
2163 ++wep_hidden;
2164 else
2165 ++disowned_cnt;
2166 }
2167 });
2168 int wep_cnt = (REGISTRY_COUNT(Weapons) - 1) - disowned_cnt - wep_hidden;
2169
2170 // pre-calculate number of items
2171 disowned_cnt = 0;
2172 int uninteresting_cnt = 0;
2174 {
2175 if (is_item_filtered(it))
2176 ++uninteresting_cnt;
2177 else if (!g_inventory.inv_items[it.m_id]) // debug: display all items by changing this to `else if (true)`
2178 ++disowned_cnt;
2179 });
2180 int items_cnt = REGISTRY_COUNT(Items) - disowned_cnt - uninteresting_cnt;
2181
2182 // size pre-calculations
2183 float table_width = panel_size.x - panel_bg_padding * 2;
2184
2185 float item_height_just_item = hud_fontsize.y * 2.3;
2186 float item_height = item_height_just_item + hud_fontsize.y;
2187 int item_columns = min(floor(table_width / (autocvar_hud_panel_scoreboard_itemstats_min_aspect * item_height)), items_cnt);
2188 int item_rows = item_columns ? ceil(items_cnt / item_columns) : 0;
2189
2190 float wep_height_just_weapon = hud_fontsize.y * 2.3 / max(0.001, autocvar_hud_panel_weapons_aspect);
2191 float wep_height = wep_height_just_weapon + hud_fontsize.y;
2192 int wep_columns = min(floor(table_width / (autocvar_hud_panel_scoreboard_accuracy_min_aspect * wep_height)), wep_cnt);
2193 int wep_rows = wep_columns ? ceil(wep_cnt / wep_columns) : 0;
2194
2195
2196 float items_posy = pos.y;
2197 bool draw_accuracy = (Scoreboard_AccuracyStats_WouldDraw(pos.y) && wep_cnt > 0);
2198 if (draw_accuracy)
2199 {
2200 // Scoreboard_ItemStats_WouldDraw needs the y-pos as if Scoreboard_AccuracyStats_WouldDraw has already been called,
2201 // so we need to calculate it manually
2202 items_posy += wep_height * wep_rows;
2203 if (panel.current_panel_bg != "0")
2204 items_posy += panel_bg_border;
2206 items_posy += hud_fontsize.y * 1.75 + (panel_bg_padding + panel_bg_border) * 2;
2207 }
2208 bool draw_items = (Scoreboard_ItemStats_WouldDraw(items_posy) && items_cnt > 0);
2209 bool merged = (autocvar_hud_panel_scoreboard_minimal && draw_accuracy && draw_items);
2210 // can only merge if both are drawn, hence all this pre-calculation is necessary (determining which will draw)
2211
2212 if (draw_accuracy)
2213 pos = Scoreboard_AccuracyStats_Draw(pos, rgb, bg_size, all_weps, wep_rows, wep_columns, wep_height, wep_height_just_weapon, item_rows, item_height, merged);
2214 if (draw_items)
2215 pos = Scoreboard_ItemStats_Draw(pos, rgb, bg_size, item_rows, item_columns, item_height, item_height_just_item, merged);
2216 return pos;
2217}
2218
2219vector MapStats_DrawKeyValue(vector pos, string key, string value)
2220{
2221 float px = pos.x;
2222 pos.x += hud_fontsize.x * 0.25;
2224 pos.x = panel_pos.x + panel_size.x - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25;
2225 drawstring(pos, value, hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
2226 pos.x = px;
2227 pos.y += hud_fontsize.y;
2228
2229 return pos;
2230}
2231
2233{
2234 float stat_secrets_found, stat_secrets_total;
2235 float stat_monsters_killed, stat_monsters_total;
2236 float rows = 0;
2237 string val;
2238
2239 // get monster stats
2240 stat_monsters_killed = STAT(MONSTERS_KILLED);
2241 stat_monsters_total = STAT(MONSTERS_TOTAL);
2242
2243 // get secrets stats
2244 stat_secrets_found = STAT(SECRETS_FOUND);
2245 stat_secrets_total = STAT(SECRETS_TOTAL);
2246
2247 // get number of rows
2248 if (stat_secrets_total)
2249 ++rows;
2250 if (stat_monsters_total)
2251 ++rows;
2252
2253 // if no rows, return
2254 if (!rows)
2255 return pos;
2256
2257 // draw table header
2258 drawstring(pos + eX * panel_bg_padding, _("Map stats:"), hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
2259 pos.y += 1.25 * hud_fontsize.y;
2260 if (panel.current_panel_bg != "0")
2261 pos.y += panel_bg_border;
2262
2263 panel_pos = pos;
2264 panel_size.y = hud_fontsize.y * rows;
2267
2268 vector end_pos = panel_pos + eY * (panel_size.y + 0.5 * hud_fontsize.y);
2269 if (panel.current_panel_bg != "0")
2270 end_pos.y += panel_bg_border * 2;
2271
2272 if (panel_bg_padding)
2273 {
2274 panel_pos += '1 1 0' * panel_bg_padding;
2275 panel_size -= '2 2 0' * panel_bg_padding;
2276 }
2277
2278 pos = panel_pos;
2279
2280 if (sbt_bg_alpha)
2281 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, panel_size, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL);
2282
2283 // draw monsters
2284 if (stat_monsters_total)
2285 {
2286 val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total);
2287 pos = MapStats_DrawKeyValue(pos, _("Monsters killed:"), val);
2288 }
2289
2290 // draw secrets
2291 if (stat_secrets_total)
2292 {
2293 val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total);
2294 pos = MapStats_DrawKeyValue(pos, _("Secrets found:"), val);
2295 }
2296
2297 panel_size.x += panel_bg_padding * 2; // restore initial width
2298 return end_pos;
2299}
2300
2301vector Scoreboard_Rankings_Draw(vector pos, string ranktitle, entity pl, vector rgb, vector bg_size)
2302{
2303 int i;
2305 for (i = RANKINGS_CNT - 1; i >= 0; --i)
2306 if (grecordtime[i])
2308
2309 if (RANKINGS_RECEIVED_CNT == 0)
2310 return pos;
2311
2312 vector hl_rgb = rgb + '0.5 0.5 0.5';
2313
2314 vector scoreboard_selected_hl_pos = pos;
2315
2317 {
2319 pos.y += 1.25 * hud_fontsize.y;
2320 }
2321 if (panel.current_panel_bg != "0")
2322 pos.y += panel_bg_border;
2323
2324 vector scoreboard_selected_hl_size = '0 0 0';
2325 scoreboard_selected_hl_size.x = scoreboard_right - scoreboard_left;
2326 scoreboard_selected_hl_size.y = pos.y - scoreboard_selected_hl_pos.y;
2327
2328 panel_pos = pos;
2329
2330 float namesize = 0;
2331 for (i = 0; i < RANKINGS_RECEIVED_CNT; ++i)
2332 {
2334 if (f > namesize)
2335 namesize = f;
2336 }
2337 bool cut;
2339 {
2341 cut = true;
2342 }
2343 else
2344 cut = false;
2345
2346 float ranksize = 3 * hud_fontsize.x;
2347 float timesize = 5 * hud_fontsize.x;
2348 vector columnsize = vec2(ranksize + timesize + namesize + hud_fontsize.x, 1.25 * hud_fontsize.y);
2349 rankings_columns = max(1, floor((panel_size.x - 2 * panel_bg_padding) / columnsize.x));
2351 if (!rankings_cnt)
2352 {
2355 }
2356
2357 // expand name column to fill the entire row
2358 float available_space = (panel_size.x - 2 * panel_bg_padding - columnsize.x * rankings_columns) / rankings_columns;
2359 namesize += available_space;
2360 columnsize.x += available_space;
2361
2362 panel_size.y = rankings_rows * 1.25 * hud_fontsize.y;
2364 scoreboard_selected_hl_size.y += panel_size.y;
2365
2367
2368 vector end_pos = panel_pos + eY * (panel_size.y + 0.5 * hud_fontsize.y);
2369 if (panel.current_panel_bg != "0")
2370 end_pos.y += panel_bg_border * 2;
2371
2372 if (panel_bg_padding)
2373 {
2374 panel_pos += '1 1 0' * panel_bg_padding;
2375 panel_size -= '2 2 0' * panel_bg_padding;
2376 }
2377
2378 pos = panel_pos;
2379
2380 if (sbt_bg_alpha)
2381 drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, panel_size, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL);
2382
2383 vector text_ofs = vec2(0.5 * hud_fontsize.x, (1.25 - 1) * 0.5 * hud_fontsize.y); // center text vertically
2384 string str = "";
2385 int column = 0, j = 0;
2386 string zoned_name_self = strzone(strdecolorize(entcs_GetName(player_localnum)));
2387 int start_item = rankings_start_column * rankings_rows;
2388 for (i = start_item; i < start_item + rankings_cnt; ++i)
2389 {
2390 int t = grecordtime[i];
2391 if (t == 0)
2392 continue;
2393
2394 if (strdecolorize(grecordholder[i]) == zoned_name_self)
2395 drawfill(pos, columnsize, hl_rgb, sbt_highlight_alpha_self, DRAWFLAG_NORMAL);
2396 else if (!((j + rankings_start_column + column) & 1) && sbt_highlight)
2397 drawfill(pos, columnsize, hl_rgb, sbt_highlight_alpha, DRAWFLAG_NORMAL);
2398
2399 str = count_ordinal(i + 1);
2400 vector rank_color;
2401 switch (i)
2402 {
2403 case 0: rank_color = '0.933 0.733 0.200'; break; // 1st; gold
2404 case 1: rank_color = '0.667 0.667 0.667'; break; // 2nd; silver
2405 case 2: rank_color = '0.800 0.467 0.267'; break; // 3rd; bronze
2406 default: rank_color = '1 1 1';
2407 }
2408 drawstring(pos + text_ofs, str, hud_fontsize, rank_color, sbt_fg_alpha, DRAWFLAG_NORMAL);
2409 drawstring(pos + text_ofs + eX * ranksize, TIME_ENCODED_TOSTRING(t, true), hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL);
2411 if (cut)
2412 str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors);
2413 drawcolorcodedstring(pos + text_ofs + eX * (ranksize + timesize), str, hud_fontsize, sbt_fg_alpha, DRAWFLAG_NORMAL);
2414
2415 pos.y += 1.25 * hud_fontsize.y;
2416 ++j;
2417 if (j >= rankings_rows)
2418 {
2419 ++column;
2420 j = 0;
2421 pos.x += panel_size.x / rankings_columns;
2422 pos.y = panel_pos.y;
2423 }
2424 }
2425 strfree(zoned_name_self);
2426
2428 {
2429 float fade = max(0, (1 - (time - scoreboard_selected_panel_time) * 2));
2430 drawfill(scoreboard_selected_hl_pos, scoreboard_selected_hl_size, '1 1 1', fade * 0.44, DRAWFLAG_NORMAL);
2431 }
2432
2433 panel_size.x += panel_bg_padding * 2; // restore initial width
2434 return end_pos;
2435}
2436
2438{
2439 string str = "";
2440
2441 for (entity tm, pl = players.sort_next; pl; pl = pl.sort_next)
2442 if (pl.team == NUM_SPECTATOR)
2443 {
2444 for (tm = teams.sort_next; tm; tm = tm.sort_next)
2445 if (tm.team == NUM_SPECTATOR)
2446 break;
2447 str = strcat(_("Spectators"), sprintf(_(" (%s)"), itos(tm.team_size)));
2451 pos.y += 1.25 * hud_fontsize.y;
2452
2453 pos = Scoreboard_DrawOthers(pos, '0 0 0', pl.team, NULL, pl, 0);
2454 pos.y += 1.25 * hud_fontsize.y;
2455
2456 break;
2457 }
2458
2459 if (str != "") // if there's at least one spectator
2460 pos.y += 0.5 * hud_fontsize.y;
2461
2462 return pos;
2463}
2464
2465string Scoreboard_Fraglimit_Draw(float limit, bool is_leadlimit, bool is_last)
2466{
2469
2470 if (autocvar_hud_panel_scoreboard_minimal && !is_last) // only add the label at the end (is_last) if minimal
2471 return strcat((is_leadlimit ? "^5+" : "^5"), ScoreString(s_flags, limit, 0));
2472 return strcat("^5", sprintf((is_leadlimit ? _("+%s %s") : _("%s %s")), ScoreString(s_flags, limit, 0),
2473 (s_label == "score") ? CTX(_("SCO^points")) :
2474 (s_label == "fastest") ? "" : TranslateScoresLabel(s_label)));
2475}
2476
2477void Scoreboard_Draw(bool should_draw)
2478{
2479 if (!should_draw)
2480 return;
2482 {
2483 if (!hud_draw_maximized)
2484 return;
2485
2486 // frametime checks allow to toggle the scoreboard even when the game is paused
2488 {
2489 if (scoreboard_fade_alpha == 0)
2491 if (hud_configure_menu_open == 1)
2495 else
2497
2498 static string hud_fontsize_str;
2499 if (hud_fontsize_str != autocvar_hud_fontsize)
2500 {
2501 hud_fontsize = HUD_GetFontsize("hud_fontsize");
2502 strcpy(hud_fontsize_str, autocvar_hud_fontsize);
2504 }
2505
2506 static float scoreboard_table_fieldtitle_maxwidth_prev;
2507 if (scoreboard_table_fieldtitle_maxwidth_prev != autocvar_hud_panel_scoreboard_table_fieldtitle_maxwidth)
2508 {
2509 scoreboard_table_fieldtitle_maxwidth_prev = autocvar_hud_panel_scoreboard_table_fieldtitle_maxwidth;
2513 }
2514 }
2515 else
2516 {
2519 else
2521 }
2522
2524 {
2527 return;
2528 }
2529 }
2530 else
2532
2535 else
2537
2538 if (scoreboard_fade_alpha <= 0)
2539 return;
2542
2550
2551 // don't overlap with con_notify
2554
2556 float fixed_scoreboard_width = bound(vid_conwidth * autocvar_hud_panel_scoreboard_minwidth, vid_conwidth - excess, vid_conwidth * 0.9);
2557 scoreboard_left = 0.5 * (vid_conwidth - fixed_scoreboard_width);
2558 scoreboard_right = scoreboard_left + fixed_scoreboard_width;
2560 panel_size.x = fixed_scoreboard_width;
2561
2562 // field sizes can be initialized now after panel_size.x calculation
2564 {
2565 bool compress = (sb_field_sizes_init == 2);
2566 Scoreboard_initFieldSizes(compress);
2568 }
2569
2571
2573 vector pos = panel_pos;
2574
2576 drawfill('0 0 0', eX * vid_conwidth + eY * vid_conheight, '0 0 0', 0.7 * panel_fade_alpha, DRAWFLAG_NORMAL);
2577
2578 vector sb_gameinfo_type_fontsize, sb_gameinfo_detail_fontsize;
2579
2580 // Begin of Game Info Section
2581 sb_gameinfo_type_fontsize = hud_fontsize * 2.5;
2582 sb_gameinfo_detail_fontsize = hud_fontsize * 1.3;
2583
2584 string str;
2585 if (GET_NEXTMAP() != "")
2586 {
2587 // NOTE: nextmap is drawn before title to avoid covering title in case of long map name
2588 str = strcat("^7", _("Next map:"), " ^9", GET_NEXTMAP());
2589 drawcolorcodedstring(pos + vec2(hud_fontsize.x * 0.5, sb_gameinfo_type_fontsize.y - hud_fontsize.y * 1.25),
2591 }
2592
2593 // Game Info: Gametype
2594 if (scoreboard_ui_enabled == 2)
2595 str = _("Team Selection");
2596 else if (gametype_custom_name != "")
2598 else
2601 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);
2603
2604 pos.y += sb_gameinfo_type_fontsize.y;
2605 // Game Info: Game Detail
2606 if (scoreboard_ui_enabled == 2)
2607 {
2609 str = sprintf(_("^7Press ^3%s^7 to join the selected team"), translate_key("SPACE"));
2610 else
2611 str = sprintf(_("^7Press ^3%s^7 to auto-select a team and join"), translate_key("SPACE"));
2612 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);
2613
2614 pos.y += sb_gameinfo_detail_fontsize.y + hud_fontsize.y * 0.3;
2615 str = sprintf(_("^7Press ^3%s ^7to select a specific team"), translate_key("TAB"));
2616 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);
2617 }
2618 else
2619 {
2620 float tl = STAT(TIMELIMIT);
2621 float fl = STAT(FRAGLIMIT);
2622 float ll = STAT(LEADLIMIT);
2623 str = "";
2624 if (tl > 0)
2625 str = strcat(str, "^3", count_minutes(floor(tl)));
2626 if (!gametype.m_hidelimits)
2627 {
2628 bool draw_ll = (ll > 0 && (ll < fl || fl <= 0));
2629 if (fl > 0)
2630 {
2631 if (tl > 0)
2632 str = strcat(str, "^7 / "); // delimiter
2633 str = strcat(str, Scoreboard_Fraglimit_Draw(fl, false, !draw_ll));
2634 }
2635 if (draw_ll)
2636 {
2637 if (tl > 0 || fl > 0)
2638 {
2639 // delimiter
2640 if (STAT(LEADLIMIT_AND_FRAGLIMIT) && fl > 0)
2641 str = strcat(str, "^7 & ");
2642 else
2643 str = strcat(str, "^7 / ");
2644 }
2645 str = strcat(str, Scoreboard_Fraglimit_Draw(ll, true, true));
2646 }
2647 }
2648 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
2649 // map name and player count
2650 if (campaign)
2651 str = "";
2652 else
2653 str = sprintf(_("^5%d^7/^5%d ^7players"), numplayers, srv_maxplayers ? srv_maxplayers : maxclients);
2655 str = strcat("^2", mi_shortname, " ", str);
2656 else
2657 str = strcat("^7", _("Map:"), " ^2", mi_shortname, " ", str); // reusing "Map:" translatable string
2658 drawcolorcodedstring(pos, str, sb_gameinfo_detail_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL); // align left
2659 }
2660 // End of Game Info Section
2661
2662 pos.y += sb_gameinfo_detail_fontsize.y + hud_fontsize.y * 0.3; // space between Game Info Section and score table
2663 if (panel.current_panel_bg != "0")
2664 pos.y += panel_bg_border;
2665
2666 // Draw the scoreboard
2668 if (scale <= 0)
2669 scale = 0.25;
2670 vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * scale;
2671
2672 entity tm;
2673 if (teamplay)
2674 {
2675 vector panel_bg_color_save = panel_bg_color;
2676 vector team_score_baseoffset;
2677 vector team_size_baseoffset;
2678 float y_offset = panel_bg_padding + (autocvar_hud_panel_scoreboard_minimal ? 0 : 0.5) * hud_fontsize.y;
2679 if (autocvar_hud_panel_scoreboard_team_size_position != 1) // team size not on left
2680 {
2681 // put team score to the left of scoreboard (and team size to the right)
2682 team_score_baseoffset = vec2(-0.5 * hud_fontsize.x, y_offset);
2683 team_size_baseoffset = vec2( 0.5 * hud_fontsize.x, y_offset);
2684 if (panel.current_panel_bg != "0")
2685 {
2686 team_score_baseoffset.x -= panel_bg_border;
2687 team_size_baseoffset.x += panel_bg_border;
2688 }
2689 }
2690 else
2691 {
2692 // put team score to the right of scoreboard (and team size to the left)
2693 team_score_baseoffset = vec2( 0.5 * hud_fontsize.x, y_offset);
2694 team_size_baseoffset = vec2(-0.5 * hud_fontsize.x, y_offset);
2695 if (panel.current_panel_bg != "0")
2696 {
2697 team_score_baseoffset.x += panel_bg_border;
2698 team_size_baseoffset.x -= panel_bg_border;
2699 }
2700 }
2701
2702 int team_size_total = 0;
2703 if (autocvar_hud_panel_scoreboard_team_size_position != 0) // team size not off
2704 {
2705 // calculate team size total (sum of all team sizes)
2706 for (tm = teams.sort_next; tm; tm = tm.sort_next)
2707 if (tm.team != NUM_SPECTATOR)
2708 team_size_total += tm.team_size;
2709 }
2710
2711 if (autocvar_hud_panel_scoreboard_minimal) // draw header table first
2712 pos = Scoreboard_MakeTable(pos, NULL, '0.5 0.5 0.5', bg_size);
2713
2714 vector str_pos;
2715 for (tm = teams.sort_next; tm; tm = tm.sort_next)
2716 {
2717 if (tm.team == NUM_SPECTATOR)
2718 continue;
2719 if (!tm.team)
2720 continue;
2721
2723 vector rgb = Team_ColorRGB(tm.team);
2724 str = ftos(tm.(teamscores(ts_primary)));
2725 if (autocvar_hud_panel_scoreboard_team_size_position != 1) // team size not on left
2726 {
2727 // team score on the left (default)
2728 str_pos = pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5);
2729 }
2730 else
2731 {
2732 // team score on the right
2733 str_pos = pos + team_score_baseoffset + eX * (panel_size.x + hud_fontsize.x * 1.5);
2734 }
2735 drawstring(str_pos, str, hud_fontsize * 1.5, rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
2736
2737 // team size (if set to show on the side)
2738 if (autocvar_hud_panel_scoreboard_team_size_position != 0) // team size not off
2739 {
2740 // calculate the starting position for the whole team size info string
2741 str = sprintf("%d/%d", tm.team_size, team_size_total);
2743 // team size on the left
2744 str_pos = pos + team_size_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5);
2745 else
2746 // team size on the right
2747 str_pos = pos + team_size_baseoffset + eX * (panel_size.x + hud_fontsize.x * 1.5);
2748
2749 str = sprintf("%d", tm.team_size);
2750 drawstring(str_pos, str, hud_fontsize * 1.5, rgb, panel_fg_alpha, DRAWFLAG_NORMAL);
2751 str_pos += eX * stringwidth(str, true, hud_fontsize * 1.5) + eY * hud_fontsize.y * 0.5;
2752 str = sprintf("/%d", team_size_total);
2753 drawstring(str_pos, str, hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
2754 }
2755
2756
2757 // secondary score, e.g. keyhunt
2758 if (ts_primary != ts_secondary)
2759 {
2760 str = ftos(tm.(teamscores(ts_secondary)));
2761 if (autocvar_hud_panel_scoreboard_team_size_position != 1) // team size not on left
2762 // left
2763 str_pos = pos + team_score_baseoffset - vec2(stringwidth(str, false, hud_fontsize), hud_fontsize.y * -1.5);
2764 else
2765 // right
2766 str_pos = pos + team_score_baseoffset + vec2(panel_size.x + hud_fontsize.x * 1.5, hud_fontsize.y * 1.5);
2767
2769 }
2773 else if (panel_bg_color_team > 0)
2775 else
2776 panel_bg_color = rgb;
2777 pos = Scoreboard_MakeTable(pos, tm, panel_bg_color, bg_size);
2778 }
2779 panel_bg_color = panel_bg_color_save;
2780
2782 pos.y += 0.5 * hud_fontsize.y;
2783 }
2784 else
2785 {
2786 for (tm = teams.sort_next; tm; tm = tm.sort_next)
2787 if (tm.team != NUM_SPECTATOR)
2788 break;
2789
2790 // display it anyway
2791 pos = Scoreboard_MakeTable(pos, tm, panel_bg_color, bg_size);
2792 }
2793
2794 // if the name column is too small, try to compress all other field titles
2797
2798 // draw scoreboard spectators before accuracy and item stats
2800 pos = Scoreboard_Spectators_Draw(pos);
2801
2802 // draw accuracy and item stats
2803 if (!deathscoreboard)
2805
2806 // draw scoreboard spectators after accuracy and item stats and before rankings
2808 pos = Scoreboard_Spectators_Draw(pos);
2809
2810 // draw rankings
2811 if (MUTATOR_CALLHOOK(ShowRankings, deathscoreboard))
2812 {
2813 string ranktitle = M_ARGV(1, string);
2815 float conversion_factor = GetSpeedUnitFactor(autocvar_hud_speed_unit);
2817 {
2818 string name;
2820 str = "";
2821 if (race_speedaward)
2822 {
2824 str = sprintf(_("Speed award: %d%s ^7(%s^7)"), race_speedaward * conversion_factor, unit, name);
2825 str = strcat(str, " / ");
2826 }
2828 str = strcat(str, sprintf(_("All-time fastest: %d%s ^7(%s^7)"), race_speedaward_alltimebest * conversion_factor, unit, name));
2830 pos.y += 1.25 * hud_fontsize.y; // line height + line spacing
2831 }
2832 pos = Scoreboard_Rankings_Draw(pos, ranktitle, playerslots[player_localnum], panel_bg_color, bg_size);
2833 }
2834 else
2835 rankings_cnt = 0;
2836
2837 // draw scoreboard spectators after rankings
2839 pos = Scoreboard_Spectators_Draw(pos);
2840
2841 pos = Scoreboard_MapStats_Draw(pos, panel_bg_color, bg_size);
2842
2843 // draw scoreboard spectators after mapstats
2845 pos = Scoreboard_Spectators_Draw(pos);
2846
2847
2848 // print information about respawn status
2849 float respawn_time = STAT(RESPAWN_TIME);
2850 if (!intermission && respawn_time)
2851 {
2852 if (respawn_time < 0)
2853 {
2854 // a negative number means we are awaiting respawn, time value is still the same
2855 respawn_time *= -1; // remove mark now that we checked it
2856
2857 if (respawn_time < time) // it happens for a few frames when server is respawning the player
2858 str = ""; // draw an empty string to not change suddenly scoreboard_bottom
2859 else
2860 str = sprintf(_("^1Respawning in ^3%s^1..."), (autocvar_hud_panel_scoreboard_respawntime_decimals
2863 }
2864 else if (time < respawn_time)
2865 str = sprintf(_("You are dead, wait ^3%s^7 before respawning"), (autocvar_hud_panel_scoreboard_respawntime_decimals
2868 else if (time >= respawn_time)
2869 str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey(_("jump"), "+jump"));
2870
2871 pos.y += 1.2 * hud_fontsize.y;
2873 }
2874
2875 pos.y += hud_fontsize.y;
2876 if (scoreboard_fade_alpha < 1)
2878 else if (pos.y != scoreboard_bottom)
2879 {
2880 if (pos.y > scoreboard_bottom)
2881 scoreboard_bottom = min(pos.y, scoreboard_bottom + frametime * 10 * (pos.y - scoreboard_top));
2882 else
2883 scoreboard_bottom = max(pos.y, scoreboard_bottom - frametime * 10 * (pos.y - scoreboard_top));
2884 }
2885
2886 if (rankings_cnt)
2887 {
2888 if (scoreboard_fade_alpha == 1)
2889 {
2890 if (scoreboard_bottom > 0.95 * vid_conheight)
2892 else if (scoreboard_bottom + 1.25 * hud_fontsize.y < 0.95 * vid_conheight)
2894 }
2896 }
2897}
#define MUTATOR_CALLHOOK(name,...)
Definition base.qh:186
#define M_ARGV(idx, type)
Definition base.qh:153
#define BITS(n)
Definition bits.qh:9
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:1123
void Release_Common_Keys()
Definition main.qc:476
bool SetTeam(entity o, int Team)
Definition main.qc:347
float GetSpeedUnitFactor(int speed_unit)
Definition main.qc:1110
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
float update_time
Definition net.qh:11
#define scores_label(this)
Definition scores.qh:146
#define MAX_TEAMSCORE
Definition scores.qh:149
#define IS_DECREASING(x)
Definition scores.qh:138
entity PlayerScoreField
Definition scores.qh:140
#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:379
float isGametypeInFilter(Gametype gt, float tp, float ts, string pattern)
Definition util.qc:1189
string textShortenToWidth(string theText, float maxWidth, vector theFontSize, textLengthUpToWidth_widthFunction_t tw)
Definition util.qc:1173
string ScoreString(int pFlags, float pValue, int rounds_played)
Definition util.qc:423
string translate_key(string key)
Definition util.qc:1612
string mi_shortname
Definition util.qh:133
string strtolower(string s)
#define TIME_ENCODED_TOSTRING(n, compact)
Definition util.qh:103
#define LABEL(id)
Definition compiler.qh:34
const int RANKINGS_CNT
Definition constants.qh:32
#define count_seconds_decs(time, decs)
Definition counting.qh:121
ERASEABLE string count_ordinal(int num)
Definition counting.qh:132
#define count_seconds(time)
Definition counting.qh:122
#define count_minutes(time)
Definition counting.qh:113
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:93
string entcs_GetName(int i)
Definition ent_cs.qh:148
const int ENTCS_SPEC_PURE
real spectator
Definition ent_cs.qh:70
int wants_join
Definition ent_cs.qh:69
int entcs_GetTeam(int i)
Definition ent_cs.qh:132
int entcs_GetScoreTeam(int i)
Same as entcs_GetTeam, but returns -1 for no team in teamplay.
Definition ent_cs.qh:138
entity CSQCModel_server2csqc(int i)
Definition cl_model.qc:314
int entcs_GetWantsJoin(int i)
Definition ent_cs.qh:102
int entcs_GetClientColors(int i)
Definition ent_cs.qh:111
int entcs_GetSpecState(int i)
Definition ent_cs.qh:77
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:405
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:290
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:46
#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:7
noref float vid_conheight
Definition draw.qh:8
#define STAT(...)
Definition stats.qh:94
#define LOG_HELP(...)
Definition log.qh:83
#define LOG_INFO(...)
Definition log.qh:62
#define LOG_INFOF(...)
Definition log.qh:63
string MapInfo_Type_ToText(Gametype t)
Definition mapinfo.qc:660
#define M_PI
pi
Definition mathlib.qh:114
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
void Scoreboard_Draw(bool should_draw)
int autocvar_hud_panel_scoreboard_ping_high
float sbt_fixcolumnwidth_iconlen_playercolor
bool have_item_stats
string sbt_field_icon_extra[SBT_FIELD_ICON_EXTRA_MAX]
float autocvar_hud_panel_scoreboard_table_fg_alpha
Definition scoreboard.qc:74
vector sbt_field_rgb
bool autocvar_hud_panel_scoreboard_itemstats
Definition scoreboard.qc:91
float sbt_field_size[MAX_SBT_FIELDS+1]
Definition scoreboard.qc:48
float autocvar_hud_panel_scoreboard_table_fieldtitle_maxwidth
Definition scoreboard.qc:76
bool scoreboard_ui_disabling
bool Scoreboard_WouldDraw()
void Cmd_Scoreboard_Help()
float autocvar_hud_panel_scoreboard_bg_teams_color_team
Definition scoreboard.qc:81
#define COLOR_MED
int sbt_num_fields
Definition scoreboard.qc:52
string Scoreboard_FixColumnWidth(int i, string str, bool init)
float sbt_highlight_alpha_self
Definition scoreboard.qc:66
float name_field_index
Definition scoreboard.qc:58
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:94
string TranslateScoresLabel(string label)
int rankings_cnt
float autocvar_hud_panel_scoreboard_team_size_position
Definition scoreboard.qc:82
float sbt_bg_alpha
Definition scoreboard.qc:61
float sbt_highlight_alpha_eliminated
Definition scoreboard.qc:67
void Scoreboard_initFieldSizes(bool compress_more)
const int SBT_FIELD_ICON_EXTRA_MAX
bool autocvar_hud_panel_scoreboard_dynamichud
Definition scoreboard.qc:98
float max_namesize
Definition scoreboard.qc:57
#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
string autocvar_hud_fontsize
Definition scoreboard.qc:56
#define COLOR_LOW
bool autocvar_hud_panel_scoreboard_accuracy_nocolors
Definition scoreboard.qc:87
void PrintScoresLabels()
bool autocvar_hud_panel_scoreboard_others_showscore
string Label_getInfo(string label, bool log_all)
float autocvar_hud_panel_scoreboard_minwidth
vector autocvar_hud_panel_scoreboard_ping_low_color
vector Scoreboard_AccuracyStats_Draw(vector pos, vector rgb, vector bg_size, WepSet all_weps, int wep_rows, int wep_columns, float wep_height, float wep_height_just_weapon, int item_rows, float item_height, bool merged)
PlayerScoreField sbt_field[MAX_SBT_FIELDS+1]
Definition scoreboard.qc:47
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:83
const string sbt_field_icon_playercolor[3]
bool autocvar_hud_panel_scoreboard_table_highlight
Definition scoreboard.qc:77
float autocvar_hud_panel_scoreboard_itemstats_showdelay_minpos
Definition scoreboard.qc:96
bool autocvar_hud_panel_scoreboard_spectators_aligned
vector sbt_field_icon_extra_rgb[SBT_FIELD_ICON_EXTRA_MAX]
float autocvar_hud_panel_scoreboard_table_bg_alpha
Definition scoreboard.qc:72
void Scoreboard_UI_Enable(int mode)
string autocvar_hud_panel_scoreboard_playerid_prefix
void Cmd_Scoreboard_SetFields(int argc)
float Scoreboard_ComparePlayerScores(entity left, entity right)
float autocvar_hud_panel_scoreboard_table_highlight_alpha
Definition scoreboard.qc:78
float sbt_field_title_maxwidth_factor
Definition scoreboard.qc:54
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)
#define SCOREBOARD_DEFAULT_COLUMNS
float autocvar_hud_panel_scoreboard_respawntime_decimals
Definition scoreboard.qc:71
vector Scoreboard_MapStats_Draw(vector pos, vector rgb, vector bg_size)
int autocvar_hud_panel_scoreboard_itemstats_filter
Definition scoreboard.qc:93
vector sbt_field_icon_playercolor_rgb[3]
bool deathscoreboard
Whether the scoreboard is displaying because we're dead - hide accuracy & item stats & rankings.
float autocvar_hud_panel_scoreboard_table_bg_scale
Definition scoreboard.qc:73
vector MapStats_DrawKeyValue(vector pos, string key, string value)
string Scoreboard_Fraglimit_Draw(float limit, bool is_leadlimit, bool is_last)
float Scoreboard_CompareTeamScores(entity left, entity right)
float sbt_highlight_alpha
Definition scoreboard.qc:65
float autocvar_hud_panel_scoreboard_accuracy_min_aspect
Definition scoreboard.qc:86
#define PING_HIGH
const int MAX_SBT_FIELDS
Definition scoreboard.qc:45
float sbt_fg_alpha_self
Definition scoreboard.qc:63
float autocvar_hud_panel_scoreboard_fadeoutspeed
Definition scoreboard.qc:70
bool Scoreboard_ItemStats_WouldDraw(float ypos)
float sbt_field_title_condense_factor[MAX_SBT_FIELDS+1]
Definition scoreboard.qc:50
int sb_field_sizes_init
Definition scoreboard.qc:59
float sbt_fg_alpha
Definition scoreboard.qc:62
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:79
vector autocvar_hud_panel_scoreboard_ping_high_color
void Scoreboard_Draw_Export(int fh)
Definition scoreboard.qc:23
bool is_item_filtered(entity it)
void HUD_Scoreboard_UI_Disable_Instantly()
float autocvar_hud_panel_scoreboard_fadeinspeed
Definition scoreboard.qc:69
float autocvar_hud_panel_scoreboard_table_fg_alpha_self
Definition scoreboard.qc:75
bool autocvar_hud_panel_scoreboard_accuracy
Definition scoreboard.qc:85
float autocvar_hud_panel_scoreboard_accuracy_showdelay_minpos
Definition scoreboard.qc:89
void Scoreboard_UpdateTeamPos(entity Team)
#define PING_MED
vector Scoreboard_AccuracyAndItemStats_Draw(vector pos, vector rgb, vector bg_size)
float sbt_fixcolumnwidth_iconlen_extra[SBT_FIELD_ICON_EXTRA_MAX]
int sbt_field_icon_playercolor_set
#define SCO_LABEL(strlabel, label, padding, help)
float sbt_field_title_maxwidth
Definition scoreboard.qc:53
float autocvar_hud_panel_scoreboard_maxheight
string autocvar_hud_panel_scoreboard_playerid_suffix
#define ADD_ICON_PADDING(val)
int autocvar_hud_panel_scoreboard_ping_medium
float autocvar_hud_panel_scoreboard_table_highlight_alpha_eliminated
Definition scoreboard.qc:80
float sbt_field_title_width[MAX_SBT_FIELDS+1]
Definition scoreboard.qc:51
bool sbt_highlight
Definition scoreboard.qc:64
bool Scoreboard_AccuracyStats_WouldDraw(float ypos)
vector Scoreboard_ItemStats_Draw(vector pos, vector rgb, vector bg_size, int item_rows, int item_columns, float item_height, float item_height_just_item, bool merged)
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:49
float autocvar_hud_panel_scoreboard_accuracy_showdelay
Definition scoreboard.qc:88
vector Scoreboard_DrawOthers(vector item_pos, vector rgb, int this_team, entity excluded_pl, entity pl, int pl_number)
float autocvar_hud_panel_scoreboard_itemstats_min_aspect
Definition scoreboard.qc:92
vector Scoreboard_Rankings_Draw(vector pos, string ranktitle, entity pl, vector rgb, vector bg_size)
vector Scoreboard_DrawHeader(vector pos, vector rgb, bool other_players, bool draw_text)
bool autocvar_hud_panel_scoreboard_minimal
int sbt_field_icon_extra_set
float autocvar_hud_panel_scoreboard_itemstats_showdelay
Definition scoreboard.qc:95
int scoreboard_selected_panel
Definition scoreboard.qh:44
float scoreboard_right
Definition scoreboard.qh:24
#define GET_NEXTMAP()
Definition scoreboard.qh:12
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
minimum team size
Definition scoreboard.qh:27
int scoreboard_ui_enabled
Definition scoreboard.qh:40
const int SB_PANEL_SCOREBOARD
Definition scoreboard.qh:52
entity scoreboard_selected_team
Definition scoreboard.qh:47
float scoreboard_top
Definition scoreboard.qh:21
const int SB_PANEL_FIRST
Definition scoreboard.qh:51
float scoreboard_left
Definition scoreboard.qh:23
const int SB_PANEL_MAX
Definition scoreboard.qh:54
bool scoreboard_active
Definition scoreboard.qh:16
float scoreboard_fade_alpha
Definition scoreboard.qh:17
bool sb_showscores
set by +showscores engine command
Definition scoreboard.qh:14
const 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
maximum team size
Definition scoreboard.qh:28
float scoreboard_itemstats_fade_alpha
Definition scoreboard.qh:19
float autocvar_cl_deathscoreboard_delay
Definition scoreboard.qh:5
vector
Definition self.qh:96
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:50
ERASEABLE string ColorTranslateRGB(string s)
Definition string.qh:194
#define strfree(this)
Definition string.qh:57
#define strcpy(this, s)
Definition string.qh:51
ERASEABLE string ftos_decimals_percentage(float number, int decimals)
Converts a percentage to a string with the indicated number of decimals.
Definition string.qh:485
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:44
const vector eX
Definition vector.qh:43
#define vec2(...)
Definition vector.qh:95
#define vec3(_x, _y, _z)
Definition vector.qh:100
WepSet WepSet_GetFromStat_InMap()
Definition all.qc:84
WepSet WepSet_GetFromStat()
Definition all.qc:80
const int WEP_FIRST
Definition all.qh:344
const int WEP_FLAG_MUTATORBLOCKED
hides from impulse 99 etc. (mutators are allowed to clear this flag)
Definition weapon.qh:256
const int WEP_FLAG_HIDDEN
hides from menu
Definition weapon.qh:253
vector WepSet
Definition weapon.qh:14
const int WEP_TYPE_OTHER
not for damaging people
Definition weapon.qh:248
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