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
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: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
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:293
#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:471
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: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:2144
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: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