Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
scores.qc
Go to the documentation of this file.
1#include "scores.qh"
2
3#include <common/mapinfo.qh>
7#include <common/scores.qh>
8#include <common/state.qh>
9#include <common/stats.qh>
10#include <common/teams.qh>
12#include <server/client.qh>
17#include <server/world.qh>
18
29
30// returns cmp value
31int ScoreField_Compare(entity t1, entity t2, .float field, int fieldflags, int previous)
32{
33 if(fieldflags & SFL_NOT_SORTABLE) // column does not sort
34 return previous;
35 if (t1.(field) == t2.(field))
36 return previous;
37
38 if(fieldflags & SFL_ZERO_IS_WORST)
39 {
40 if (t1.(field) == 0)
41 {
42 previous = -1;
43 return previous;
44 }
45 else if (t2.(field) == 0)
46 {
47 previous = +1;
48 return previous;
49 }
50 }
51
52 if (fieldflags & SFL_LOWER_IS_BETTER)
53 previous = (t2.(field) - t1.(field));
54 else
55 previous = (t1.(field) - t2.(field));
56
57 return previous;
58}
59
60/*
61 * teamscore entities
62 */
63
64bool TeamScore_SendEntity(entity this, entity to, float sendflags)
65{
66 int i, longflags;
67
68 WriteHeader(MSG_ENTITY, ENT_CLIENT_TEAMSCORES);
69 int t = this.team - 1;
70 assert(t, eprint(this));
72
73 longflags = 0;
74 for(i = 0; i < MAX_TEAMSCORE; ++i)
75 if(this.(teamscores(i)) > 127 || this.(teamscores(i)) <= -128)
76 longflags |= BIT(i);
77
78#if MAX_TEAMSCORE <= 8
79 WriteByte(MSG_ENTITY, sendflags);
80 WriteByte(MSG_ENTITY, longflags);
81#else
82 WriteShort(MSG_ENTITY, sendflags);
83 WriteShort(MSG_ENTITY, longflags);
84#endif
85 for(i = 0; i < MAX_TEAMSCORE; ++i)
86 if(sendflags & BIT(i))
87 {
88 if(longflags & BIT(i))
89 WriteInt24_t(MSG_ENTITY, this.(teamscores(i)));
90 else
92 }
93
94 return true;
95}
96
97void TeamScore_Spawn(float t, string name)
98{
99 entity ts = new_pure(csqc_score_team);
100 ts.netname = name; // not used yet, FIXME
101 ts.team = t;
103 teamscorekeepers[t - 1] = ts;
106}
107
108float TeamScore_AddToTeam(int t, float scorefield, float score)
109{
110 entity s;
111
112 if(game_stopped)
113 score = 0;
114
115 if(!scores_initialized) return 0; // FIXME remove this when everything uses this system
116 if(t <= 0 || t >= 16)
117 {
118 if(game_stopped)
119 return 0;
120 error("Adding score to invalid team!");
121 }
122 s = teamscorekeepers[t - 1];
123 if(!s)
124 {
125 if(game_stopped)
126 return 0;
127 error("Adding score to unknown team!");
128 }
129 if(score)
130 if(teamscores_label(scorefield) != "")
131 s.SendFlags |= BIT(scorefield);
132 return (s.(teamscores(scorefield)) += score);
133}
134
135float TeamScore_Add(entity player, float scorefield, float score)
136{
137 return TeamScore_AddToTeam(player.team, scorefield, score);
138}
139
140// strict: compare others fields too besides primary and secondary
141int TeamScore_Compare(entity t1, entity t2, bool strict)
142{
143 if(!t1 || !t2) return (!t2) - !t1;
144
145 // supporting MAX_TEAMSCORE > 2 requires keeping track of primary and secondary teamscore
146 if (MAX_TEAMSCORE > 2)
147 error("MAX_TEAMSCORE > 2 not supported");
148
149 // first compare primary, then others (don't check secondary flag since there are only 2 teamscores)
150 int result = 0;
153 if (result == 0 && strict)
154 {
155 i = (i + 1) % MAX_TEAMSCORE;
157 if (result == 0)
158 result = t1.team - t2.team;
159 }
160
161 return result;
162}
163
164/*
165 * the scoreinfo entity
166 */
167
168void ScoreInfo_SetLabel_PlayerScore(PlayerScoreField i, string label, int scoreflags)
169{
170 scores_label(i) = label;
171 scores_flags(i) = scoreflags;
172 if((scoreflags & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_PRIMARY)
173 {
175 scores_flags_primary = scoreflags;
176 }
177 else if((scoreflags & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_SECONDARY)
178 {
180 scores_flags_secondary = scoreflags;
181 }
182 if(label != "")
183 {
186 }
187}
188
189void ScoreInfo_SetLabel_TeamScore(int i, string label, int scoreflags)
190{
191 teamscores_label(i) = label;
192 teamscores_flags(i) = scoreflags;
193 if((scoreflags & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_PRIMARY)
194 {
196 teamscores_flags_primary = scoreflags;
197 }
198 if(label != "")
199 {
202 }
203}
204
205bool ScoreInfo_SendEntity(entity this, entity to, int sf)
206{
207 int i;
208 WriteHeader(MSG_ENTITY, ENT_CLIENT_SCORES_INFO);
210 string gt_name = "";
212 gt_name = cvar_string(strcat("sv_vote_gametype_", loaded_gametype_custom_string, "_name"));
213 WriteString(MSG_ENTITY, gt_name);
214 FOREACH(Scores, true, {
217 });
218 for(i = 0; i < MAX_TEAMSCORE; ++i)
219 {
222 }
223 // prevent sending the welcome message again when score types are sent again because the scoring system has changed
224 // it can happen in some gametypes like Race when the qualyfing session ends and the race starts
225 bool welcome_msg_too = (!CS(to) || time < CS(to).jointime + 5);
226 WriteByte(MSG_ENTITY, welcome_msg_too);
227 // welcome message is sent here because it needs to know the gametype
228 if (welcome_msg_too)
230 return true;
231}
232
234{
236 {
237 scores_initialized.SendFlags |= 1; // force a resend
238 }
239 else
240 {
241 scores_initialized = new_pure(ent_client_scoreinfo);
243 }
244 if(teams & BIT(0))
246 if(teams & BIT(1))
248 if(teams & BIT(2))
249 TeamScore_Spawn(NUM_TEAM_3, "Yellow");
250 if(teams & BIT(3))
252}
253
254/*
255 * per-player score entities
256 */
257
258bool PlayerScore_SendEntity(entity this, entity to, float sendflags)
259{
260 WriteHeader(MSG_ENTITY, ENT_CLIENT_SCORES);
262
263 int longflags = 0;
264 FOREACH(Scores, true, {
265 int p = BIT(i % 16);
266 if (this.(scores(it)) > 127 || this.(scores(it)) <= -128)
267 longflags |= p;
268 });
269
270 WriteShort(MSG_ENTITY, sendflags);
271 WriteShort(MSG_ENTITY, longflags);
272 FOREACH(Scores, true, {
273 int p = BIT(i % 16);
274 if (sendflags & p)
275 {
276 if(longflags & p)
277 WriteInt24_t(MSG_ENTITY, this.(scores(it)));
278 else
279 WriteChar(MSG_ENTITY, this.(scores(it)));
280 }
281 });
282
283 return true;
284}
285
287{
289 || (autocvar_g_score_resetonjoin == -1 && !MUTATOR_CALLHOOK(PreferPlayerScore_Clear)))
290 return false;
291
292 entity sk = CS(player).scorekeeper;
293 FOREACH(Scores, true, {
294 if(sk.(scores(it)) != 0 && scores_label(it) != "")
295 sk.SendFlags |= BIT(i % 16);
296 if(i != SP_SKILL.m_id)
297 sk.(scores(it)) = 0;
298 });
299
300 return true;
301}
302
304{
305 entity sk;
306 int t;
307 FOREACH_CLIENTSLOT(true, {
308 sk = CS(it).scorekeeper;
309 if (!sk) continue;
310 FOREACH(Scores, true, {
311 if(sk.(scores(it)) != 0 && scores_label(it) != "")
312 sk.SendFlags |= BIT(i % 16);
313 if(i != SP_SKILL.m_id)
314 sk.(scores(it)) = 0;
315 });
316 });
317 for(t = 0; t < 16; ++t)
318 {
319 sk = teamscorekeepers[t];
320 if(!sk)
321 continue;
322 for(int j = 0; j < MAX_TEAMSCORE; ++j)
323 {
324 if(sk.(teamscores(j)) != 0 && teamscores_label(j) != "")
325 sk.SendFlags |= BIT(j);
326 sk.(teamscores(j)) = 0;
327 }
328 }
329}
330
332{
333 if(CS(player).scorekeeper)
334 error("player already has a scorekeeper");
336 sk.owner = player;
338 CS(player).scorekeeper = sk;
339}
340
342{
343 if(!CS(player).scorekeeper)
344 error("player has no scorekeeper");
345 delete(CS(player).scorekeeper);
346 CS(player).scorekeeper = NULL;
347}
348
349float PlayerScore_Add(entity player, PlayerScoreField scorefield, float score)
350{
351 bool mutator_returnvalue = MUTATOR_CALLHOOK(AddPlayerScore, scorefield, score, player);
352 score = M_ARGV(1, float);
353
354 if(!mutator_returnvalue && game_stopped)
355 {
356 score = 0;
357 }
358
359 if(!scores_initialized) return 0; // FIXME remove this when everything uses this system
360 entity s = CS(player).scorekeeper;
361 if(!s)
362 {
363 if(game_stopped)
364 return 0;
365 LOG_WARN("Adding score to unknown player!");
366 return 0;
367 }
368 if(!score)
369 {
370 return s.(scores(scorefield));
371 }
372 if(scores_label(scorefield) != "")
373 s.SendFlags |= BIT(scorefield.m_id % 16);
374 if(!warmup_stage)
376 s.(scores(scorefield)) += score;
377 MUTATOR_CALLHOOK(AddedPlayerScore, scorefield, score, player);
378 return s.(scores(scorefield));
379}
380
381float PlayerScore_Set(entity player, PlayerScoreField scorefield, float score)
382{
383 if(!scores_initialized) return 0; // FIXME remove this when everything uses this system
384 entity s = CS(player).scorekeeper;
385 if(!s)
386 {
387 if(game_stopped)
388 return 0;
389 LOG_WARN("Setting score of unknown player!");
390 return 0;
391 }
392
393 float oldscore = s.(scores(scorefield));
394 if(oldscore == score)
395 return oldscore;
396
397 if(scores_label(scorefield) != "")
398 s.SendFlags |= BIT(scorefield.m_id % 16);
399 s.(scores(scorefield)) = score;
400 return s.(scores(scorefield));
401}
402
403float PlayerTeamScore_Add(entity player, PlayerScoreField pscorefield, float tscorefield, float score)
404{
405 float r = PlayerScore_Add(player, pscorefield, score);
406 if(teamscores_entities_count) // only for teamplay
407 r = TeamScore_Add(player, tscorefield, score);
408 return r;
409}
410
411// strict: compare others fields too besides primary and secondary
412float PlayerScore_Compare(entity t1, entity t2, bool strict)
413{
414 if(!t1 || !t2) return (!t2) - !t1;
415
416 int result = 0;
418 // NOTE: if (scores_secondary) doesn't work because it's a field pointer
419 if (result == 0 && scores_flags_secondary)
421
422 if (result == 0 && strict)
423 {
424 FOREACH(Scores, true, {
426 continue;
427 if (scores_label(it) == "")
428 continue;
429 var .float f = scores(it);
430 result = ScoreField_Compare(t1, t2, f, scores_flags(it), result);
431 if (result) break;
432 });
433 if (result == 0)
434 result = t1.owner.playerid - t2.owner.playerid;
435 }
436
437 return result;
438}
439
441{
442 float c;
443 string s;
444 entity sk;
445
446 // format:
447 // gametype:P<pure>:S<slots>::plabel,plabel:tlabel,tlabel:teamid:tscore,tscore:teamid:tscore,tscore
448 // score labels always start with a symbol or with lower case
449 // so to match pure, match for :P0:
450 // to match full, match for :S0:
451
452 // NOTE can't use a single strcat because strcat concatenates max 8 strings
453 s = strcat(GetGametype(),
456 ":S", ftos(nJoinAllowed(NULL)));
457 s = strcat(s,
458 ":F", ftos(serverflags),
460 ":M", modname);
461 s = strcat(s,
463
464 entity winnerscorekeeper = NULL;
465 entity secondscorekeeper = NULL;
466
468 {
469 int t;
470
471 s = strcat(s, ":", GetTeamScoreString(0, 1));
472 for(t = 0; t < 16; ++t)
473 if(teamscorekeepers[t])
474 s = strcat(s, ":", ftos(t+1), ":", GetTeamScoreString(t+1, 1));
475
478 for(t = 0; t < 16; ++t)
479 {
480 sk = teamscorekeepers[t];
481 c = TeamScore_Compare(winnerscorekeeper, sk, true);
482 if(c < 0)
483 {
486 secondscorekeeper = winnerscorekeeper;
487 winnerscorekeeper = sk;
488 }
489 else
490 {
491 c = TeamScore_Compare(secondscorekeeper, sk, true);
492 if(c < 0)
493 {
495 secondscorekeeper = sk;
496 }
497 }
498 }
499
500 WinningConditionHelper_equality = (TeamScore_Compare(winnerscorekeeper, secondscorekeeper, false) == 0);
503
504 WinningConditionHelper_topscore = winnerscorekeeper.teamscores_primary;
505 WinningConditionHelper_secondscore = secondscorekeeper.teamscores_primary;
508
509 WinningConditionHelper_winner = NULL; // not supported in teamplay
510 WinningConditionHelper_second = NULL; // not supported in teamplay
511 }
512 else
513 {
517 sk = CS(it).scorekeeper;
518 c = PlayerScore_Compare(winnerscorekeeper, sk, true);
519 if(c < 0)
520 {
523 secondscorekeeper = winnerscorekeeper;
524 winnerscorekeeper = sk;
525 }
526 else
527 {
528 c = PlayerScore_Compare(secondscorekeeper, sk, true);
529 if(c < 0)
530 {
532 secondscorekeeper = sk;
533 }
534 }
535 });
536
537 WinningConditionHelper_equality = (PlayerScore_Compare(winnerscorekeeper, secondscorekeeper, false) == 0);
540
541 WinningConditionHelper_topscore = winnerscorekeeper.scores_primary;
542 WinningConditionHelper_secondscore = secondscorekeeper.scores_primary;
545
546 WinningConditionHelper_winnerteam = -1; // no teamplay
547 WinningConditionHelper_secondteam = -1; // no teamplay
548 }
549
551 {
553 {
556 else
558 }
559 if(player_count == 0) // special case: empty servers DO end the match at a 0:0 tie
561 }
562
564 {
566 {
569 else
571 }
572 }
573
574 if (s != worldstatus)
576
577 FOREACH_CLIENT(true, {
578 string s = "";
580 {
581 s = GetPlayerScoreString(it, 1);
582 s = strcat(s, IS_REAL_CLIENT(it) ? ":human" : ":bot");
583 if(!(IS_PLAYER(it) || INGAME_JOINED(it)))
584 s = strcat(s, ":spectator");
585 }
586 else
587 {
588 if (IS_PLAYER(it) || INGAME_JOINED(it))
589 s = GetPlayerScoreString(it, 2);
590 else
591 s = "-666";
592 }
593
594 if (s != it.clientstatus)
595 strcpy(it.clientstatus, s);
596 });
597}
598
599string GetScoreLogLabel(string label, float fl)
600{
601 if(fl & SFL_LOWER_IS_BETTER)
602 label = strcat(label, "<");
604 label = strcat(label, "!!");
606 label = strcat(label, "!");
607 return label;
608}
609
610string GetPlayerScoreString(entity pl, float shortString)
611{
612 entity sk;
613 int fl;
614 string lbl;
615
616 string out = "";
617 if(!pl)
618 {
619 // label
620 FOREACH(Scores, true, {
622 {
623 fl = scores_flags(it);
624 lbl = scores_label(it);
625 out = strcat(out, GetScoreLogLabel(lbl, fl), ",");
626 }
627 });
628 if(shortString < 2)
629 FOREACH(Scores, true, {
631 {
632 fl = scores_flags(it);
633 lbl = scores_label(it);
634 out = strcat(out, GetScoreLogLabel(lbl, fl), ",");
635 }
636 });
637 if(shortString < 1)
638 FOREACH(Scores, true, {
641 {
642 fl = scores_flags(it);
643 lbl = scores_label(it);
644 out = strcat(out, GetScoreLogLabel(lbl, fl), ",");
645 }
646 });
647 out = substring(out, 0, strlen(out) - 1);
648 }
649 else if((sk = CS(pl).scorekeeper))
650 {
651 FOREACH(Scores, true, {
653 out = strcat(out, ftos(sk.(scores(it))), ",");
654 });
655 if(shortString < 2)
656 FOREACH(Scores, true, {
658 out = strcat(out, ftos(sk.(scores(it))), ",");
659 });
660 if(shortString < 1)
661 FOREACH(Scores, true, {
664 out = strcat(out, ftos(sk.(scores(it))), ",");
665 });
666 out = substring(out, 0, strlen(out) - 1);
667 }
668 return out;
669}
670
671string GetTeamScoreString(int tm, float shortString)
672{
673 entity sk;
674 int i, fl;
675 string lbl;
676
677 string out = "";
678 if(tm == 0)
679 {
680 // label
681 for(i = 0; i < MAX_TEAMSCORE; ++i)
683 {
684 fl = teamscores_flags(i);
685 lbl = teamscores_label(i);
686 out = strcat(out, GetScoreLogLabel(lbl, fl), ",");
687 }
688 if(shortString < 2)
689 for(i = 0; i < MAX_TEAMSCORE; ++i)
691 {
692 fl = teamscores_flags(i);
693 lbl = teamscores_label(i);
694 out = strcat(out, GetScoreLogLabel(lbl, fl), ",");
695 }
696 if(shortString < 1)
697 for(i = 0; i < MAX_TEAMSCORE; ++i)
700 {
701 fl = teamscores_flags(i);
702 lbl = teamscores_label(i);
703 out = strcat(out, GetScoreLogLabel(lbl, fl), ",");
704 }
705 out = substring(out, 0, strlen(out) - 1);
706 }
707 else if((sk = teamscorekeepers[tm - 1]))
708 {
709 for(i = 0; i < MAX_TEAMSCORE; ++i)
711 out = strcat(out, ftos(sk.(teamscores(i))), ",");
712 if(shortString < 2)
713 for(i = 0; i < MAX_TEAMSCORE; ++i)
715 out = strcat(out, ftos(sk.(teamscores(i))), ",");
716 if(shortString < 1)
717 for(i = 0; i < MAX_TEAMSCORE; ++i)
720 out = strcat(out, ftos(sk.(teamscores(i))), ",");
721 out = substring(out, 0, strlen(out) - 1);
722 }
723 return out;
724}
725
726// strict: compare others fields too besides primary and secondary
727int PlayerTeamScore_Compare(entity p1, entity p2, float teams, bool strict)
728{
730 {
731 if(p1.team != p2.team)
732 {
733 entity t1 = teamscorekeepers[p1.team - 1];
734 entity t2 = teamscorekeepers[p2.team - 1];
735 int r = TeamScore_Compare(t1, t2, ((teams >= 0) ? 1 : strict));
736 return r;
737 }
738 if(teams < 0)
739 return 0;
740 }
741
742 return PlayerScore_Compare(CS(p1).scorekeeper, CS(p2).scorekeeper, strict);
743}
744
745entity PlayerScore_Sort(.float field, int teams, bool strict, bool nospectators)
746{
747 entity p, plist, pprev, pbest, pbestprev, pfirst, plast;
748
749 plist = NULL;
750
751 FOREACH_CLIENT(true, { it.(field) = 0; });
752
754 {
755 if(nospectators)
756 if(it.frags == FRAGS_SPECTATOR)
757 continue;
758
759 it.chain = plist;
760 plist = it;
761 });
762 // Now plist points to the whole list.
763
764 pfirst = plast = NULL;
765
766 int i = 0, j = 0;
767 while(plist)
768 {
769 pprev = pbestprev = NULL;
770 pbest = plist;
771 for(p = plist; (pprev = p), (p = p.chain); )
772 {
773 if(PlayerTeamScore_Compare(p, pbest, teams, strict) > 0)
774 {
775 pbest = p;
776 pbestprev = pprev;
777 }
778 }
779
780 // remove pbest out of the chain
781 if(pbestprev == NULL)
782 plist = pbest.chain;
783 else
784 pbestprev.chain = pbest.chain;
785 pbest.chain = NULL;
786
787 ++i;
788 if(!plast || PlayerTeamScore_Compare(plast, pbest, teams, strict))
789 j = i;
790
791 pbest.(field) = j;
792
793 if (!pfirst)
794 pfirst = pbest;
795 if(plast)
796 plast.chain = pbest;
797 plast = pbest;
798 }
799
800 return pfirst;
801}
802
804{
805 float s;
806
807 if(t <= 0 || t >= 16)
808 {
809 if(game_stopped)
810 return 0;
811 error("Reading score of invalid team!");
812 }
813
814 entity sk = teamscorekeepers[t - 1];
815 if (!sk)
816 return -999999999;
817 s = sk.teamscores_primary;
819 if(!s)
820 return -999999999;
822 s = -s;
823 return s;
824}
825
826const float NAMEWIDTH = 22;
827const float SCORESWIDTH = 58;
828// TODO put this somewhere in common?
830{
831 if(vflags & SFL_SORT_PRIO_PRIMARY)
832 return "^3";
833 else if(vflags & SFL_SORT_PRIO_SECONDARY)
834 return "^5";
835 else
836 return "^7";
837}
838
839void Score_NicePrint_Team(entity to, float t, float w)
840{
841 int fl;
842 string s = "";
843
844 entity sk = teamscorekeepers[t - 1];
845 if(sk)
846 {
847 s = strcat(s, Team_ColoredFullName(t));
848 for(int i = 0; i < MAX_TEAMSCORE; ++i)
849 if(teamscores_label(i) != "")
850 {
851 fl = teamscores_flags(i);
852 float sc = sk.(teamscores(i));
853 s = strcat(s, " ", Score_NicePrint_ItemColor(fl), ScoreString(fl, sc, 0));
854 }
855 }
856 else
857 s = "Scores:";
858
859 s = strcat(s, strpad(max(0, NAMEWIDTH - strlennocol(s)), ""));
860
861 FOREACH(Scores, true, {
862 if(scores_label(it) != "")
863 {
864 fl = scores_flags(it);
865 string s2 = scores_label(it);
866 s = strcat(s, " ", Score_NicePrint_ItemColor(fl), strpad(-w, substring(s2, 0, w)));
867 }
868 });
869
870 print_to(to, s);
871}
872
874{
875 float i;
876 entity sk = CS(p).scorekeeper;
877
878 string s = strcat(" ", playername(p.netname, p.team, false));
879 for (;;)
880 {
881 i = strlennocol(s) - NAMEWIDTH;
882 if(i > 0)
883 s = substring(s, 0, strlen(s) - i);
884 else
885 {
886 s = strcat(s, strpad(i, ""));
887 break;
888 }
889 }
890
891 FOREACH(Scores, true, {
892 if(scores_label(it) != "")
893 {
894 int fl = scores_flags(it);
895 float sc = sk.(scores(it));
896 s = strcat(s, " ", Score_NicePrint_ItemColor(fl), strpad(-w, ScoreString(fl, sc, 0)));
897 }
898 });
899
900 print_to(to, s);
901}
902
904{
905 print_to(to, "Spectators:");
906}
907
909{
910 print_to(to, strcat(" ", playername(p.netname, p.team, false)));
911}
912
915{
916 int t = 0;
917 FOREACH(Scores, true, {
918 if(scores_label(it) != "")
919 ++t;
920 });
921 int w = bound(6, floor(SCORESWIDTH / t - 1), 9);
922
923 entity p = PlayerScore_Sort(score_dummyfield, 1, true, false);
924 t = -1;
925
927 Score_NicePrint_Team(to, t, w);
928 while(p)
929 {
931 if(t != p.team)
932 Score_NicePrint_Team(to, p.team, w);
933 Score_NicePrint_Player(to, p, w);
934 t = p.team;
935 p = p.chain;
936 }
937
938 t = 0;
940 if (!t)
943 t = 1;
944 });
945}
946
948{
949 entity s = CS(p).scorekeeper;
950 FOREACH(Scores, true, {
951 if(s.(scores(it)) != 0 && scores_label(it) != "")
953 strcat(PLAYERSTATS_SCOREBOARD, scores_label(it)), s.(scores(it)));
954 });
955}
956
958{
959 for (int t = 0; t < 16; ++t)
960 {
961 entity sk = teamscorekeepers[t];
962 if (!sk)
963 continue;
964 for (int i = 0; i < MAX_TEAMSCORE; ++i)
965 if(sk.(teamscores(i)) != 0 && teamscores_label(i) != "")
966 // the +1 is important here!
968 strcat(PLAYERSTATS_SCOREBOARD, teamscores_label(i)), sk.(teamscores(i)));
969 }
970}
int player_count
Definition api.qh:103
#define MUTATOR_CALLHOOK(id,...)
Definition base.qh:143
#define BIT(n)
Only ever assign into the first 24 bits in QC (so max is BIT(23)).
Definition bits.qh:8
#define boolean(value)
Definition bool.qh:9
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
entity owner
Definition main.qh:87
bool warmup_stage
Definition main.qh:120
int serverflags
Definition main.qh:211
int team
Definition main.qh:188
entity teams
Definition main.qh:58
#define M_ARGV(x, type)
Definition events.qh:17
#define IS_PLAYER(s)
Definition player.qh:242
#define scores_label(this)
Definition scores.qh:146
#define MAX_TEAMSCORE
Definition scores.qh:149
#define scores(this)
Definition scores.qh:145
entity PlayerScoreField
Definition scores.qh:140
const int SFL_LOWER_IS_BETTER
Lower scores are better (e.g.
Definition scores.qh:102
#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_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 teamscores(i)
Definition scores.qh:152
#define SFL_ZERO_IS_WORST
Definition scores.qh:127
const int SFL_SORT_PRIO_PRIMARY
Definition scores.qh:134
float game_stopped
Definition stats.qh:81
string playername(string thename, int teamid, bool team_colorize)
Definition util.qc:2178
string ScoreString(int pFlags, float pValue, int rounds_played)
Definition util.qc:421
const int FRAGS_SPECTATOR
Definition constants.qh:4
float time
string worldstatus
#define strlen
WriteString(chan, ent.netname)
WriteByte(chan, ent.angles.y/DEC_FACTOR)
string GetGametype()
#define FOREACH(list, cond, body)
Definition iter.qh:19
const int MSG_ENTITY
Definition net.qh:156
#define WriteHeader(to, id)
Definition net.qh:265
void Net_LinkEntity(entity e, bool docull, float dt, bool(entity this, entity to, int sendflags) sendfunc)
Definition net.qh:167
#define WriteRegistered(r, to, it)
Definition net.qh:292
#define assert(expr,...)
Definition log.qh:8
#define LOG_WARN(...)
Definition log.qh:58
Gametype MapInfo_LoadedGametype
Definition mapinfo.qh:220
string name
Definition menu.qh:30
float bound(float min, float value, float max)
string substring(string s, float start, float length)
void WriteChar(float data, float dest, float desto)
const string cvar_string(string name)
void WriteShort(float data, float dest, float desto)
string ftos(float f)
void eprint(entity e)
float floor(float f)
float max(float f,...)
#define etof(e)
Definition misc.qh:25
spree_inf s1 s2 s3loc s2 spree_inf s1 s2 s3loc s2 spree_inf s1 s2 s3loc s2 s1 s2loc s1 s2loc s1 s2loc s1 s2loc s1 s2loc s1 s2loc s1 s2 f1points s1 s2
Definition all.inc:474
strcat(_("^F4Countdown stopped!"), "\n^BG", _("Teams are too unbalanced."))
#define new_pure(class)
purely logical entities (not linked to the area grid)
Definition oo.qh:66
void PlayerStats_GameReport_AddTeam(int t)
void PlayerStats_GameReport_AddEvent(string event_id)
#define PlayerStats_GameReport_Event_Team(team, eventid, val)
const string PLAYERSTATS_SCOREBOARD
const string PLAYERSTATS_TOTAL
#define PlayerStats_GameReport_Event_Player(ent, eventid, val)
#define NULL
Definition post.qh:14
#define error
Definition pre.qh:6
entity result
Definition promise.qc:45
const float NAMEWIDTH
Definition scores.qc:826
void Score_NicePrint_Team(entity to, float t, float w)
Definition scores.qc:839
var int scores_primary
Definition scores.qc:22
string Score_NicePrint_ItemColor(int vflags)
Definition scores.qc:829
int ScoreField_Compare(entity t1, entity t2,.float field, int fieldflags, int previous)
Definition scores.qc:31
void PlayerScore_Detach(entity player)
Detaches a PlayerScore entity from the player.
Definition scores.qc:341
entity teamscorekeepers[16]
Definition scores.qc:20
const float SCORESWIDTH
Definition scores.qc:827
string GetTeamScoreString(int tm, float shortString)
Definition scores.qc:671
int scores_flags_secondary
Definition scores.qc:27
float TeamScore_Add(entity player, float scorefield, float score)
Adds a score to the player's team's scores.
Definition scores.qc:135
var int teamscores_primary
Definition scores.qc:23
float TeamScore_GetCompareValue(float t)
Returns a value indicating the team score (and higher is better).
Definition scores.qc:803
void WinningConditionHelper(entity this)
Sets the following results for the current scores entities.
Definition scores.qc:440
void ScoreInfo_Init(int teams)
Initialize the scores info for the given number of teams.
Definition scores.qc:233
string GetScoreLogLabel(string label, float fl)
Definition scores.qc:599
void Score_ClearAll()
Clear ALL scores (for ready-restart).
Definition scores.qc:303
void TeamScore_Spawn(float t, string name)
Definition scores.qc:97
int teamscores_flags_primary
Definition scores.qc:25
int scores_flags_primary
Definition scores.qc:24
void Score_NicePrint_Spectator(entity to, entity p)
Definition scores.qc:908
float TeamScore_AddToTeam(int t, float scorefield, float score)
Adds a score to the given team.
Definition scores.qc:108
int teamscores_entities_count
Definition scores.qc:21
void ScoreInfo_SetLabel_TeamScore(int i, string label, int scoreflags)
Definition scores.qc:189
var int scores_secondary
Definition scores.qc:26
float score_dummyfield
Definition scores.qc:913
bool ScoreInfo_SendEntity(entity this, entity to, int sf)
Definition scores.qc:205
float PlayerScore_Compare(entity t1, entity t2, bool strict)
Definition scores.qc:412
void PlayerScore_PlayerStats(entity p)
Definition scores.qc:947
float PlayerTeamScore_Add(entity player, PlayerScoreField pscorefield, float tscorefield, float score)
Adds a score to both the player and the team.
Definition scores.qc:403
void Score_NicePrint_Player(entity to, entity p, float w)
Definition scores.qc:873
int PlayerTeamScore_Compare(entity p1, entity p2, float teams, bool strict)
Definition scores.qc:727
void PlayerScore_Attach(entity player)
Attaches a PlayerScore entity to a player.
Definition scores.qc:331
void Score_NicePrint(entity to)
Prints the scores to the console of a player.
Definition scores.qc:914
bool PlayerScore_SendEntity(entity this, entity to, float sendflags)
Definition scores.qc:258
int TeamScore_Compare(entity t1, entity t2, bool strict)
Definition scores.qc:141
entity scorekeeper
Definition scores.qc:19
void Score_NicePrint_Spectators(entity to)
Definition scores.qc:903
string GetPlayerScoreString(entity pl, float shortString)
Returns score strings for eventlog etc.
Definition scores.qc:610
entity PlayerScore_Sort(.float field, int teams, bool strict, bool nospectators)
Sorts the players and stores their place in the given field, starting with.
Definition scores.qc:745
void ScoreInfo_SetLabel_PlayerScore(PlayerScoreField i, string label, int scoreflags)
Definition scores.qc:168
float PlayerScore_Add(entity player, PlayerScoreField scorefield, float score)
Adds a score to the player's scores.
Definition scores.qc:349
bool TeamScore_SendEntity(entity this, entity to, float sendflags)
Definition scores.qc:64
int autocvar_g_score_resetonjoin
Definition scores.qc:28
float PlayerScore_Set(entity player, PlayerScoreField scorefield, float score)
Sets the player's score to the score parameter.
Definition scores.qc:381
bool PlayerScore_Clear(entity player)
Initialize the score of this player if needed.
Definition scores.qc:286
void PlayerScore_TeamStats()
Definition scores.qc:957
void SendWelcomeMessage(entity this, int msg_type)
Definition client.qc:1077
int nJoinAllowed(entity this)
Determines how many player slots are free.
Definition client.qc:2166
string autocvar_g_xonoticversion
Definition client.qh:46
void print_to(entity to, string input)
Definition common.qc:171
float WinningConditionHelper_secondscore
second highest score
Definition scores.qh:108
entity WinningConditionHelper_second
the second player, or NULL if none
Definition scores.qh:113
entity scores_initialized
Definition scores.qh:7
float WinningConditionHelper_winnerteam
the color of the winning team, or -1 if none
Definition scores.qh:109
entity WinningConditionHelper_winner
the winning player, or NULL if none
Definition scores.qh:112
float WinningConditionHelper_topscore
highest score
Definition scores.qh:107
float WinningConditionHelper_equality
we have no winner
Definition scores.qh:111
bool autocvar_g_full_getstatus_responses
Definition scores.qh:5
float WinningConditionHelper_lowerisbetter
lower is better, duh
Definition scores.qh:114
float WinningConditionHelper_zeroisworst
zero is worst, duh
Definition scores.qh:115
float WinningConditionHelper_secondteam
the color of the second team, or -1 if none
Definition scores.qh:110
ClientState CS(Client this)
Definition state.qh:47
#define strcpy(this, s)
Definition string.qh:51
#define INGAME_JOINED(it)
Definition sv_rules.qh:25
const int NUM_TEAM_2
Definition teams.qh:14
const int NUM_TEAM_4
Definition teams.qh:16
const int NUM_TEAM_3
Definition teams.qh:15
#define Team_ColoredFullName(teamid)
Definition teams.qh:232
const int NUM_TEAM_1
Definition teams.qh:13
#define IS_REAL_CLIENT(v)
Definition utils.qh:17
#define FOREACH_CLIENT(cond, body)
Definition utils.qh:52
#define FOREACH_CLIENTSLOT(cond, body)
Definition utils.qh:42
string loaded_gametype_custom_string
Definition world.qh:53
float cvar_purechanges_count
Definition world.qh:47
string modname
Definition world.qh:49
string sv_termsofservice_url_escaped
Definition world.qh:59