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