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
28
29// returns cmp value
30int ScoreField_Compare(entity t1, entity t2, .float field, int fieldflags, int previous)
31{
32 if(fieldflags & SFL_NOT_SORTABLE) // column does not sort
33 return previous;
34 if (t1.(field) == t2.(field))
35 return previous;
36
37 if(fieldflags & SFL_ZERO_IS_WORST)
38 {
39 if (t1.(field) == 0)
40 {
41 previous = -1;
42 return previous;
43 }
44 else if (t2.(field) == 0)
45 {
46 previous = +1;
47 return previous;
48 }
49 }
50
51 if (fieldflags & SFL_LOWER_IS_BETTER)
52 previous = (t2.(field) - t1.(field));
53 else
54 previous = (t1.(field) - t2.(field));
55
56 return previous;
57}
58
59/*
60 * teamscore entities
61 */
62
63bool TeamScore_SendEntity(entity this, entity to, float sendflags)
64{
65 int i, longflags;
66
67 WriteHeader(MSG_ENTITY, ENT_CLIENT_TEAMSCORES);
68 int t = this.team - 1;
69 assert(t, eprint(this));
71
72 longflags = 0;
73 for(i = 0; i < MAX_TEAMSCORE; ++i)
74 if(this.(teamscores(i)) > 127 || this.(teamscores(i)) <= -128)
75 longflags |= BIT(i);
76
77#if MAX_TEAMSCORE <= 8
78 WriteByte(MSG_ENTITY, sendflags);
79 WriteByte(MSG_ENTITY, longflags);
80#else
81 WriteShort(MSG_ENTITY, sendflags);
82 WriteShort(MSG_ENTITY, longflags);
83#endif
84 for(i = 0; i < MAX_TEAMSCORE; ++i)
85 if(sendflags & BIT(i))
86 {
87 if(longflags & BIT(i))
88 WriteInt24_t(MSG_ENTITY, this.(teamscores(i)));
89 else
91 }
92
93 return true;
94}
95
96void TeamScore_Spawn(float t, string name)
97{
98 entity ts = new_pure(csqc_score_team);
99 ts.netname = name; // not used yet, FIXME
100 ts.team = t;
102 teamscorekeepers[t - 1] = ts;
105}
106
107float TeamScore_AddToTeam(int t, float scorefield, float score)
108{
109 entity s;
110
111 if(game_stopped)
112 score = 0;
113
114 if(!scores_initialized) return 0; // FIXME remove this when everything uses this system
115 if(t <= 0 || t >= 16)
116 {
117 if(game_stopped)
118 return 0;
119 error("Adding score to invalid team!");
120 }
121 s = teamscorekeepers[t - 1];
122 if(!s)
123 {
124 if(game_stopped)
125 return 0;
126 error("Adding score to unknown team!");
127 }
128 if(score)
129 if(teamscores_label(scorefield) != "")
130 s.SendFlags |= BIT(scorefield);
131 return (s.(teamscores(scorefield)) += score);
132}
133
134float TeamScore_Add(entity player, float scorefield, float score)
135{
136 return TeamScore_AddToTeam(player.team, scorefield, score);
137}
138
139// strict: compare others fields too besides primary and secondary
140int TeamScore_Compare(entity t1, entity t2, bool strict)
141{
142 if(!t1 || !t2) return (!t2) - !t1;
143
144 // supporting MAX_TEAMSCORE > 2 requires keeping track of primary and secondary teamscore
145 if (MAX_TEAMSCORE > 2)
146 error("MAX_TEAMSCORE > 2 not supported");
147
148 // first compare primary, then others (don't check secondary flag since there are only 2 teamscores)
149 int result = 0;
152 if (result == 0 && strict)
153 {
154 i = (i + 1) % MAX_TEAMSCORE;
156 if (result == 0)
157 result = t1.team - t2.team;
158 }
159
160 return result;
161}
162
163/*
164 * the scoreinfo entity
165 */
166
167void ScoreInfo_SetLabel_PlayerScore(PlayerScoreField i, string label, int scoreflags)
168{
169 scores_label(i) = label;
170 scores_flags(i) = scoreflags;
171 if((scoreflags & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_PRIMARY)
172 {
174 scores_flags_primary = scoreflags;
175 }
176 else if((scoreflags & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_SECONDARY)
177 {
179 scores_flags_secondary = scoreflags;
180 }
181 if(label != "")
182 {
185 }
186}
187
188void ScoreInfo_SetLabel_TeamScore(int i, string label, int scoreflags)
189{
190 teamscores_label(i) = label;
191 teamscores_flags(i) = scoreflags;
192 if((scoreflags & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_PRIMARY)
193 {
195 teamscores_flags_primary = scoreflags;
196 }
197 if(label != "")
198 {
201 }
202}
203
204bool ScoreInfo_SendEntity(entity this, entity to, int sf)
205{
206 int i;
207 WriteHeader(MSG_ENTITY, ENT_CLIENT_SCORES_INFO);
209 string gt_name = "";
211 gt_name = cvar_string(strcat("sv_vote_gametype_", loaded_gametype_custom_string, "_name"));
212 WriteString(MSG_ENTITY, gt_name);
213 FOREACH(Scores, true, {
216 });
217 for(i = 0; i < MAX_TEAMSCORE; ++i)
218 {
221 }
222 // prevent sending the welcome message again when score types are sent again because the scoring system has changed
223 // it can happen in some gametypes like Race when the qualyfing session ends and the race starts
224 bool welcome_msg_too = (!CS(to) || time < CS(to).jointime + 5);
225 WriteByte(MSG_ENTITY, welcome_msg_too);
226 // welcome message is sent here because it needs to know the gametype
227 if (welcome_msg_too)
229 return true;
230}
231
233{
235 {
236 scores_initialized.SendFlags |= 1; // force a resend
237 }
238 else
239 {
240 scores_initialized = new_pure(ent_client_scoreinfo);
242 }
243 if(teams & BIT(0))
245 if(teams & BIT(1))
247 if(teams & BIT(2))
248 TeamScore_Spawn(NUM_TEAM_3, "Yellow");
249 if(teams & BIT(3))
251}
252
253/*
254 * per-player score entities
255 */
256
257bool PlayerScore_SendEntity(entity this, entity to, float sendflags)
258{
259 WriteHeader(MSG_ENTITY, ENT_CLIENT_SCORES);
261
262 int longflags = 0;
263 FOREACH(Scores, true, {
264 int p = BIT(i % 16);
265 if (this.(scores(it)) > 127 || this.(scores(it)) <= -128)
266 longflags |= p;
267 });
268
269 WriteShort(MSG_ENTITY, sendflags);
270 WriteShort(MSG_ENTITY, longflags);
271 FOREACH(Scores, true, {
272 int p = BIT(i % 16);
273 if (sendflags & p)
274 {
275 if(longflags & p)
276 WriteInt24_t(MSG_ENTITY, this.(scores(it)));
277 else
278 WriteChar(MSG_ENTITY, this.(scores(it)));
279 }
280 });
281
282 return true;
283}
284
286{
287 entity sk;
288
290 return false;
291
292 if(MUTATOR_CALLHOOK(ForbidPlayerScore_Clear))
293 return false;
294
295 sk = CS(player).scorekeeper;
296 FOREACH(Scores, true, {
297 if(sk.(scores(it)) != 0 && scores_label(it) != "")
298 sk.SendFlags |= BIT(i % 16);
299 if(i != SP_SKILL.m_id)
300 sk.(scores(it)) = 0;
301 });
302
303 return true;
304}
305
307{
308 entity sk;
309 int t;
310 FOREACH_CLIENTSLOT(true, {
311 sk = CS(it).scorekeeper;
312 if (!sk) continue;
313 FOREACH(Scores, true, {
314 if(sk.(scores(it)) != 0 && scores_label(it) != "")
315 sk.SendFlags |= BIT(i % 16);
316 if(i != SP_SKILL.m_id)
317 sk.(scores(it)) = 0;
318 });
319 });
320 for(t = 0; t < 16; ++t)
321 {
322 sk = teamscorekeepers[t];
323 if(!sk)
324 continue;
325 for(int j = 0; j < MAX_TEAMSCORE; ++j)
326 {
327 if(sk.(teamscores(j)) != 0 && teamscores_label(j) != "")
328 sk.SendFlags |= BIT(j);
329 sk.(teamscores(j)) = 0;
330 }
331 }
332}
333
335{
336 if(CS(player).scorekeeper)
337 error("player already has a scorekeeper");
339 sk.owner = player;
341 CS(player).scorekeeper = sk;
342}
343
345{
346 if(!CS(player).scorekeeper)
347 error("player has no scorekeeper");
348 delete(CS(player).scorekeeper);
349 CS(player).scorekeeper = NULL;
350}
351
352float PlayerScore_Add(entity player, PlayerScoreField scorefield, float score)
353{
354 bool mutator_returnvalue = MUTATOR_CALLHOOK(AddPlayerScore, scorefield, score, player);
355 score = M_ARGV(1, float);
356
357 if(!mutator_returnvalue && game_stopped)
358 {
359 score = 0;
360 }
361
362 if(!scores_initialized) return 0; // FIXME remove this when everything uses this system
363 entity s = CS(player).scorekeeper;
364 if(!s)
365 {
366 if(game_stopped)
367 return 0;
368 LOG_WARN("Adding score to unknown player!");
369 return 0;
370 }
371 if(!score)
372 {
373 return s.(scores(scorefield));
374 }
375 if(scores_label(scorefield) != "")
376 s.SendFlags |= BIT(scorefield.m_id % 16);
377 if(!warmup_stage)
379 s.(scores(scorefield)) += score;
380 MUTATOR_CALLHOOK(AddedPlayerScore, scorefield, score, player);
381 return s.(scores(scorefield));
382}
383
384float PlayerScore_Set(entity player, PlayerScoreField scorefield, float score)
385{
386 if(!scores_initialized) return 0; // FIXME remove this when everything uses this system
387 entity s = CS(player).scorekeeper;
388 if(!s)
389 {
390 if(game_stopped)
391 return 0;
392 LOG_WARN("Setting score of unknown player!");
393 return 0;
394 }
395
396 float oldscore = s.(scores(scorefield));
397 if(oldscore == score)
398 return oldscore;
399
400 if(scores_label(scorefield) != "")
401 s.SendFlags |= BIT(scorefield.m_id % 16);
402 s.(scores(scorefield)) = score;
403 return s.(scores(scorefield));
404}
405
406float PlayerTeamScore_Add(entity player, PlayerScoreField pscorefield, float tscorefield, float score)
407{
408 float r = PlayerScore_Add(player, pscorefield, score);
409 if(teamscores_entities_count) // only for teamplay
410 r = TeamScore_Add(player, tscorefield, score);
411 return r;
412}
413
414// strict: compare others fields too besides primary and secondary
415float PlayerScore_Compare(entity t1, entity t2, bool strict)
416{
417 if(!t1 || !t2) return (!t2) - !t1;
418
419 int result = 0;
421 // NOTE: if (scores_secondary) doesn't work because it's a field pointer
422 if (result == 0 && scores_flags_secondary)
424
425 if (result == 0 && strict)
426 {
427 FOREACH(Scores, true, {
429 continue;
430 if (scores_label(it) == "")
431 continue;
432 var .float f = scores(it);
433 result = ScoreField_Compare(t1, t2, f, scores_flags(it), result);
434 if (result) break;
435 });
436 if (result == 0)
437 result = t1.owner.playerid - t2.owner.playerid;
438 }
439
440 return result;
441}
442
444{
445 float c;
446 string s;
447 entity sk;
448
449 // format:
450 // gametype:P<pure>:S<slots>::plabel,plabel:tlabel,tlabel:teamid:tscore,tscore:teamid:tscore,tscore
451 // score labels always start with a symbol or with lower case
452 // so to match pure, match for :P0:
453 // to match full, match for :S0:
454
455 // NOTE can't use a single strcat because strcat concatenates max 8 strings
456 s = strcat(GetGametype(),
459 ":S", ftos(nJoinAllowed(NULL)));
460 s = strcat(s,
461 ":F", ftos(serverflags),
463 ":M", modname);
464 s = strcat(s,
466
467 entity winnerscorekeeper = NULL;
468 entity secondscorekeeper = NULL;
469
471 {
472 int t;
473
474 s = strcat(s, ":", GetTeamScoreString(0, 1));
475 for(t = 0; t < 16; ++t)
476 if(teamscorekeepers[t])
477 s = strcat(s, ":", ftos(t+1), ":", GetTeamScoreString(t+1, 1));
478
481 for(t = 0; t < 16; ++t)
482 {
483 sk = teamscorekeepers[t];
484 c = TeamScore_Compare(winnerscorekeeper, sk, true);
485 if(c < 0)
486 {
489 secondscorekeeper = winnerscorekeeper;
490 winnerscorekeeper = sk;
491 }
492 else
493 {
494 c = TeamScore_Compare(secondscorekeeper, sk, true);
495 if(c < 0)
496 {
498 secondscorekeeper = sk;
499 }
500 }
501 }
502
503 WinningConditionHelper_equality = (TeamScore_Compare(winnerscorekeeper, secondscorekeeper, false) == 0);
506
507 WinningConditionHelper_topscore = winnerscorekeeper.teamscores_primary;
508 WinningConditionHelper_secondscore = secondscorekeeper.teamscores_primary;
511
512 WinningConditionHelper_winner = NULL; // not supported in teamplay
513 WinningConditionHelper_second = NULL; // not supported in teamplay
514 }
515 else
516 {
520 sk = CS(it).scorekeeper;
521 c = PlayerScore_Compare(winnerscorekeeper, sk, true);
522 if(c < 0)
523 {
526 secondscorekeeper = winnerscorekeeper;
527 winnerscorekeeper = sk;
528 }
529 else
530 {
531 c = PlayerScore_Compare(secondscorekeeper, sk, true);
532 if(c < 0)
533 {
535 secondscorekeeper = sk;
536 }
537 }
538 });
539
540 WinningConditionHelper_equality = (PlayerScore_Compare(winnerscorekeeper, secondscorekeeper, false) == 0);
543
544 WinningConditionHelper_topscore = winnerscorekeeper.scores_primary;
545 WinningConditionHelper_secondscore = secondscorekeeper.scores_primary;
548
549 WinningConditionHelper_winnerteam = -1; // no teamplay
550 WinningConditionHelper_secondteam = -1; // no teamplay
551 }
552
554 {
556 {
559 else
561 }
562 if(player_count == 0) // special case: empty servers DO end the match at a 0:0 tie
564 }
565
567 {
569 {
572 else
574 }
575 }
576
577 if (s != worldstatus)
579
580 FOREACH_CLIENT(true, {
581 string s = "";
583 {
584 s = GetPlayerScoreString(it, 1);
585 s = strcat(s, IS_REAL_CLIENT(it) ? ":human" : ":bot");
586 if(!(IS_PLAYER(it) || INGAME_JOINED(it)))
587 s = strcat(s, ":spectator");
588 }
589 else
590 {
591 if (IS_PLAYER(it) || INGAME_JOINED(it))
592 s = GetPlayerScoreString(it, 2);
593 else
594 s = "-666";
595 }
596
597 if (s != it.clientstatus)
598 strcpy(it.clientstatus, s);
599 });
600}
601
602string GetScoreLogLabel(string label, float fl)
603{
604 if(fl & SFL_LOWER_IS_BETTER)
605 label = strcat(label, "<");
607 label = strcat(label, "!!");
609 label = strcat(label, "!");
610 return label;
611}
612
613string GetPlayerScoreString(entity pl, float shortString)
614{
615 entity sk;
616 int fl;
617 string lbl;
618
619 string out = "";
620 if(!pl)
621 {
622 // label
623 FOREACH(Scores, true, {
625 {
626 fl = scores_flags(it);
627 lbl = scores_label(it);
628 out = strcat(out, GetScoreLogLabel(lbl, fl), ",");
629 }
630 });
631 if(shortString < 2)
632 FOREACH(Scores, true, {
634 {
635 fl = scores_flags(it);
636 lbl = scores_label(it);
637 out = strcat(out, GetScoreLogLabel(lbl, fl), ",");
638 }
639 });
640 if(shortString < 1)
641 FOREACH(Scores, true, {
644 {
645 fl = scores_flags(it);
646 lbl = scores_label(it);
647 out = strcat(out, GetScoreLogLabel(lbl, fl), ",");
648 }
649 });
650 out = substring(out, 0, strlen(out) - 1);
651 }
652 else if((sk = CS(pl).scorekeeper))
653 {
654 FOREACH(Scores, true, {
656 out = strcat(out, ftos(sk.(scores(it))), ",");
657 });
658 if(shortString < 2)
659 FOREACH(Scores, true, {
661 out = strcat(out, ftos(sk.(scores(it))), ",");
662 });
663 if(shortString < 1)
664 FOREACH(Scores, true, {
667 out = strcat(out, ftos(sk.(scores(it))), ",");
668 });
669 out = substring(out, 0, strlen(out) - 1);
670 }
671 return out;
672}
673
674string GetTeamScoreString(int tm, float shortString)
675{
676 entity sk;
677 int i, fl;
678 string lbl;
679
680 string out = "";
681 if(tm == 0)
682 {
683 // label
684 for(i = 0; i < MAX_TEAMSCORE; ++i)
686 {
687 fl = teamscores_flags(i);
688 lbl = teamscores_label(i);
689 out = strcat(out, GetScoreLogLabel(lbl, fl), ",");
690 }
691 if(shortString < 2)
692 for(i = 0; i < MAX_TEAMSCORE; ++i)
694 {
695 fl = teamscores_flags(i);
696 lbl = teamscores_label(i);
697 out = strcat(out, GetScoreLogLabel(lbl, fl), ",");
698 }
699 if(shortString < 1)
700 for(i = 0; i < MAX_TEAMSCORE; ++i)
703 {
704 fl = teamscores_flags(i);
705 lbl = teamscores_label(i);
706 out = strcat(out, GetScoreLogLabel(lbl, fl), ",");
707 }
708 out = substring(out, 0, strlen(out) - 1);
709 }
710 else if((sk = teamscorekeepers[tm - 1]))
711 {
712 for(i = 0; i < MAX_TEAMSCORE; ++i)
714 out = strcat(out, ftos(sk.(teamscores(i))), ",");
715 if(shortString < 2)
716 for(i = 0; i < MAX_TEAMSCORE; ++i)
718 out = strcat(out, ftos(sk.(teamscores(i))), ",");
719 if(shortString < 1)
720 for(i = 0; i < MAX_TEAMSCORE; ++i)
723 out = strcat(out, ftos(sk.(teamscores(i))), ",");
724 out = substring(out, 0, strlen(out) - 1);
725 }
726 return out;
727}
728
729// strict: compare others fields too besides primary and secondary
730int PlayerTeamScore_Compare(entity p1, entity p2, float teams, bool strict)
731{
733 {
734 if(p1.team != p2.team)
735 {
736 entity t1 = teamscorekeepers[p1.team - 1];
737 entity t2 = teamscorekeepers[p2.team - 1];
738 int r = TeamScore_Compare(t1, t2, ((teams >= 0) ? 1 : strict));
739 return r;
740 }
741 if(teams < 0)
742 return 0;
743 }
744
745 return PlayerScore_Compare(CS(p1).scorekeeper, CS(p2).scorekeeper, strict);
746}
747
748entity PlayerScore_Sort(.float field, int teams, bool strict, bool nospectators)
749{
750 entity p, plist, pprev, pbest, pbestprev, pfirst, plast;
751
752 plist = NULL;
753
754 FOREACH_CLIENT(true, { it.(field) = 0; });
755
757 {
758 if(nospectators)
759 if(it.frags == FRAGS_SPECTATOR)
760 continue;
761
762 it.chain = plist;
763 plist = it;
764 });
765 // Now plist points to the whole list.
766
767 pfirst = plast = NULL;
768
769 int i = 0, j = 0;
770 while(plist)
771 {
772 pprev = pbestprev = NULL;
773 pbest = plist;
774 for(p = plist; (pprev = p), (p = p.chain); )
775 {
776 if(PlayerTeamScore_Compare(p, pbest, teams, strict) > 0)
777 {
778 pbest = p;
779 pbestprev = pprev;
780 }
781 }
782
783 // remove pbest out of the chain
784 if(pbestprev == NULL)
785 plist = pbest.chain;
786 else
787 pbestprev.chain = pbest.chain;
788 pbest.chain = NULL;
789
790 ++i;
791 if(!plast || PlayerTeamScore_Compare(plast, pbest, teams, strict))
792 j = i;
793
794 pbest.(field) = j;
795
796 if (!pfirst)
797 pfirst = pbest;
798 if(plast)
799 plast.chain = pbest;
800 plast = pbest;
801 }
802
803 return pfirst;
804}
805
807{
808 float s;
809
810 if(t <= 0 || t >= 16)
811 {
812 if(game_stopped)
813 return 0;
814 error("Reading score of invalid team!");
815 }
816
817 entity sk = teamscorekeepers[t - 1];
818 if (!sk)
819 return -999999999;
820 s = sk.teamscores_primary;
822 if(!s)
823 return -999999999;
825 s = -s;
826 return s;
827}
828
829const float NAMEWIDTH = 22;
830const float SCORESWIDTH = 58;
831// TODO put this somewhere in common?
833{
834 if(vflags & SFL_SORT_PRIO_PRIMARY)
835 return "^3";
836 else if(vflags & SFL_SORT_PRIO_SECONDARY)
837 return "^5";
838 else
839 return "^7";
840}
841
842void Score_NicePrint_Team(entity to, float t, float w)
843{
844 int fl;
845 string s = "";
846
847 entity sk = teamscorekeepers[t - 1];
848 if(sk)
849 {
850 s = strcat(s, Team_ColoredFullName(t));
851 for(int i = 0; i < MAX_TEAMSCORE; ++i)
852 if(teamscores_label(i) != "")
853 {
854 fl = teamscores_flags(i);
855 float sc = sk.(teamscores(i));
856 s = strcat(s, " ", Score_NicePrint_ItemColor(fl), ScoreString(fl, sc, 0));
857 }
858 }
859 else
860 s = "Scores:";
861
862 s = strcat(s, strpad(max(0, NAMEWIDTH - strlennocol(s)), ""));
863
864 FOREACH(Scores, true, {
865 if(scores_label(it) != "")
866 {
867 fl = scores_flags(it);
868 string s2 = scores_label(it);
869 s = strcat(s, " ", Score_NicePrint_ItemColor(fl), strpad(-w, substring(s2, 0, w)));
870 }
871 });
872
873 print_to(to, s);
874}
875
877{
878 float i;
879 entity sk = CS(p).scorekeeper;
880
881 string s = strcat(" ", playername(p.netname, p.team, false));
882 for (;;)
883 {
884 i = strlennocol(s) - NAMEWIDTH;
885 if(i > 0)
886 s = substring(s, 0, strlen(s) - i);
887 else
888 {
889 s = strcat(s, strpad(i, ""));
890 break;
891 }
892 }
893
894 FOREACH(Scores, true, {
895 if(scores_label(it) != "")
896 {
897 int fl = scores_flags(it);
898 float sc = sk.(scores(it));
899 s = strcat(s, " ", Score_NicePrint_ItemColor(fl), strpad(-w, ScoreString(fl, sc, 0)));
900 }
901 });
902
903 print_to(to, s);
904}
905
907{
908 print_to(to, "Spectators:");
909}
910
912{
913 print_to(to, strcat(" ", playername(p.netname, p.team, false)));
914}
915
918{
919 int t = 0;
920 FOREACH(Scores, true, {
921 if(scores_label(it) != "")
922 ++t;
923 });
924 int w = bound(6, floor(SCORESWIDTH / t - 1), 9);
925
926 entity p = PlayerScore_Sort(score_dummyfield, 1, true, false);
927 t = -1;
928
930 Score_NicePrint_Team(to, t, w);
931 while(p)
932 {
934 if(t != p.team)
935 Score_NicePrint_Team(to, p.team, w);
936 Score_NicePrint_Player(to, p, w);
937 t = p.team;
938 p = p.chain;
939 }
940
941 t = 0;
943 if (!t)
946 t = 1;
947 });
948}
949
951{
952 entity s = CS(p).scorekeeper;
953 FOREACH(Scores, true, {
954 if(s.(scores(it)) != 0 && scores_label(it) != "")
956 strcat(PLAYERSTATS_SCOREBOARD, scores_label(it)), s.(scores(it)));
957 });
958}
959
961{
962 for (int t = 0; t < 16; ++t)
963 {
964 entity sk = teamscorekeepers[t];
965 if (!sk)
966 continue;
967 for (int i = 0; i < MAX_TEAMSCORE; ++i)
968 if(sk.(teamscores(i)) != 0 && teamscores_label(i) != "")
969 // the +1 is important here!
971 strcat(PLAYERSTATS_SCOREBOARD, teamscores_label(i)), sk.(teamscores(i)));
972 }
973}
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:243
#define scores_label(this)
Definition scores.qh:146
entity PlayerScoreField
Definition scores.qh:140
#define MAX_TEAMSCORE
Definition scores.qh:149
#define scores(this)
Definition scores.qh:145
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:2082
string ScoreString(int pFlags, float pValue, int rounds_played)
Definition util.qc:423
const int FRAGS_SPECTATOR
Definition constants.qh:4
float time
string worldstatus
#define strlen
string GetGametype()
#define FOREACH(list, cond, body)
Definition iter.qh:19
const int MSG_ENTITY
Definition net.qh:115
#define WriteHeader(to, id)
Definition net.qh:221
void Net_LinkEntity(entity e, bool docull, float dt, bool(entity this, entity to, int sendflags) sendfunc)
Definition net.qh:123
#define WriteRegistered(r, to, it)
Definition net.qh:299
#define assert(expr,...)
Definition log.qh:8
#define LOG_WARN(...)
Definition log.qh:61
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 WriteString(string data, float dest, float desto)
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)
void WriteByte(float data, float dest, float desto)
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:469
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:67
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:43
const float NAMEWIDTH
Definition scores.qc:829
void Score_NicePrint_Team(entity to, float t, float w)
Definition scores.qc:842
var int scores_primary
Definition scores.qc:22
string Score_NicePrint_ItemColor(int vflags)
Definition scores.qc:832
int ScoreField_Compare(entity t1, entity t2,.float field, int fieldflags, int previous)
Definition scores.qc:30
void PlayerScore_Detach(entity player)
Detaches a PlayerScore entity from the player.
Definition scores.qc:344
entity teamscorekeepers[16]
Definition scores.qc:20
const float SCORESWIDTH
Definition scores.qc:830
string GetTeamScoreString(int tm, float shortString)
Definition scores.qc:674
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:134
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:806
void WinningConditionHelper(entity this)
Sets the following results for the current scores entities.
Definition scores.qc:443
void ScoreInfo_Init(int teams)
Initialize the scores info for the given number of teams.
Definition scores.qc:232
string GetScoreLogLabel(string label, float fl)
Definition scores.qc:602
void Score_ClearAll()
Clear ALL scores (for ready-restart).
Definition scores.qc:306
void TeamScore_Spawn(float t, string name)
Definition scores.qc:96
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:911
float TeamScore_AddToTeam(int t, float scorefield, float score)
Adds a score to the given team.
Definition scores.qc:107
int teamscores_entities_count
Definition scores.qc:21
void ScoreInfo_SetLabel_TeamScore(int i, string label, int scoreflags)
Definition scores.qc:188
var int scores_secondary
Definition scores.qc:26
float score_dummyfield
Definition scores.qc:916
bool ScoreInfo_SendEntity(entity this, entity to, int sf)
Definition scores.qc:204
float PlayerScore_Compare(entity t1, entity t2, bool strict)
Definition scores.qc:415
void PlayerScore_PlayerStats(entity p)
Definition scores.qc:950
float PlayerTeamScore_Add(entity player, PlayerScoreField pscorefield, float tscorefield, float score)
Adds a score to both the player and the team.
Definition scores.qc:406
void Score_NicePrint_Player(entity to, entity p, float w)
Definition scores.qc:876
int PlayerTeamScore_Compare(entity p1, entity p2, float teams, bool strict)
Definition scores.qc:730
void PlayerScore_Attach(entity player)
Attaches a PlayerScore entity to a player.
Definition scores.qc:334
void Score_NicePrint(entity to)
Prints the scores to the console of a player.
Definition scores.qc:917
bool PlayerScore_SendEntity(entity this, entity to, float sendflags)
Definition scores.qc:257
int TeamScore_Compare(entity t1, entity t2, bool strict)
Definition scores.qc:140
entity scorekeeper
Definition scores.qc:19
void Score_NicePrint_Spectators(entity to)
Definition scores.qc:906
string GetPlayerScoreString(entity pl, float shortString)
Returns score strings for eventlog etc.
Definition scores.qc:613
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:748
void ScoreInfo_SetLabel_PlayerScore(PlayerScoreField i, string label, int scoreflags)
Definition scores.qc:167
float PlayerScore_Add(entity player, PlayerScoreField scorefield, float score)
Adds a score to the player's scores.
Definition scores.qc:352
bool TeamScore_SendEntity(entity this, entity to, float sendflags)
Definition scores.qc:63
float PlayerScore_Set(entity player, PlayerScoreField scorefield, float score)
Sets the player's score to the score parameter.
Definition scores.qc:384
bool PlayerScore_Clear(entity player)
Initialize the score of this player if needed.
Definition scores.qc:285
void PlayerScore_TeamStats()
Definition scores.qc:960
void SendWelcomeMessage(entity this, int msg_type)
Definition client.qc:1075
int nJoinAllowed(entity this)
Determines how many player slots are free.
Definition client.qc:2143
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:52
#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:50
#define FOREACH_CLIENTSLOT(cond, body)
Definition utils.qh:40
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