]> de.git.xonotic.org Git - voretournament/voretournament.git/blob - data/qcsrc/server/scores.qc
Set good crosshair defaults for the new crosshairs
[voretournament/voretournament.git] / data / qcsrc / server / scores.qc
1 .entity scorekeeper;\r
2 entity teamscorekeepers[16];\r
3 string scores_label[MAX_SCORE];\r
4 float scores_flags[MAX_SCORE];\r
5 string teamscores_label[MAX_TEAMSCORE];\r
6 float teamscores_flags[MAX_TEAMSCORE];\r
7 float teamscores_entities_count;\r
8 var .float scores_primary;\r
9 var .float teamscores_primary;\r
10 float scores_flags_primary;\r
11 float teamscores_flags_primary;\r
12 \r
13 vector ScoreField_Compare(entity t1, entity t2, .float field, float fieldflags, vector previous) // returns: cmp value, best prio\r
14 {\r
15         if(!(fieldflags & SFL_SORT_PRIO_MASK)) // column does not sort\r
16                 return previous;\r
17         if(fieldflags & SFL_SORT_PRIO_MASK < previous_y)\r
18                 return previous;\r
19         if(t1.field == t2.field)\r
20                 return previous;\r
21 \r
22         previous_y = fieldflags & SFL_SORT_PRIO_MASK;\r
23 \r
24         if(fieldflags & SFL_ZERO_IS_WORST)\r
25         {\r
26                 if(t1.field == 0)\r
27                 {\r
28                         previous_x = -1;\r
29                         return previous;\r
30                 }\r
31                 else if(t2.field == 0)\r
32                 {\r
33                         previous_x = +1;\r
34                         return previous;\r
35                 }\r
36         }\r
37 \r
38         if(fieldflags & SFL_LOWER_IS_BETTER)\r
39                 previous_x = (t2.field - t1.field);\r
40         else\r
41                 previous_x = (t1.field - t2.field);\r
42 \r
43         return previous;\r
44 }\r
45 \r
46 /*\r
47  * teamscore entities\r
48  */\r
49 \r
50 float TeamScore_SendEntity(entity to, float sendflags)\r
51 {\r
52         float i, p, longflags;\r
53 \r
54         WriteByte(MSG_ENTITY, ENT_CLIENT_TEAMSCORES);\r
55         WriteByte(MSG_ENTITY, self.team - 1);\r
56 \r
57         longflags = 0;\r
58         for(i = 0, p = 1; i < MAX_TEAMSCORE; ++i, p *= 2)\r
59                 if(self.teamscores[i] > 127 || self.teamscores[i] <= -128)\r
60                         longflags |= p;\r
61 \r
62 #if MAX_TEAMSCORE <= 8\r
63         WriteByte(MSG_ENTITY, sendflags);\r
64         WriteByte(MSG_ENTITY, longflags);\r
65 #else\r
66         WriteShort(MSG_ENTITY, sendflags);\r
67         WriteShort(MSG_ENTITY, longflags);\r
68 #endif\r
69         for(i = 0, p = 1; i < MAX_TEAMSCORE; ++i, p *= 2)\r
70                 if(sendflags & p)\r
71                 {\r
72                         if(longflags & p)\r
73                                 WriteInt24_t(MSG_ENTITY, self.teamscores[i]);\r
74                         else\r
75                                 WriteChar(MSG_ENTITY, self.teamscores[i]);\r
76                 }\r
77 \r
78         return TRUE;\r
79 }\r
80 \r
81 void TeamScore_Spawn(float t, string name)\r
82 {\r
83         entity ts;\r
84         ts = spawn();\r
85         ts.classname = "csqc_score_team";\r
86         ts.netname = name; // not used yet, FIXME\r
87         ts.team = t;\r
88         Net_LinkEntity(ts, FALSE, 0, TeamScore_SendEntity);\r
89         teamscorekeepers[t - 1] = ts;\r
90         ++teamscores_entities_count;\r
91 }\r
92 \r
93 float TeamScore_AddToTeam(float t, float scorefield, float score)\r
94 {\r
95         entity s;\r
96 \r
97         if(gameover)\r
98                 score = 0;\r
99 \r
100         if(!scores_initialized) return 0; // FIXME remove this when everything uses this system\r
101         if(t <= 0 || t >= 16)\r
102         {\r
103                 if(gameover)\r
104                         return 0;\r
105                 error("Adding score to invalid team!");\r
106         }\r
107         s = teamscorekeepers[t - 1];\r
108         if(!s)\r
109         {\r
110                 if(gameover)\r
111                         return 0;\r
112                 error("Adding score to unknown team!");\r
113         }\r
114         if(score)\r
115                 if(teamscores_label[scorefield] != "")\r
116                         s.SendFlags |= pow(2, scorefield);\r
117         return (s.(teamscores[scorefield]) += score);\r
118 }\r
119 \r
120 float TeamScore_Add(entity player, float scorefield, float score)\r
121 {\r
122         return TeamScore_AddToTeam(player.team, scorefield, score);\r
123 }\r
124 \r
125 float TeamScore_Compare(entity t1, entity t2)\r
126 {\r
127         if(!t1 || !t2) return (!t2) - !t1;\r
128 \r
129         vector result;\r
130         float i;\r
131         for(i = 0; i < MAX_TEAMSCORE; ++i)\r
132         {\r
133                 var .float f;\r
134                 f = teamscores[i];\r
135                 result = ScoreField_Compare(t1, t2, f, teamscores_flags[i], result);\r
136         }\r
137         return result_x;\r
138 }\r
139 \r
140 /*\r
141  * the scoreinfo entity\r
142  */\r
143 \r
144 void ScoreInfo_SetLabel_PlayerScore(float i, string label, float scoreflags)\r
145 {\r
146         scores_label[i] = label;\r
147         scores_flags[i] = scoreflags;\r
148         if(scoreflags & SFL_SORT_PRIO_MASK == SFL_SORT_PRIO_PRIMARY)\r
149         {\r
150                 scores_primary = scores[i];\r
151                 scores_flags_primary = scoreflags;\r
152         }\r
153 }\r
154 \r
155 void ScoreInfo_SetLabel_TeamScore(float i, string label, float scoreflags)\r
156 {\r
157         teamscores_label[i] = label;\r
158         teamscores_flags[i] = scoreflags;\r
159         if(scoreflags & SFL_SORT_PRIO_MASK == SFL_SORT_PRIO_PRIMARY)\r
160         {\r
161                 teamscores_primary = teamscores[i];\r
162                 teamscores_flags_primary = scoreflags;\r
163         }\r
164 }\r
165 \r
166 float ScoreInfo_SendEntity(entity to, float sf)\r
167 {\r
168         float i;\r
169         WriteByte(MSG_ENTITY, ENT_CLIENT_SCORES_INFO);\r
170         WriteByte(MSG_ENTITY, game);\r
171         for(i = 0; i < MAX_SCORE; ++i)\r
172         {\r
173                 WriteString(MSG_ENTITY, scores_label[i]);\r
174                 WriteByte(MSG_ENTITY, scores_flags[i]);\r
175         }\r
176         for(i = 0; i < MAX_TEAMSCORE; ++i)\r
177         {\r
178                 WriteString(MSG_ENTITY, teamscores_label[i]);\r
179                 WriteByte(MSG_ENTITY, teamscores_flags[i]);\r
180         }\r
181         return TRUE;\r
182 }\r
183 \r
184 void ScoreInfo_Init(float teams)\r
185 {\r
186         if(scores_initialized)\r
187         {\r
188                 scores_initialized.SendFlags |= 1; // force a resend\r
189         }\r
190         else\r
191         {\r
192                 scores_initialized = spawn();\r
193                 scores_initialized.classname = "ent_client_scoreinfo";\r
194                 Net_LinkEntity(scores_initialized, FALSE, 0, ScoreInfo_SendEntity);\r
195         }\r
196         if(teams >= 1)\r
197                 TeamScore_Spawn(COLOR_TEAM1, "Red");\r
198         if(teams >= 2)\r
199                 TeamScore_Spawn(COLOR_TEAM2, "Blue");\r
200         if(teams >= 3)\r
201                 TeamScore_Spawn(COLOR_TEAM3, "Yellow");\r
202         if(teams >= 4)\r
203                 TeamScore_Spawn(COLOR_TEAM4, "Pink");\r
204 }\r
205 \r
206 /*\r
207  * per-player score entities\r
208  */\r
209 \r
210 float PlayerScore_SendEntity(entity to, float sendflags)\r
211 {\r
212         float i, p, longflags;\r
213 \r
214         WriteByte(MSG_ENTITY, ENT_CLIENT_SCORES);\r
215         WriteByte(MSG_ENTITY, num_for_edict(self.owner));\r
216 \r
217         longflags = 0;\r
218         for(i = 0, p = 1; i < MAX_SCORE; ++i, p *= 2)\r
219                 if(self.scores[i] > 127 || self.scores[i] <= -128)\r
220                         longflags |= p;\r
221 \r
222 #if MAX_SCORE <= 8\r
223         WriteByte(MSG_ENTITY, sendflags);\r
224         WriteByte(MSG_ENTITY, longflags);\r
225 #else\r
226         WriteShort(MSG_ENTITY, sendflags);\r
227         WriteShort(MSG_ENTITY, longflags);\r
228 #endif\r
229         for(i = 0, p = 1; i < MAX_SCORE; ++i, p *= 2)\r
230                 if(sendflags & p)\r
231                 {\r
232                         if(longflags & p)\r
233                                 WriteInt24_t(MSG_ENTITY, self.scores[i]);\r
234                         else\r
235                                 WriteChar(MSG_ENTITY, self.scores[i]);\r
236                 }\r
237 \r
238         return TRUE;\r
239 }\r
240 \r
241 void PlayerScore_Clear(entity player)\r
242 {\r
243         entity sk;\r
244         float i;\r
245 \r
246         if(teamscores_entities_count)\r
247                 return;\r
248         if(g_lms) return;\r
249         if(g_arena || g_ca) return;\r
250         if(g_race && !g_race_qualifying) return;\r
251 \r
252         sk = player.scorekeeper;\r
253         for(i = 0; i < MAX_SCORE; ++i)\r
254         {\r
255                 if(sk.(scores[i]) != 0)\r
256                         if(scores_label[i] != "")\r
257                                 sk.SendFlags |= pow(2, i);\r
258                 sk.(scores[i]) = 0;\r
259         }\r
260 }\r
261 \r
262 void Score_ClearAll()\r
263 {\r
264         entity p, sk;\r
265         float i, t;\r
266         FOR_EACH_CLIENTSLOT(p)\r
267         {\r
268                 sk = p.scorekeeper;\r
269                 if(!sk)\r
270                         continue;\r
271                 for(i = 0; i < MAX_SCORE; ++i)\r
272                 {\r
273                         if(sk.(scores[i]) != 0)\r
274                                 if(scores_label[i] != "")\r
275                                         sk.SendFlags |= pow(2, i);\r
276                         sk.(scores[i]) = 0;\r
277                 }\r
278         }\r
279         for(t = 0; t < 16; ++t)\r
280         {\r
281                 sk = teamscorekeepers[t];\r
282                 if(!sk)\r
283                         continue;\r
284                 for(i = 0; i < MAX_TEAMSCORE; ++i)\r
285                 {\r
286                         if(sk.(teamscores[i]) != 0)\r
287                                 if(teamscores_label[i] != "")\r
288                                         sk.SendFlags |= pow(2, i);\r
289                         sk.(teamscores[i]) = 0;\r
290                 }\r
291         }\r
292 }\r
293 \r
294 void PlayerScore_Attach(entity player)\r
295 {\r
296         entity sk;\r
297         if(player.scorekeeper)\r
298                 error("player already has a scorekeeper");\r
299         sk = spawn();\r
300         sk.owner = player;\r
301         Net_LinkEntity(sk, FALSE, 0, PlayerScore_SendEntity);\r
302         player.scorekeeper = sk;\r
303 }\r
304 \r
305 void PlayerScore_Detach(entity player)\r
306 {\r
307         if(!player.scorekeeper)\r
308                 error("player has no scorekeeper");\r
309         remove(player.scorekeeper);\r
310         player.scorekeeper = world;\r
311 }\r
312 \r
313 float PlayerScore_Add(entity player, float scorefield, float score)\r
314 {\r
315         entity s;\r
316 \r
317         if(gameover)\r
318         if not(g_lms && scorefield == SP_LMS_RANK) // allow writing to this field in intermission as it is needed for newly joining players\r
319                 score = 0;\r
320 \r
321         if(!scores_initialized) return 0; // FIXME remove this when everything uses this system\r
322         s = player.scorekeeper;\r
323         if(!s)\r
324         {\r
325                 if(gameover)\r
326                         return 0;\r
327                 error("Adding score to unknown player!");\r
328         }\r
329         if(score)\r
330                 if(scores_label[scorefield] != "")\r
331                         s.SendFlags |= pow(2, scorefield);\r
332         return (s.(scores[scorefield]) += score);\r
333 }\r
334 \r
335 float PlayerTeamScore_Add(entity player, float pscorefield, float tscorefield, float score)\r
336 {\r
337         float r;\r
338         r = PlayerScore_Add(player, pscorefield, score);\r
339         if(teamscores_entities_count) // only for teamplay\r
340                 r = TeamScore_Add(player, tscorefield, score);\r
341         return r;\r
342 }\r
343 \r
344 float PlayerScore_Compare(entity t1, entity t2)\r
345 {\r
346         if(!t1 || !t2) return (!t2) - !t1;\r
347 \r
348         vector result;\r
349         float i;\r
350         for(i = 0; i < MAX_SCORE; ++i)\r
351         {\r
352                 var .float f;\r
353                 f = scores[i];\r
354                 result = ScoreField_Compare(t1, t2, f, scores_flags[i], result);\r
355         }\r
356         return result_x;\r
357 }\r
358 \r
359 void WinningConditionHelper()\r
360 {\r
361         float c;\r
362         string s;\r
363         entity p;\r
364         float fullstatus;\r
365         entity winnerscorekeeper;\r
366         entity secondscorekeeper;\r
367         entity sk;\r
368 \r
369         s = GetGametype();\r
370         s = strcat(s, ":", cvar_string("g_voretournamentversion"));\r
371         s = strcat(s, "::", GetPlayerScoreString(world, 2)); // make this 1 once we can\r
372 \r
373         fullstatus = cvar("g_full_getstatus_responses");\r
374 \r
375         if(teamscores_entities_count)\r
376         {\r
377                 float t;\r
378 \r
379                 s = strcat(s, ":", GetTeamScoreString(0, 1));\r
380                 for(t = 0; t < 16; ++t)\r
381                         if(teamscorekeepers[t])\r
382                                 s = strcat(s, ":", ftos(t+1), ":", GetTeamScoreString(t+1, 1));\r
383 \r
384                 WinningConditionHelper_winnerteam = -1;\r
385                 WinningConditionHelper_secondteam = -1;\r
386                 winnerscorekeeper = world;\r
387                 secondscorekeeper = world;\r
388                 for(t = 0; t < 16; ++t)\r
389                 {\r
390                         sk = teamscorekeepers[t];\r
391                         c = TeamScore_Compare(winnerscorekeeper, sk);\r
392                         if(c < 0)\r
393                         {\r
394                                 WinningConditionHelper_secondteam = WinningConditionHelper_winnerteam;\r
395                                 WinningConditionHelper_winnerteam = t + 1;\r
396                                 secondscorekeeper = winnerscorekeeper;\r
397                                 winnerscorekeeper = sk;\r
398                         }\r
399                         else\r
400                         {\r
401                                 c = TeamScore_Compare(secondscorekeeper, sk);\r
402                                 if(c < 0)\r
403                                 {\r
404                                         WinningConditionHelper_secondteam = t + 1;\r
405                                         secondscorekeeper = sk;\r
406                                 }\r
407                         }\r
408                 }\r
409 \r
410                 WinningConditionHelper_equality = (TeamScore_Compare(winnerscorekeeper, secondscorekeeper) == 0);\r
411                 if(WinningConditionHelper_equality)\r
412                         WinningConditionHelper_winnerteam = WinningConditionHelper_secondteam = -1;\r
413 \r
414                 WinningConditionHelper_topscore = winnerscorekeeper.teamscores_primary;\r
415                 WinningConditionHelper_secondscore = secondscorekeeper.teamscores_primary;\r
416                 WinningConditionHelper_lowerisbetter = (teamscores_flags_primary & SFL_LOWER_IS_BETTER);\r
417                 WinningConditionHelper_zeroisworst = (teamscores_flags_primary & SFL_ZERO_IS_WORST);\r
418 \r
419                 WinningConditionHelper_winner = world; // not supported in teamplay\r
420                 WinningConditionHelper_second = world; // not supported in teamplay\r
421         }\r
422         else\r
423         {\r
424                 WinningConditionHelper_winner = world;\r
425                 WinningConditionHelper_second = world;\r
426                 winnerscorekeeper = world;\r
427                 secondscorekeeper = world;\r
428                 FOR_EACH_PLAYER(p)\r
429                 {\r
430                         sk = p.scorekeeper;\r
431                         c = PlayerScore_Compare(winnerscorekeeper, sk);\r
432                         if(c < 0)\r
433                         {\r
434                                 WinningConditionHelper_second = WinningConditionHelper_winner;\r
435                                 WinningConditionHelper_winner = p;\r
436                                 secondscorekeeper = winnerscorekeeper;\r
437                                 winnerscorekeeper = sk;\r
438                         }\r
439                         else\r
440                         {\r
441                                 c = PlayerScore_Compare(secondscorekeeper, sk);\r
442                                 if(c < 0)\r
443                                 {\r
444                                         WinningConditionHelper_second = p;\r
445                                         secondscorekeeper = sk;\r
446                                 }\r
447                         }\r
448                 }\r
449 \r
450                 WinningConditionHelper_equality = (PlayerScore_Compare(winnerscorekeeper, secondscorekeeper) == 0);\r
451                 if(WinningConditionHelper_equality)\r
452                         WinningConditionHelper_winner = WinningConditionHelper_second = world;\r
453 \r
454                 WinningConditionHelper_topscore = winnerscorekeeper.scores_primary;\r
455                 WinningConditionHelper_secondscore = secondscorekeeper.scores_primary;\r
456                 WinningConditionHelper_lowerisbetter = (scores_flags_primary & SFL_LOWER_IS_BETTER);\r
457                 WinningConditionHelper_zeroisworst = (scores_flags_primary & SFL_ZERO_IS_WORST);\r
458 \r
459                 WinningConditionHelper_winnerteam = -1; // no teamplay\r
460                 WinningConditionHelper_secondteam = -1; // no teamplay\r
461         }\r
462 \r
463         if(WinningConditionHelper_topscore == 0)\r
464         {\r
465                 if(scores_flags_primary & SFL_ZERO_IS_WORST)\r
466                 {\r
467                         if(WinningConditionHelper_lowerisbetter)\r
468                                 WinningConditionHelper_topscore = 999999999;\r
469                         else\r
470                                 WinningConditionHelper_topscore = -999999999;\r
471                 }\r
472                 WinningConditionHelper_equality = 0;\r
473         }\r
474 \r
475         if(WinningConditionHelper_secondscore == 0)\r
476         {\r
477                 if(scores_flags_primary & SFL_ZERO_IS_WORST)\r
478                 {\r
479                         if(WinningConditionHelper_lowerisbetter)\r
480                                 WinningConditionHelper_secondscore = 999999999;\r
481                         else\r
482                                 WinningConditionHelper_secondscore = -999999999;\r
483                 }\r
484         }\r
485 \r
486         if(worldstatus)\r
487                 strunzone(worldstatus);\r
488         worldstatus = strzone(s);\r
489 \r
490         FOR_EACH_CLIENT(p)\r
491         {\r
492                 if(fullstatus)\r
493                 {\r
494                         s = GetPlayerScoreString(p, 1);\r
495                         if(clienttype(p) == CLIENTTYPE_REAL)\r
496                                 s = strcat(s, ":human");\r
497                         else\r
498                                 s = strcat(s, ":bot");\r
499                         if(p.classname != "player" && !g_arena && !g_ca && !g_lms)\r
500                                 s = strcat(s, ":spectator");\r
501                 }\r
502                 else\r
503                 {\r
504                         if(p.classname == "player" || g_arena || g_ca || g_lms)\r
505                                 s = GetPlayerScoreString(p, 2);\r
506                         else\r
507                                 s = "-666";\r
508                 }\r
509 \r
510                 if(p.clientstatus)\r
511                         strunzone(p.clientstatus);\r
512                 p.clientstatus = strzone(s);\r
513         }\r
514 }\r
515 \r
516 string GetScoreLogLabel(string label, float fl)\r
517 {\r
518         if(fl & SFL_LOWER_IS_BETTER)\r
519                 label = strcat(label, "<");\r
520         if(fl & SFL_SORT_PRIO_MASK == SFL_SORT_PRIO_PRIMARY)\r
521                 label = strcat(label, "!!");\r
522         else if(fl & SFL_SORT_PRIO_MASK == SFL_SORT_PRIO_SECONDARY)\r
523                 label = strcat(label, "!");\r
524         return label;\r
525 }\r
526 \r
527 string GetPlayerScoreString(entity pl, float shortString)\r
528 {\r
529         string out;\r
530         entity sk;\r
531         float i, f;\r
532         string l;\r
533 \r
534         out = "";\r
535         if(!pl)\r
536         {\r
537                 // label\r
538                 for(i = 0; i < MAX_SCORE; ++i)\r
539                         if(scores_flags[i] & SFL_SORT_PRIO_MASK == SFL_SORT_PRIO_PRIMARY)\r
540                         {\r
541                                 f = scores_flags[i];\r
542                                 l = scores_label[i];\r
543                                 out = strcat(out, GetScoreLogLabel(l, f), ",");\r
544                         }\r
545                 if(shortString < 2)\r
546                 for(i = 0; i < MAX_SCORE; ++i)\r
547                         if(scores_flags[i] & SFL_SORT_PRIO_MASK == SFL_SORT_PRIO_SECONDARY)\r
548                         {\r
549                                 f = scores_flags[i];\r
550                                 l = scores_label[i];\r
551                                 out = strcat(out, GetScoreLogLabel(l, f), ",");\r
552                         }\r
553                 if(shortString < 1)\r
554                 for(i = 0; i < MAX_SCORE; ++i)\r
555                         if(scores_flags[i] & SFL_SORT_PRIO_MASK != SFL_SORT_PRIO_PRIMARY)\r
556                         if(scores_flags[i] & SFL_SORT_PRIO_MASK != SFL_SORT_PRIO_SECONDARY)\r
557                         {\r
558                                 f = scores_flags[i];\r
559                                 l = scores_label[i];\r
560                                 out = strcat(out, GetScoreLogLabel(l, f), ",");\r
561                         }\r
562                 out = substring(out, 0, strlen(out) - 1);\r
563         }\r
564         else if((sk = pl.scorekeeper))\r
565         {\r
566                 for(i = 0; i < MAX_SCORE; ++i)\r
567                         if(scores_flags[i] & SFL_SORT_PRIO_MASK == SFL_SORT_PRIO_PRIMARY)\r
568                                 out = strcat(out, ftos(sk.(scores[i])), ",");\r
569                 if(shortString < 2)\r
570                 for(i = 0; i < MAX_SCORE; ++i)\r
571                         if(scores_flags[i] & SFL_SORT_PRIO_MASK == SFL_SORT_PRIO_SECONDARY)\r
572                                 out = strcat(out, ftos(sk.(scores[i])), ",");\r
573                 if(shortString < 1)\r
574                 for(i = 0; i < MAX_SCORE; ++i)\r
575                         if(scores_flags[i] & SFL_SORT_PRIO_MASK != SFL_SORT_PRIO_PRIMARY)\r
576                         if(scores_flags[i] & SFL_SORT_PRIO_MASK != SFL_SORT_PRIO_SECONDARY)\r
577                                 out = strcat(out, ftos(sk.(scores[i])), ",");\r
578                 out = substring(out, 0, strlen(out) - 1);\r
579         }\r
580         return out;\r
581 }\r
582 \r
583 string GetTeamScoreString(float tm, float shortString)\r
584 {\r
585         string out;\r
586         entity sk;\r
587         float i, f;\r
588         string l;\r
589 \r
590         out = "";\r
591         if(tm == 0)\r
592         {\r
593                 // label\r
594                 for(i = 0; i < MAX_SCORE; ++i)\r
595                         if(teamscores_flags[i] & SFL_SORT_PRIO_MASK == SFL_SORT_PRIO_PRIMARY)\r
596                         {\r
597                                 f = teamscores_flags[i];\r
598                                 l = teamscores_label[i];\r
599                                 out = strcat(out, GetScoreLogLabel(l, f), ",");\r
600                         }\r
601                 if(shortString < 2)\r
602                 for(i = 0; i < MAX_SCORE; ++i)\r
603                         if(teamscores_flags[i] & SFL_SORT_PRIO_MASK == SFL_SORT_PRIO_SECONDARY)\r
604                         {\r
605                                 f = teamscores_flags[i];\r
606                                 l = teamscores_label[i];\r
607                                 out = strcat(out, GetScoreLogLabel(l, f), ",");\r
608                         }\r
609                 if(shortString < 1)\r
610                 for(i = 0; i < MAX_SCORE; ++i)\r
611                         if(teamscores_flags[i] & SFL_SORT_PRIO_MASK != SFL_SORT_PRIO_PRIMARY)\r
612                         if(teamscores_flags[i] & SFL_SORT_PRIO_MASK != SFL_SORT_PRIO_SECONDARY)\r
613                         {\r
614                                 f = teamscores_flags[i];\r
615                                 l = teamscores_label[i];\r
616                                 out = strcat(out, GetScoreLogLabel(l, f), ",");\r
617                         }\r
618                 out = substring(out, 0, strlen(out) - 1);\r
619         }\r
620         else if((sk = teamscorekeepers[tm - 1]))\r
621         {\r
622                 for(i = 0; i < MAX_TEAMSCORE; ++i)\r
623                         if(teamscores_flags[i] & SFL_SORT_PRIO_MASK == SFL_SORT_PRIO_PRIMARY)\r
624                                 out = strcat(out, ftos(sk.(teamscores[i])), ",");\r
625                 if(shortString < 2)\r
626                 for(i = 0; i < MAX_TEAMSCORE; ++i)\r
627                         if(teamscores_flags[i] & SFL_SORT_PRIO_MASK == SFL_SORT_PRIO_SECONDARY)\r
628                                 out = strcat(out, ftos(sk.(teamscores[i])), ",");\r
629                 if(shortString < 1)\r
630                 for(i = 0; i < MAX_TEAMSCORE; ++i)\r
631                         if(teamscores_flags[i] & SFL_SORT_PRIO_MASK != SFL_SORT_PRIO_PRIMARY)\r
632                         if(teamscores_flags[i] & SFL_SORT_PRIO_MASK != SFL_SORT_PRIO_SECONDARY)\r
633                                 out = strcat(out, ftos(sk.(teamscores[i])), ",");\r
634                 out = substring(out, 0, strlen(out) - 1);\r
635         }\r
636         return out;\r
637 }\r
638 \r
639 float PlayerTeamScore_Compare(entity p1, entity p2)\r
640 {\r
641         if(teamscores_entities_count)\r
642                 if(p1.team != p2.team)\r
643                 {\r
644                         entity t1, t2;\r
645                         float r;\r
646                         t1 = teamscorekeepers[p1.team - 1];\r
647                         t2 = teamscorekeepers[p2.team - 1];\r
648                         r = TeamScore_Compare(t1, t2);\r
649                         if(r == 0) // ensure a deterministic order\r
650                                 r = p1.team - p2.team;\r
651                         return r;\r
652                 }\r
653         \r
654         return PlayerScore_Compare(p1.scorekeeper, p2.scorekeeper);\r
655 }\r
656 \r
657 entity PlayerScore_Sort(.float field)\r
658 {\r
659         entity p, plist, pprev, pbest, pbestprev, pfirst, plast;\r
660         float i;\r
661 \r
662         plist = world;\r
663 \r
664         FOR_EACH_CLIENT(p)\r
665                 p.field = 0;\r
666 \r
667         FOR_EACH_PLAYER(p) if(p.scorekeeper)\r
668         {\r
669                 p.chain = plist;\r
670                 plist = p;\r
671         }\r
672         // Now plist points to the whole list.\r
673         \r
674         pfirst = plast = world;\r
675 \r
676         i = 0;\r
677         while(plist)\r
678         {\r
679                 pprev = pbestprev = world;\r
680                 pbest = plist;\r
681                 for(p = plist; (pprev = p), (p = p.chain); )\r
682                 {\r
683                         if(PlayerTeamScore_Compare(p, pbest) > 0)\r
684                         {\r
685                                 pbest = p;\r
686                                 pbestprev = pprev;\r
687                         }\r
688                 }\r
689 \r
690                 // remove pbest out of the chain\r
691                 if(pbestprev == world)\r
692                         plist = pbest.chain;\r
693                 else\r
694                         pbestprev.chain = pbest.chain;\r
695                 pbest.chain = world;\r
696 \r
697                 pbest.field = ++i;\r
698 \r
699                 if not(pfirst)\r
700                         pfirst = pbest;\r
701                 if(plast)\r
702                         plast.chain = pbest;\r
703                 plast = pbest;\r
704         }\r
705 \r
706         return pfirst;\r
707 }\r
708 \r
709 float TeamScore_GetCompareValue(float t)\r
710 {\r
711         float s;\r
712         entity sk;\r
713 \r
714         if(t <= 0 || t >= 16)\r
715         {\r
716                 if(gameover)\r
717                         return 0;\r
718                 error("Reading score of invalid team!");\r
719         }\r
720 \r
721         sk = teamscorekeepers[t - 1];\r
722         if not(sk)\r
723                 return -999999999;\r
724         s = sk.teamscores_primary;\r
725         if(teamscores_flags_primary & SFL_ZERO_IS_WORST)\r
726                 if(!s)\r
727                         return -999999999;\r
728         if(teamscores_flags_primary & SFL_LOWER_IS_BETTER)\r
729                 s = -s;\r
730         return s;\r
731 }\r
732 \r
733 #define NAMEWIDTH 22\r
734 #define SCORESWIDTH 58\r
735 // TODO put this somewhere in common?\r
736 string Score_NicePrint_ItemColor(float vflags)\r
737 {\r
738         if(vflags & SFL_SORT_PRIO_PRIMARY)\r
739                 return "^3";\r
740         else if(vflags & SFL_SORT_PRIO_SECONDARY)\r
741                 return "^5";\r
742         else\r
743                 return "^7";\r
744 }\r
745 \r
746 void Score_NicePrint_Team(entity to, float t, float w)\r
747 {\r
748         string s, s2;\r
749         float i;\r
750         entity sk;\r
751         float fl, sc;\r
752         s = "";\r
753 \r
754         sk = teamscorekeepers[t - 1];\r
755         if(sk)\r
756         {\r
757                 s = strcat(s, ColoredTeamName(t));\r
758                 for(i = 0; i < MAX_TEAMSCORE; ++i)\r
759                         if(teamscores_label[i] != "")\r
760                         {\r
761                                 fl = teamscores_flags[i];\r
762                                 sc = sk.(teamscores[i]);\r
763                                 s = strcat(s, " ", Score_NicePrint_ItemColor(fl), ScoreString(fl, sc));\r
764                         }\r
765         }\r
766         else\r
767                 s = "Scores:";\r
768 \r
769         s = strcat(s, strpad(max(0, NAMEWIDTH - strlennocol(s)), ""));\r
770         \r
771         for(i = 0; i < MAX_SCORE; ++i)\r
772                 if(scores_label[i] != "")\r
773                 {\r
774                         fl = scores_flags[i];\r
775                         s2 = scores_label[i];\r
776                         s = strcat(s, " ", Score_NicePrint_ItemColor(fl), strpad(-w, substring(s2, 0, w)));\r
777                 }\r
778 \r
779         print_to(to, s);\r
780 }\r
781 \r
782 void Score_NicePrint_Player(entity to, entity p, float w)\r
783 {\r
784         string s;\r
785         float i;\r
786         entity sk;\r
787         float fl, sc;\r
788         s = "  ";\r
789 \r
790         sk = p.scorekeeper;\r
791 \r
792         s = strcat(s, p.netname);\r
793         for(;;)\r
794         {\r
795                 i = strlennocol(s) - NAMEWIDTH;\r
796                 if(i > 0)\r
797                         s = substring(s, 0, strlen(s) - i);\r
798                 else\r
799                 {\r
800                         s = strcat(s, strpad(i, ""));\r
801                         break;\r
802                 }\r
803         }\r
804         \r
805         for(i = 0; i < MAX_SCORE; ++i)\r
806                 if(scores_label[i] != "")\r
807                 {\r
808                         fl = scores_flags[i];\r
809                         sc = sk.(scores[i]);\r
810                         s = strcat(s, " ", Score_NicePrint_ItemColor(fl), strpad(-w, ScoreString(fl, sc)));\r
811                 }\r
812 \r
813         print_to(to, s);\r
814 }\r
815 \r
816 void Score_NicePrint_Spectators(entity to)\r
817 {\r
818         print_to(to, "Spectators:");\r
819 }\r
820 \r
821 void Score_NicePrint_Spectator(entity to, entity p)\r
822 {\r
823         print_to(to, strcat("  ", p.netname));\r
824 }\r
825 \r
826 .float score_dummyfield;\r
827 void Score_NicePrint(entity to)\r
828 {\r
829         entity p;\r
830         float t, i;\r
831         float w;\r
832 \r
833         t = 0;\r
834         for(i = 0; i < MAX_SCORE; ++i)\r
835                 if(scores_label[i] != "")\r
836                         ++t;\r
837         w = bound(6, floor(SCORESWIDTH / t - 1), 9);\r
838 \r
839         p = PlayerScore_Sort(score_dummyfield);\r
840         t = -1;\r
841 \r
842         if(!teamscores_entities_count)\r
843                 Score_NicePrint_Team(to, t, w);\r
844         while(p)\r
845         {\r
846                 if(teamscores_entities_count)\r
847                         if(t != p.team)\r
848                                 Score_NicePrint_Team(to, p.team, w);\r
849                 Score_NicePrint_Player(to, p, w);\r
850                 t = p.team;\r
851                 p = p.chain;\r
852         }\r
853         \r
854         t = 0;\r
855         FOR_EACH_CLIENT(p)\r
856         if(p.classname != "player")\r
857         {\r
858                 if not(t)\r
859                         Score_NicePrint_Spectators(to);\r
860                 Score_NicePrint_Spectator(to, p);\r
861                 t = 1;\r
862         }\r
863 }\r
864 \r