Xonotic
scores.qc
Go to the documentation of this file.
1 #include "scores.qh"
2 
3 #include <common/mapinfo.qh>
5 #include <common/net_linked.qh>
6 #include <common/playerstats.qh>
7 #include <common/scores.qh>
8 #include <common/state.qh>
9 #include <common/stats.qh>
10 #include <common/teams.qh>
11 #include <common/weapons/_all.qh>
12 #include <server/client.qh>
13 #include <server/command/common.qh>
14 #include <server/intermission.qh>
15 #include <server/mutators/_mod.qh>
16 #include <server/round_handler.qh>
17 #include <server/world.qh>
18 
19 .entity scorekeeper;
22 var .float scores_primary;
23 var .float teamscores_primary;
26 
27 vector ScoreField_Compare(entity t1, entity t2, .float field, float fieldflags, vector previous, bool strict) // returns: cmp value, best prio
28 {
29  if(!strict && !(fieldflags & SFL_SORT_PRIO_MASK)) // column does not sort
30  return previous;
31  if((fieldflags & SFL_SORT_PRIO_MASK) < previous.y)
32  return previous;
33  if (t1.(field) == t2.(field))
34  return previous;
35 
36  previous.y = fieldflags & SFL_SORT_PRIO_MASK;
37 
38  if(fieldflags & SFL_ZERO_IS_WORST)
39  {
40  if (t1.(field) == 0)
41  {
42  previous.x = -1;
43  return previous;
44  }
45  else if (t2.(field) == 0)
46  {
47  previous.x = +1;
48  return previous;
49  }
50  }
51 
52  if (fieldflags & SFL_LOWER_IS_BETTER)
53  previous.x = (t2.(field) - t1.(field));
54  else
55  previous.x = (t1.(field) - t2.(field));
56 
57  return previous;
58 }
59 
60 /*
61  * teamscore entities
62  */
63 
65 {
66  float i, longflags;
67 
68  WriteHeader(MSG_ENTITY, ENT_CLIENT_TEAMSCORES);
69  int t = this.team - 1;
70  assert(t, eprint(this));
71  WriteByte(MSG_ENTITY, t);
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
91  WriteChar(MSG_ENTITY, this.(teamscores(i)));
92  }
93 
94  return true;
95 }
96 
97 void 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;
102  Net_LinkEntity(ts, false, 0, TeamScore_SendEntity);
103  teamscorekeepers[t - 1] = ts;
105  PlayerStats_GameReport_AddTeam(t);
106 }
107 
108 float TeamScore_AddToTeam(int t, float scorefield, float score)
109 {
110  entity s;
111 
112  if(game_stopped)
113  {
114  score = 0;
115  }
116 
117  if(!scores_initialized) return 0; // FIXME remove this when everything uses this system
118  if(t <= 0 || t >= 16)
119  {
120  if(game_stopped)
121  return 0;
122  error("Adding score to invalid team!");
123  }
124  s = teamscorekeepers[t - 1];
125  if(!s)
126  {
127  if(game_stopped)
128  return 0;
129  error("Adding score to unknown team!");
130  }
131  if(score)
132  if(teamscores_label(scorefield) != "")
133  s.SendFlags |= BIT(scorefield);
134  return (s.(teamscores(scorefield)) += score);
135 }
136 
137 float TeamScore_Add(entity player, float scorefield, float score)
138 {
139  return TeamScore_AddToTeam(player.team, scorefield, score);
140 }
141 
142 float TeamScore_Compare(entity t1, entity t2, bool strict)
143 {
144  if(!t1 || !t2) return (!t2) - !t1;
145 
146  vector result = '0 0 0';
147  float i;
148  for(i = 0; i < MAX_TEAMSCORE; ++i)
149  {
150  var .float f;
151  f = teamscores(i);
152  result = ScoreField_Compare(t1, t2, f, teamscores_flags(i), result, strict);
153  }
154 
155  if (result.x == 0 && strict)
156  result.x = t1.team - t2.team;
157 
158  return result.x;
159 }
160 
161 /*
162  * the scoreinfo entity
163  */
164 
165 void ScoreInfo_SetLabel_PlayerScore(PlayerScoreField i, string label, float scoreflags)
166 {
167  scores_label(i) = label;
168  scores_flags(i) = scoreflags;
169  if((scoreflags & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_PRIMARY)
170  {
171  scores_primary = scores(i);
172  scores_flags_primary = scoreflags;
173  }
174  if(label != "")
175  {
176  PlayerStats_GameReport_AddEvent(strcat(PLAYERSTATS_TOTAL, label));
177  PlayerStats_GameReport_AddEvent(strcat(PLAYERSTATS_SCOREBOARD, label));
178  }
179 }
180 
181 void ScoreInfo_SetLabel_TeamScore(float i, string label, float scoreflags)
182 {
183  teamscores_label(i) = label;
184  teamscores_flags(i) = scoreflags;
185  if((scoreflags & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_PRIMARY)
186  {
188  teamscores_flags_primary = scoreflags;
189  }
190  if(label != "")
191  {
192  PlayerStats_GameReport_AddEvent(strcat(PLAYERSTATS_TOTAL, label));
193  PlayerStats_GameReport_AddEvent(strcat(PLAYERSTATS_SCOREBOARD, label));
194  }
195 }
196 
198 {
199  float i;
200  WriteHeader(MSG_ENTITY, ENT_CLIENT_SCORES_INFO);
201  WriteRegistered(Gametypes, MSG_ENTITY, MapInfo_LoadedGametype);
202  FOREACH(Scores, true, {
203  WriteString(MSG_ENTITY, scores_label(it));
204  WriteByte(MSG_ENTITY, scores_flags(it));
205  });
206  for(i = 0; i < MAX_TEAMSCORE; ++i)
207  {
208  WriteString(MSG_ENTITY, teamscores_label(i));
209  WriteByte(MSG_ENTITY, teamscores_flags(i));
210  }
211  return true;
212 }
213 
215 {
217  {
218  scores_initialized.SendFlags |= 1; // force a resend
219  }
220  else
221  {
222  scores_initialized = new_pure(ent_client_scoreinfo);
223  Net_LinkEntity(scores_initialized, false, 0, ScoreInfo_SendEntity);
224  }
225  if(teams & BIT(0))
226  TeamScore_Spawn(NUM_TEAM_1, "Red");
227  if(teams & BIT(1))
228  TeamScore_Spawn(NUM_TEAM_2, "Blue");
229  if(teams & BIT(2))
230  TeamScore_Spawn(NUM_TEAM_3, "Yellow");
231  if(teams & BIT(3))
232  TeamScore_Spawn(NUM_TEAM_4, "Pink");
233 }
234 
235 /*
236  * per-player score entities
237  */
238 
240 {
241  WriteHeader(MSG_ENTITY, ENT_CLIENT_SCORES);
242  WriteByte(MSG_ENTITY, etof(this.owner));
243 
244  int longflags = 0;
245  FOREACH(Scores, true, {
246  int p = 1 << (i % 16);
247  if (this.(scores(it)) > 127 || this.(scores(it)) <= -128)
248  longflags |= p;
249  });
250 
251  WriteShort(MSG_ENTITY, sendflags);
252  WriteShort(MSG_ENTITY, longflags);
253  FOREACH(Scores, true, {
254  int p = 1 << (i % 16);
255  if (sendflags & p)
256  {
257  if(longflags & p)
258  WriteInt24_t(MSG_ENTITY, this.(scores(it)));
259  else
260  WriteChar(MSG_ENTITY, this.(scores(it)));
261  }
262  });
263 
264  return true;
265 }
266 
268 {
269  entity sk;
270 
272  return 0;
273 
274  if(MUTATOR_CALLHOOK(ForbidPlayerScore_Clear)) return 0;
275 
276  sk = CS(player).scorekeeper;
277  FOREACH(Scores, true, {
278  if(sk.(scores(it)) != 0)
279  if(scores_label(it) != "")
280  sk.SendFlags |= BIT(i % 16);
281  if(i != SP_ELO.m_id)
282  sk.(scores(it)) = 0;
283  });
284 
285  return 1;
286 }
287 
289 {
290  entity sk;
291  float t;
292  FOREACH_CLIENTSLOT(true, {
293  sk = CS(it).scorekeeper;
294  if (!sk) continue;
295  FOREACH(Scores, true, {
296  if(sk.(scores(it)) != 0)
297  if(scores_label(it) != "")
298  sk.SendFlags |= BIT(i % 16);
299  if(i != SP_ELO.m_id)
300  sk.(scores(it)) = 0;
301  });
302  });
303  for(t = 0; t < 16; ++t)
304  {
305  sk = teamscorekeepers[t];
306  if(!sk)
307  continue;
308  for(int j = 0; j < MAX_TEAMSCORE; ++j)
309  {
310  if(sk.(teamscores(j)) != 0)
311  if(teamscores_label(j) != "")
312  sk.SendFlags |= BIT(j);
313  sk.(teamscores(j)) = 0;
314  }
315  }
316 }
317 
319 {
320  if(CS(player).scorekeeper)
321  error("player already has a scorekeeper");
322  entity sk = new_pure(scorekeeper);
323  sk.owner = player;
324  Net_LinkEntity(sk, false, 0, PlayerScore_SendEntity);
325  CS(player).scorekeeper = sk;
326 }
327 
329 {
330  if(!CS(player).scorekeeper)
331  error("player has no scorekeeper");
332  delete(CS(player).scorekeeper);
333  CS(player).scorekeeper = NULL;
334 }
335 
336 float PlayerScore_Add(entity player, PlayerScoreField scorefield, float score)
337 {
338  bool mutator_returnvalue = MUTATOR_CALLHOOK(AddPlayerScore, scorefield, score, player);
339  score = M_ARGV(1, float);
340 
341  if(!mutator_returnvalue && game_stopped)
342  {
343  score = 0;
344  }
345 
346  if(!scores_initialized) return 0; // FIXME remove this when everything uses this system
347  entity s = CS(player).scorekeeper;
348  if(!s)
349  {
350  if(game_stopped)
351  return 0;
352  LOG_WARN("Adding score to unknown player!");
353  return 0;
354  }
355  if(!score)
356  {
357  return s.(scores(scorefield));
358  }
359  if(scores_label(scorefield) != "")
360  s.SendFlags |= BIT(scorefield.m_id % 16);
361  if(!warmup_stage)
362  PlayerStats_GameReport_Event_Player(s.owner, strcat(PLAYERSTATS_TOTAL, scores_label(scorefield)), score);
363  s.(scores(scorefield)) += score;
364  MUTATOR_CALLHOOK(AddedPlayerScore, scorefield, score, player);
365  return s.(scores(scorefield));
366 }
367 
368 float PlayerScore_Set(entity player, PlayerScoreField scorefield, float score)
369 {
370  if(!scores_initialized) return 0; // FIXME remove this when everything uses this system
371  entity s = CS(player).scorekeeper;
372  if(!s)
373  {
374  if(game_stopped)
375  return 0;
376  LOG_WARN("Setting score of unknown player!");
377  return 0;
378  }
379 
380  float oldscore = s.(scores(scorefield));
381  if(oldscore == score)
382  return oldscore;
383 
384  if(scores_label(scorefield) != "")
385  s.SendFlags |= BIT(scorefield.m_id % 16);
386  s.(scores(scorefield)) = score;
387  return s.(scores(scorefield));
388 }
389 
390 float PlayerTeamScore_Add(entity player, PlayerScoreField pscorefield, float tscorefield, float score)
391 {
392  float r;
393  r = PlayerScore_Add(player, pscorefield, score);
394  if(teamscores_entities_count) // only for teamplay
395  r = TeamScore_Add(player, tscorefield, score);
396  return r;
397 }
398 
399 float PlayerScore_Compare(entity t1, entity t2, bool strict)
400 {
401  if(!t1 || !t2) return (!t2) - !t1;
402 
403  vector result = '0 0 0';
404  FOREACH(Scores, true, {
405  var .float f = scores(it);
406  result = ScoreField_Compare(t1, t2, f, scores_flags(it), result, strict);
407  });
408 
409  if (result.x == 0 && strict)
410  result.x = t1.owner.playerid - t2.owner.playerid;
411 
412  return result.x;
413 }
414 
416 {
417  float c;
418  string s;
419  float fullstatus;
420  entity winnerscorekeeper;
421  entity secondscorekeeper;
422  entity sk;
423 
424  // format:
425  // gametype:P<pure>:S<slots>::plabel,plabel:tlabel,tlabel:teamid:tscore,tscore:teamid:tscore,tscore
426  // score labels always start with a symbol or with lower case
427  // so to match pure, match for :P0:
428  // to match full, match for :S0:
429 
431 
432  s = GetGametype();
433  s = strcat(s, ":", autocvar_g_xonoticversion);
434  s = strcat(s, ":P", ftos(cvar_purechanges_count));
435  s = strcat(s, ":S", ftos(nJoinAllowed(this, NULL)));
436  s = strcat(s, ":F", ftos(serverflags));
438  s = strcat(s, ":M", modname);
439  s = strcat(s, "::", GetPlayerScoreString(NULL, (fullstatus ? 1 : 2)));
440 
442  {
443  float t;
444 
445  s = strcat(s, ":", GetTeamScoreString(0, 1));
446  for(t = 0; t < 16; ++t)
447  if(teamscorekeepers[t])
448  s = strcat(s, ":", ftos(t+1), ":", GetTeamScoreString(t+1, 1));
449 
452  winnerscorekeeper = NULL;
453  secondscorekeeper = NULL;
454  for(t = 0; t < 16; ++t)
455  {
456  sk = teamscorekeepers[t];
457  c = TeamScore_Compare(winnerscorekeeper, sk, 1);
458  if(c < 0)
459  {
462  secondscorekeeper = winnerscorekeeper;
463  winnerscorekeeper = sk;
464  }
465  else
466  {
467  c = TeamScore_Compare(secondscorekeeper, sk, 1);
468  if(c < 0)
469  {
471  secondscorekeeper = sk;
472  }
473  }
474  }
475 
476  WinningConditionHelper_equality = (TeamScore_Compare(winnerscorekeeper, secondscorekeeper, 0) == 0);
479 
480  WinningConditionHelper_topscore = winnerscorekeeper.teamscores_primary;
481  WinningConditionHelper_secondscore = secondscorekeeper.teamscores_primary;
484 
485  WinningConditionHelper_winner = NULL; // not supported in teamplay
486  WinningConditionHelper_second = NULL; // not supported in teamplay
487  }
488  else
489  {
492  winnerscorekeeper = NULL;
493  secondscorekeeper = NULL;
495  sk = CS(it).scorekeeper;
496  c = PlayerScore_Compare(winnerscorekeeper, sk, true);
497  if(c < 0)
498  {
501  secondscorekeeper = winnerscorekeeper;
502  winnerscorekeeper = sk;
503  }
504  else
505  {
506  c = PlayerScore_Compare(secondscorekeeper, sk, true);
507  if(c < 0)
508  {
510  secondscorekeeper = sk;
511  }
512  }
513  });
514 
515  WinningConditionHelper_equality = (PlayerScore_Compare(winnerscorekeeper, secondscorekeeper, false) == 0);
516  if(WinningConditionHelper_equality)
518 
519  WinningConditionHelper_topscore = winnerscorekeeper.scores_primary;
520  WinningConditionHelper_secondscore = secondscorekeeper.scores_primary;
523 
524  WinningConditionHelper_winnerteam = -1; // no teamplay
525  WinningConditionHelper_secondteam = -1; // no teamplay
526  }
527 
529  {
531  {
534  else
535  WinningConditionHelper_topscore = -999999999;
536  }
537  if(player_count == 0) // special case: empty servers DO end the match at a 0:0 tie
539  }
540 
542  {
544  {
547  else
549  }
550  }
551 
552  strcpy(worldstatus, s);
553 
554  FOREACH_CLIENT(true, {
555  string s = "";
556  if(fullstatus)
557  {
558  s = GetPlayerScoreString(it, 1);
559  s = strcat(s, IS_REAL_CLIENT(it) ? ":human" : ":bot");
560  if(!(IS_PLAYER(it) || INGAME_JOINED(it)))
561  s = strcat(s, ":spectator");
562  }
563  else
564  {
565  if (IS_PLAYER(it) || INGAME_JOINED(it))
566  s = GetPlayerScoreString(it, 2);
567  else
568  s = "-666";
569  }
570 
571  strcpy(it.clientstatus, s);
572  });
573 }
574 
575 string GetScoreLogLabel(string label, float fl)
576 {
577  if(fl & SFL_LOWER_IS_BETTER)
578  label = strcat(label, "<");
580  label = strcat(label, "!!");
581  else if((fl & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_SECONDARY)
582  label = strcat(label, "!");
583  return label;
584 }
585 
586 string GetPlayerScoreString(entity pl, float shortString)
587 {
588  string out;
589  entity sk;
590  float f;
591  string l;
592 
593  out = "";
594  if(!pl)
595  {
596  // label
597  FOREACH(Scores, true, {
599  {
600  f = scores_flags(it);
601  l = scores_label(it);
602  out = strcat(out, GetScoreLogLabel(l, f), ",");
603  }
604  });
605  if(shortString < 2)
606  FOREACH(Scores, true, {
608  {
609  f = scores_flags(it);
610  l = scores_label(it);
611  out = strcat(out, GetScoreLogLabel(l, f), ",");
612  }
613  });
614  if(shortString < 1)
615  FOREACH(Scores, true, {
617  if((scores_flags(it) & SFL_SORT_PRIO_MASK) != SFL_SORT_PRIO_SECONDARY)
618  {
619  f = scores_flags(it);
620  l = scores_label(it);
621  out = strcat(out, GetScoreLogLabel(l, f), ",");
622  }
623  });
624  out = substring(out, 0, strlen(out) - 1);
625  }
626  else if((sk = CS(pl).scorekeeper))
627  {
628  FOREACH(Scores, true, {
630  out = strcat(out, ftos(sk.(scores(it))), ",");
631  });
632  if(shortString < 2)
633  FOREACH(Scores, true, {
635  out = strcat(out, ftos(sk.(scores(it))), ",");
636  });
637  if(shortString < 1)
638  FOREACH(Scores, true, {
640  if((scores_flags(it) & SFL_SORT_PRIO_MASK) != SFL_SORT_PRIO_SECONDARY)
641  out = strcat(out, ftos(sk.(scores(it))), ",");
642  });
643  out = substring(out, 0, strlen(out) - 1);
644  }
645  return out;
646 }
647 
648 string GetTeamScoreString(float tm, float shortString)
649 {
650  string out;
651  entity sk;
652  float i, f;
653  string l;
654 
655  out = "";
656  if(tm == 0)
657  {
658  // label
659  for(i = 0; i < MAX_TEAMSCORE; ++i)
661  {
662  f = teamscores_flags(i);
663  l = teamscores_label(i);
664  out = strcat(out, GetScoreLogLabel(l, f), ",");
665  }
666  if(shortString < 2)
667  for(i = 0; i < MAX_TEAMSCORE; ++i)
668  if((teamscores_flags(i) & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_SECONDARY)
669  {
670  f = teamscores_flags(i);
671  l = teamscores_label(i);
672  out = strcat(out, GetScoreLogLabel(l, f), ",");
673  }
674  if(shortString < 1)
675  for(i = 0; i < MAX_TEAMSCORE; ++i)
676  if((teamscores_flags(i) & SFL_SORT_PRIO_MASK) != SFL_SORT_PRIO_PRIMARY)
677  if((teamscores_flags(i) & SFL_SORT_PRIO_MASK) != SFL_SORT_PRIO_SECONDARY)
678  {
679  f = teamscores_flags(i);
680  l = teamscores_label(i);
681  out = strcat(out, GetScoreLogLabel(l, f), ",");
682  }
683  out = substring(out, 0, strlen(out) - 1);
684  }
685  else if((sk = teamscorekeepers[tm - 1]))
686  {
687  for(i = 0; i < MAX_TEAMSCORE; ++i)
689  out = strcat(out, ftos(sk.(teamscores(i))), ",");
690  if(shortString < 2)
691  for(i = 0; i < MAX_TEAMSCORE; ++i)
693  out = strcat(out, ftos(sk.(teamscores(i))), ",");
694  if(shortString < 1)
695  for(i = 0; i < MAX_TEAMSCORE; ++i)
698  out = strcat(out, ftos(sk.(teamscores(i))), ",");
699  out = substring(out, 0, strlen(out) - 1);
700  }
701  return out;
702 }
703 
704 float PlayerTeamScore_Compare(entity p1, entity p2, float teams, bool strict)
705 {
706  if(teams && teamscores_entities_count)
707  {
708  if(p1.team != p2.team)
709  {
710  entity t1, t2;
711  float r;
712  t1 = teamscorekeepers[p1.team - 1];
713  t2 = teamscorekeepers[p2.team - 1];
714  r = TeamScore_Compare(t1, t2, ((teams >= 0) ? 1 : strict));
715  return r;
716  }
717  if(teams < 0)
718  return 0;
719  }
720 
721  return PlayerScore_Compare(CS(p1).scorekeeper, CS(p2).scorekeeper, strict);
722 }
723 
724 entity PlayerScore_Sort(.float field, int teams, bool strict, bool nospectators)
725 {
726  entity p, plist, pprev, pbest, pbestprev, pfirst, plast;
727  float i, j;
728 
729  plist = NULL;
730 
731  FOREACH_CLIENT(true, { it.(field) = 0; });
732 
734  {
735  if(nospectators)
736  if(it.frags == FRAGS_SPECTATOR)
737  continue;
738 
739  it.chain = plist;
740  plist = it;
741  });
742  // Now plist points to the whole list.
743 
744  pfirst = plast = NULL;
745 
746  i = j = 0;
747  while(plist)
748  {
749  pprev = pbestprev = NULL;
750  pbest = plist;
751  for(p = plist; (pprev = p), (p = p.chain); )
752  {
753  if(PlayerTeamScore_Compare(p, pbest, teams, strict) > 0)
754  {
755  pbest = p;
756  pbestprev = pprev;
757  }
758  }
759 
760  // remove pbest out of the chain
761  if(pbestprev == NULL)
762  plist = pbest.chain;
763  else
764  pbestprev.chain = pbest.chain;
765  pbest.chain = NULL;
766 
767  ++i;
768  if(!plast || PlayerTeamScore_Compare(plast, pbest, teams, strict))
769  j = i;
770 
771  pbest.(field) = j;
772 
773  if (!pfirst)
774  pfirst = pbest;
775  if(plast)
776  plast.chain = pbest;
777  plast = pbest;
778  }
779 
780  return pfirst;
781 }
782 
784 {
785  float s;
786  entity sk;
787 
788  if(t <= 0 || t >= 16)
789  {
790  if(game_stopped)
791  return 0;
792  error("Reading score of invalid team!");
793  }
794 
795  sk = teamscorekeepers[t - 1];
796  if (!sk)
797  return -999999999;
798  s = sk.teamscores_primary;
800  if(!s)
801  return -999999999;
803  s = -s;
804  return s;
805 }
806 
807 const float NAMEWIDTH = 22;
808 const float SCORESWIDTH = 58;
809 // TODO put this somewhere in common?
810 string Score_NicePrint_ItemColor(float vflags)
811 {
812  if(vflags & SFL_SORT_PRIO_PRIMARY)
813  return "^3";
814  else if(vflags & SFL_SORT_PRIO_SECONDARY)
815  return "^5";
816  else
817  return "^7";
818 }
819 
820 void Score_NicePrint_Team(entity to, float t, float w)
821 {
822  string s, s2;
823  float i;
824  entity sk;
825  float fl, sc;
826  s = "";
827 
828  sk = teamscorekeepers[t - 1];
829  if(sk)
830  {
831  s = strcat(s, Team_ColoredFullName(t));
832  for(i = 0; i < MAX_TEAMSCORE; ++i)
833  if(teamscores_label(i) != "")
834  {
835  fl = teamscores_flags(i);
836  sc = sk.(teamscores(i));
837  s = strcat(s, " ", Score_NicePrint_ItemColor(fl), ScoreString(fl, sc));
838  }
839  }
840  else
841  s = "Scores:";
842 
843  s = strcat(s, strpad(max(0, NAMEWIDTH - strlennocol(s)), ""));
844 
845  FOREACH(Scores, true, {
846  if(scores_label(it) != "")
847  {
848  fl = scores_flags(it);
849  s2 = scores_label(it);
850  s = strcat(s, " ", Score_NicePrint_ItemColor(fl), strpad(-w, substring(s2, 0, w)));
851  }
852  });
853 
854  print_to(to, s);
855 }
856 
858 {
859  string s;
860  float i;
861  entity sk;
862  float fl, sc;
863  s = " ";
864 
865  sk = CS(p).scorekeeper;
866 
867  s = strcat(s, playername(p.netname, p.team, false));
868  for (;;)
869  {
870  i = strlennocol(s) - NAMEWIDTH;
871  if(i > 0)
872  s = substring(s, 0, strlen(s) - i);
873  else
874  {
875  s = strcat(s, strpad(i, ""));
876  break;
877  }
878  }
879 
880  FOREACH(Scores, true, {
881  if(scores_label(it) != "")
882  {
883  fl = scores_flags(it);
884  sc = sk.(scores(it));
885  s = strcat(s, " ", Score_NicePrint_ItemColor(fl), strpad(-w, ScoreString(fl, sc)));
886  }
887  });
888 
889  print_to(to, s);
890 }
891 
893 {
894  print_to(to, "Spectators:");
895 }
896 
898 {
899  print_to(to, strcat(" ", playername(p.netname, p.team, false)));
900 }
901 
904 {
905  entity p;
906  float w;
907 
908  int t = 0;
909  FOREACH(Scores, true, {
910  if(scores_label(it) != "")
911  ++t;
912  });
913  w = bound(6, floor(SCORESWIDTH / t - 1), 9);
914 
915  p = PlayerScore_Sort(score_dummyfield, 1, true, false);
916  t = -1;
917 
919  Score_NicePrint_Team(to, t, w);
920  while(p)
921  {
923  if(t != p.team)
924  Score_NicePrint_Team(to, p.team, w);
925  Score_NicePrint_Player(to, p, w);
926  t = p.team;
927  p = p.chain;
928  }
929 
930  t = 0;
931  FOREACH_CLIENT(!IS_PLAYER(it), {
932  if (!t)
935  t = 1;
936  });
937 }
938 
940 {
941  entity s = CS(p).scorekeeper;
942  FOREACH(Scores, true, {
943  if(s.(scores(it)) != 0 && scores_label(it) != "")
944  PlayerStats_GameReport_Event_Player(s.owner,
945  strcat(PLAYERSTATS_SCOREBOARD, scores_label(it)), s.(scores(it)));
946  });
947 }
948 
950 {
951  entity sk;
952  float t, i;
953  for(t = 0; t < 16; ++t)
954  {
955  sk = teamscorekeepers[t];
956  if(!sk)
957  continue;
958  for(i = 0; i < MAX_TEAMSCORE; ++i)
959  if(sk.(teamscores(i)) != 0 && teamscores_label(i) != "")
960  // the +1 is important here!
961  PlayerStats_GameReport_Event_Team(t+1,
962  strcat(PLAYERSTATS_SCOREBOARD, teamscores_label(i)), sk.(teamscores(i)));
963  }
964 }
#define assert(expr,...)
Definition: log.qh:8
int serverflags
Definition: main.qh:184
#define LOG_WARN(...)
Definition: log.qh:66
bool autocvar_g_full_getstatus_responses
Definition: scores.qh:5
float PlayerScore_Compare(entity t1, entity t2, bool strict)
Definition: scores.qc:399
void Score_NicePrint_Player(entity to, entity p, float w)
Definition: scores.qc:857
const int NUM_TEAM_2
Definition: teams.qh:19
string GetTeamScoreString(float tm, float shortString)
Definition: scores.qc:648
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 s2loc s1 s2 f1 f1points s1 s2
Definition: all.inc:438
float score_dummyfield
Definition: scores.qc:902
const float NAMEWIDTH
Definition: scores.qc:807
const int SFL_SORT_PRIO_MASK
Definition: scores.qh:128
bool PlayerScore_SendEntity(entity this, entity to, float sendflags)
Definition: scores.qc:239
int team
Definition: main.qh:157
float WinningConditionHelper_secondteam
the color of the second team, or -1 if none
Definition: scores.qh:111
void Score_NicePrint(entity to)
Prints the scores to the console of a player.
Definition: scores.qc:903
float TeamScore_AddToTeam(int t, float scorefield, float score)
Adds a score to the given team.
Definition: scores.qc:108
float TeamScore_Compare(entity t1, entity t2, bool strict)
Definition: scores.qc:142
const int SFL_SORT_PRIO_SECONDARY
Scoring priority (NOTE: PRIMARY is used for fraglimit)
Definition: scores.qh:126
entity() spawn
entity teamscorekeepers[16]
Definition: scores.qc:20
int player_count
Definition: api.qh:103
vector ScoreField_Compare(entity t1, entity t2,.float field, float fieldflags, vector previous, bool strict)
Definition: scores.qc:27
float scores_flags_primary
Definition: scores.qc:24
ClientState CS(Client this)
Definition: state.qh:47
void TeamScore_Spawn(float t, string name)
Definition: scores.qc:97
void Score_ClearAll()
Clear ALL scores (for ready-restart).
Definition: scores.qc:288
#define FOREACH_CLIENT(cond, body)
Definition: utils.qh:49
Gametype MapInfo_LoadedGametype
Definition: mapinfo.qh:193
bool warmup_stage
Definition: main.qh:103
const int SFL_SORT_PRIO_PRIMARY
Definition: scores.qh:127
void PlayerScore_Detach(entity player)
Detaches a PlayerScore entity from the player.
Definition: scores.qc:328
void ScoreInfo_SetLabel_PlayerScore(PlayerScoreField i, string label, float scoreflags)
Set the label of a player score item, as well as the scoring flags.
Definition: scores.qc:165
float WinningConditionHelper_lowerisbetter
lower is better, duh
Definition: scores.qh:115
entity to
Definition: self.qh:96
void print_to(entity to, string input)
Definition: common.qc:172
#define WriteRegistered(r, to, it)
Definition: net.qh:294
const float SCORESWIDTH
Definition: scores.qc:808
#define SFL_ZERO_IS_WORST
Definition: scores.qh:121
entity result
Definition: promise.qc:43
entity owner
Definition: main.qh:73
const int SFL_LOWER_IS_BETTER
Lower scores are better (e.g.
Definition: scores.qh:98
#define IS_REAL_CLIENT(v)
Definition: utils.qh:17
void Score_NicePrint_Team(entity to, float t, float w)
Definition: scores.qc:820
#define strcpy(this, s)
Definition: string.qh:49
entity teams
Definition: main.qh:44
void PlayerScore_PlayerStats(entity p)
Definition: scores.qc:939
#define teamscores_label(i)
Definition: scores.qh:147
string modname
Definition: world.qh:45
entity WinningConditionHelper_winner
the winning player, or NULL if none
Definition: scores.qh:113
bool ScoreInfo_SendEntity(entity this, entity to, int sf)
Definition: scores.qc:197
void ScoreInfo_Init(int teams)
Definition: scores.qc:214
void Score_NicePrint_Spectators(entity to)
Definition: scores.qc:892
#define BIT(n)
Only ever assign into the first 24 bits in QC (so max is BIT(23)).
Definition: bits.qh:8
#define FOREACH_CLIENTSLOT(cond, body)
Definition: utils.qh:39
float WinningConditionHelper_zeroisworst
zero is worst, duh
Definition: scores.qh:116
void PlayerScore_TeamStats()
Definition: scores.qc:949
string GetScoreLogLabel(string label, float fl)
Definition: scores.qc:575
float cvar_purechanges_count
Definition: world.qh:43
entity scores_initialized
Definition: scores.qh:7
float WinningConditionHelper_secondscore
second highest score
Definition: scores.qh:109
spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 f1 s1 strcat(_("Level %s: "), "^BG%s\3\, _("^BGPress ^F2%s^BG to enter the game"))
entity scorekeeper
Definition: scores.qc:19
#define MAX_TEAMSCORE
Definition: scores.qh:142
var float teamscores_primary
Definition: scores.qc:23
entity WinningConditionHelper_second
the second player, or NULL if none
Definition: scores.qh:114
float PlayerTeamScore_Add(entity player, PlayerScoreField pscorefield, float tscorefield, float score)
Adds a score to both the player and the team.
Definition: scores.qc:390
#define NULL
Definition: post.qh:17
float TeamScore_GetCompareValue(float t)
Returns a value indicating the team score (and higher is better).
Definition: scores.qc:783
float PlayerScore_Clear(entity player)
Initialize the score of this player if needed.
Definition: scores.qc:267
void Score_NicePrint_Spectator(entity to, entity p)
Definition: scores.qc:897
float PlayerTeamScore_Compare(entity p1, entity p2, float teams, bool strict)
Definition: scores.qc:704
#define M_ARGV(x, type)
Definition: events.qh:17
#define scores_flags(this)
Definition: scores.qh:140
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:724
vector(float skel, float bonenum) _skel_get_boneabs_hidden
const int NUM_TEAM_4
Definition: teams.qh:21
void ScoreInfo_SetLabel_TeamScore(float i, string label, float scoreflags)
Set the label of a team score item, as well as the scoring flags.
Definition: scores.qc:181
#define scores_label(this)
Definition: scores.qh:139
bool TeamScore_SendEntity(entity this, entity to, float sendflags)
Definition: scores.qc:64
float WinningConditionHelper_winnerteam
the color of the winning team, or -1 if none
Definition: scores.qh:110
string GetGametype()
Definition: intermission.qc:13
#define INGAME_JOINED(it)
Definition: sv_rules.qh:21
const int FRAGS_SPECTATOR
Definition: constants.qh:4
#define MUTATOR_CALLHOOK(id,...)
Definition: base.qh:140
float teamscores_flags_primary
Definition: scores.qc:25
string sv_termsofservice_url_escaped
Definition: world.qh:53
float teamscores_entities_count
Definition: scores.qc:21
#define new_pure(class)
purely logical entities (.origin doesn&#39;t work)
Definition: oo.qh:62
entity int sendflags
Definition: self.qh:96
string Score_NicePrint_ItemColor(float vflags)
Definition: scores.qc:810
int nJoinAllowed(entity this, entity ignore)
Determines whether the player is allowed to join.
Definition: client.qc:2006
const int NUM_TEAM_1
Definition: teams.qh:18
#define teamscores_flags(i)
Definition: scores.qh:149
var float scores_primary
Definition: scores.qc:22
float WinningConditionHelper_topscore
highest score
Definition: scores.qh:108
float TeamScore_Add(entity player, float scorefield, float score)
Adds a score to the player&#39;s team&#39;s scores.
Definition: scores.qc:137
string GetPlayerScoreString(entity pl, float shortString)
Returns score strings for eventlog etc.
Definition: scores.qc:586
float WinningConditionHelper_equality
we have no winner
Definition: scores.qh:112
float PlayerScore_Add(entity player, PlayerScoreField scorefield, float score)
Adds a score to the player&#39;s scores.
Definition: scores.qc:336
void PlayerScore_Attach(entity player)
Attaches a PlayerScore entity to a player.
Definition: scores.qc:318
#define Team_ColoredFullName(teamid)
Definition: teams.qh:230
#define FOREACH(list, cond, body)
Definition: iter.qh:19
#define teamscores(i)
Definition: scores.qh:145
entity PlayerScoreField
Definition: scores.qh:133
#define IS_PLAYER(v)
Definition: utils.qh:9
const int NUM_TEAM_3
Definition: teams.qh:20
void WinningConditionHelper(entity this)
Sets the following results for the current scores entities.
Definition: scores.qc:415
string worldstatus
string autocvar_g_xonoticversion
Definition: client.qh:45
float PlayerScore_Set(entity player, PlayerScoreField scorefield, float score)
Sets the player&#39;s score to the score parameter.
Definition: scores.qc:368
#define scores(this)
Definition: scores.qh:138