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