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