]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/scores.qc
monster framework from dpmod
[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         float fullstatus;
327
328         s = GetGametype();
329         s = strcat(s, ":", cvar_string("g_nexuizversion"));
330         s = strcat(s, "::", GetPlayerScoreString(world, 2)); // make this 1 once we can
331
332         fullstatus = cvar("g_full_getstatus_responses");
333
334         if(teamscores_entities_count)
335         {
336                 float t;
337
338                 s = strcat(s, ":", GetTeamScoreString(0, 1));
339                 for(t = 0; t < 16; ++t)
340                         if(teamscorekeepers[t])
341                                 s = strcat(s, ":", ftos(t+1), ":", GetTeamScoreString(t+1, 1));
342
343                 WinningConditionHelper_equality = 0;
344                 WinningConditionHelper_winnerteam = 0;
345                 for(t = 1; t < 16; ++t)
346                 {
347                         entity sk1, sk2;
348                         sk1 = teamscorekeepers[WinningConditionHelper_winnerteam];
349                         sk2 = teamscorekeepers[t];
350                         c = TeamScore_Compare(sk1, sk2);
351                         if(c == 0)
352                                 WinningConditionHelper_equality = 1;
353                         else if(c < 0)
354                         {
355                                 WinningConditionHelper_equality = 0;
356                                 WinningConditionHelper_winnerteam = t;
357                         }
358                 }
359
360                 WinningConditionHelper_topscore = teamscorekeepers[WinningConditionHelper_winnerteam].teamscores_primary;
361                 WinningConditionHelper_lowerisbetter = (teamscores_flags_primary & SFL_LOWER_IS_BETTER);
362                 WinningConditionHelper_zeroisworst = (teamscores_flags_primary & SFL_ZERO_IS_WORST);
363
364                 if(teamscores_flags_primary & SFL_TIME)
365                         WinningConditionHelper_topscore /= 10;
366
367                 if(WinningConditionHelper_equality)
368                         WinningConditionHelper_winnerteam = -1;
369
370                 WinningConditionHelper_winner = world;
371                 if(WinningConditionHelper_equality)
372                         WinningConditionHelper_winnerteam = -1;
373                 else
374                         ++WinningConditionHelper_winnerteam; // map to Nexuiz team numbers (as opposed to colors)
375
376                 if(WinningConditionHelper_topscore == 0)
377                 {
378                         if(scores_flags_primary & SFL_ZERO_IS_WORST)
379                         {
380                                 if(WinningConditionHelper_lowerisbetter)
381                                         WinningConditionHelper_topscore = 999999999;
382                                 else
383                                         WinningConditionHelper_topscore = -999999999;
384                         }
385                         WinningConditionHelper_equality = 0;
386                 }
387         }
388         else
389         {
390                 WinningConditionHelper_equality = 0;
391                 WinningConditionHelper_winner = world;
392                 FOR_EACH_PLAYER(p)
393                 {
394                         c = PlayerScore_Compare(WinningConditionHelper_winner.scorekeeper, p.scorekeeper);
395                         if(c == 0)
396                                 WinningConditionHelper_equality = 1;
397                         else if(c < 0)
398                         {
399                                 WinningConditionHelper_equality = 0;
400                                 WinningConditionHelper_winner = p;
401                         }
402                 }
403
404                 WinningConditionHelper_topscore = WinningConditionHelper_winner.scorekeeper.scores_primary;
405                 WinningConditionHelper_lowerisbetter = (scores_flags_primary & SFL_LOWER_IS_BETTER);
406                 WinningConditionHelper_zeroisworst = (teamscores_flags_primary & SFL_ZERO_IS_WORST);
407
408                 if(teamscores_flags_primary & SFL_TIME)
409                         WinningConditionHelper_topscore /= 10;
410
411                 WinningConditionHelper_winnerteam = -1;
412                 if(WinningConditionHelper_equality)
413                         WinningConditionHelper_winner = world;
414
415                 if(WinningConditionHelper_topscore == 0)
416                 {
417                         if(scores_flags_primary & SFL_ZERO_IS_WORST)
418                         {
419                                 if(WinningConditionHelper_lowerisbetter)
420                                         WinningConditionHelper_topscore = 999999999;
421                                 else
422                                         WinningConditionHelper_topscore = -999999999;
423                         }
424                         WinningConditionHelper_equality = 0;
425                 }
426         }
427
428         if(worldstatus)
429                 strunzone(worldstatus);
430         worldstatus = strzone(s);
431
432         FOR_EACH_CLIENT(p)
433         {
434                 if(fullstatus)
435                 {
436                         s = GetPlayerScoreString(p, 1);
437                         if(clienttype(p) == CLIENTTYPE_REAL)
438                                 s = strcat(s, ":human");
439                         else
440                                 s = strcat(s, ":bot");
441                         if(p.classname == "player" || g_arena || g_lms)
442                                 s = strcat(s, ":", ftos(p.team));
443                         else
444                                 s = strcat(s, ":spectator");
445                 }
446                 else
447                 {
448                         if(p.classname == "player" || g_arena || g_lms)
449                                 s = GetPlayerScoreString(p, 2);
450                         else
451                                 s = "-666";
452                 }
453
454                 if(p.clientstatus)
455                         strunzone(p.clientstatus);
456                 p.clientstatus = strzone(s);
457         }
458 }
459
460 string GetScoreLogLabel(string label, float fl)
461 {
462         if(fl & SFL_LOWER_IS_BETTER)
463                 label = strcat(label, "<");
464         if(fl & SFL_SORT_PRIO_MASK == SFL_SORT_PRIO_PRIMARY)
465                 label = strcat(label, "!!");
466         else if(fl & SFL_SORT_PRIO_MASK == SFL_SORT_PRIO_SECONDARY)
467                 label = strcat(label, "!");
468         return label;
469 }
470
471 string GetPlayerScoreString(entity pl, float shortString)
472 {
473         string out;
474         entity sk;
475         float i, f;
476         string l;
477
478         out = "";
479         if(!pl)
480         {
481                 // label
482                 for(i = 0; i < MAX_SCORE; ++i)
483                         if(scores_flags[i] & SFL_SORT_PRIO_MASK == SFL_SORT_PRIO_PRIMARY)
484                         {
485                                 f = scores_flags[i];
486                                 l = scores_label[i];
487                                 out = strcat(out, GetScoreLogLabel(l, f), ",");
488                         }
489                 if(shortString < 2)
490                 for(i = 0; i < MAX_SCORE; ++i)
491                         if(scores_flags[i] & SFL_SORT_PRIO_MASK == SFL_SORT_PRIO_SECONDARY)
492                         {
493                                 f = scores_flags[i];
494                                 l = scores_label[i];
495                                 out = strcat(out, GetScoreLogLabel(l, f), ",");
496                         }
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                         {
502                                 f = scores_flags[i];
503                                 l = scores_label[i];
504                                 out = strcat(out, GetScoreLogLabel(l, f), ",");
505                         }
506                 out = substring(out, 0, strlen(out) - 1);
507         }
508         else if((sk = pl.scorekeeper))
509         {
510                 for(i = 0; i < MAX_SCORE; ++i)
511                         if(scores_flags[i] & SFL_SORT_PRIO_MASK == SFL_SORT_PRIO_PRIMARY)
512                                 out = strcat(out, ftos(sk.(scores[i])), ",");
513                 if(shortString < 2)
514                 for(i = 0; i < MAX_SCORE; ++i)
515                         if(scores_flags[i] & SFL_SORT_PRIO_MASK == SFL_SORT_PRIO_SECONDARY)
516                                 out = strcat(out, ftos(sk.(scores[i])), ",");
517                 if(shortString < 1)
518                 for(i = 0; i < MAX_SCORE; ++i)
519                         if(scores_flags[i] & SFL_SORT_PRIO_MASK != SFL_SORT_PRIO_PRIMARY)
520                         if(scores_flags[i] & SFL_SORT_PRIO_MASK != SFL_SORT_PRIO_SECONDARY)
521                                 out = strcat(out, ftos(sk.(scores[i])), ",");
522                 out = substring(out, 0, strlen(out) - 1);
523         }
524         return out;
525 }
526
527 string GetTeamScoreString(float tm, float shortString)
528 {
529         string out;
530         entity sk;
531         float i, f;
532         string l;
533
534         out = "";
535         if(tm == 0)
536         {
537                 // label
538                 for(i = 0; i < MAX_SCORE; ++i)
539                         if(teamscores_flags[i] & SFL_SORT_PRIO_MASK == SFL_SORT_PRIO_PRIMARY)
540                         {
541                                 f = teamscores_flags[i];
542                                 l = teamscores_label[i];
543                                 out = strcat(out, GetScoreLogLabel(l, f), ",");
544                         }
545                 if(shortString < 2)
546                 for(i = 0; i < MAX_SCORE; ++i)
547                         if(teamscores_flags[i] & SFL_SORT_PRIO_MASK == SFL_SORT_PRIO_SECONDARY)
548                         {
549                                 f = teamscores_flags[i];
550                                 l = teamscores_label[i];
551                                 out = strcat(out, GetScoreLogLabel(l, f), ",");
552                         }
553                 if(shortString < 1)
554                 for(i = 0; i < MAX_SCORE; ++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                         {
558                                 f = teamscores_flags[i];
559                                 l = teamscores_label[i];
560                                 out = strcat(out, GetScoreLogLabel(l, f), ",");
561                         }
562                 out = substring(out, 0, strlen(out) - 1);
563         }
564         else if((sk = teamscorekeepers[tm - 1]))
565         {
566                 for(i = 0; i < MAX_TEAMSCORE; ++i)
567                         if(teamscores_flags[i] & SFL_SORT_PRIO_MASK == SFL_SORT_PRIO_PRIMARY)
568                                 out = strcat(out, ftos(sk.(teamscores[i])), ",");
569                 if(shortString < 2)
570                 for(i = 0; i < MAX_TEAMSCORE; ++i)
571                         if(teamscores_flags[i] & SFL_SORT_PRIO_MASK == SFL_SORT_PRIO_SECONDARY)
572                                 out = strcat(out, ftos(sk.(teamscores[i])), ",");
573                 if(shortString < 1)
574                 for(i = 0; i < MAX_TEAMSCORE; ++i)
575                         if(teamscores_flags[i] & SFL_SORT_PRIO_MASK != SFL_SORT_PRIO_PRIMARY)
576                         if(teamscores_flags[i] & SFL_SORT_PRIO_MASK != SFL_SORT_PRIO_SECONDARY)
577                                 out = strcat(out, ftos(sk.(teamscores[i])), ",");
578                 out = substring(out, 0, strlen(out) - 1);
579         }
580         return out;
581 }
582
583 float PlayerTeamScore_Compare(entity p1, entity p2)
584 {
585         if(teamscores_entities_count)
586                 if(p1.team != p2.team)
587                 {
588                         entity t1, t2;
589                         float r;
590                         t1 = teamscorekeepers[p1.team - 1];
591                         t2 = teamscorekeepers[p2.team - 1];
592                         r = TeamScore_Compare(t1, t2);
593                         if(r == 0) // ensure a deterministic order
594                                 r = p1.team - p2.team;
595                         return r;
596                 }
597         
598         return PlayerScore_Compare(p1.scorekeeper, p2.scorekeeper);
599 }
600
601 entity PlayerScore_Sort(.float field)
602 {
603         entity p, plist, pprev, pbest, pbestprev, pfirst, plast;
604         float i;
605
606         plist = world;
607
608         FOR_EACH_CLIENT(p)
609                 p.field = 0;
610
611         FOR_EACH_PLAYER(p) if(p.scorekeeper)
612         {
613                 p.chain = plist;
614                 plist = p;
615         }
616         // Now plist points to the whole list.
617         
618         pfirst = plast = world;
619
620         i = 0;
621         while(plist)
622         {
623                 pprev = pbestprev = world;
624                 pbest = plist;
625                 for(p = plist; (pprev = p), (p = p.chain); )
626                 {
627                         if(PlayerTeamScore_Compare(p, pbest) > 0)
628                         {
629                                 pbest = p;
630                                 pbestprev = pprev;
631                         }
632                 }
633
634                 // remove pbest out of the chain
635                 if(pbestprev == world)
636                         plist = pbest.chain;
637                 else
638                         pbestprev.chain = pbest.chain;
639                 pbest.chain = world;
640
641                 pbest.field = ++i;
642
643                 if not(pfirst)
644                         pfirst = pbest;
645                 if(plast)
646                         plast.chain = pbest;
647                 plast = pbest;
648         }
649
650         return pfirst;
651 }
652
653 float TeamScore_GetCompareValue(float t)
654 {
655         float s;
656         entity sk;
657
658         if(t <= 0 || t >= 16)
659                 error("Reading score of invalid team!");
660
661         sk = teamscorekeepers[t - 1];
662         if not(sk)
663                 return -999999999;
664         s = sk.teamscores_primary;
665         if(teamscores_flags_primary & SFL_ZERO_IS_WORST)
666                 if(!s)
667                         return -999999999;
668         if(teamscores_flags_primary & SFL_LOWER_IS_BETTER)
669                 s = -s;
670         return s;
671 }
672
673 #define NAMEWIDTH 22
674 #define SCORESWIDTH 58
675 // TODO put this somewhere in common?
676 string Score_NicePrint_ItemColor(float vflags)
677 {
678         if(vflags & SFL_SORT_PRIO_PRIMARY)
679                 return "^3";
680         else if(vflags & SFL_SORT_PRIO_SECONDARY)
681                 return "^5";
682         else
683                 return "^7";
684 }
685
686 void Score_NicePrint_Team(entity to, float t, float w)
687 {
688         string s, s2;
689         float i;
690         entity sk;
691         float fl, sc;
692         s = "";
693
694         sk = teamscorekeepers[t - 1];
695         if(sk)
696         {
697                 s = strcat(s, ColoredTeamName(t));
698                 for(i = 0; i < MAX_TEAMSCORE; ++i)
699                         if(teamscores_label[i] != "")
700                         {
701                                 fl = teamscores_flags[i];
702                                 sc = sk.(teamscores[i]);
703                                 s = strcat(s, " ", Score_NicePrint_ItemColor(fl), ScoreString(fl, sc));
704                         }
705         }
706         else
707                 s = "Scores:";
708
709         s = strcat(s, strpad(max(0, NAMEWIDTH - strlennocol(s)), ""));
710         
711         for(i = 0; i < MAX_SCORE; ++i)
712                 if(scores_label[i] != "")
713                 {
714                         fl = scores_flags[i];
715                         s2 = scores_label[i];
716                         s = strcat(s, " ", Score_NicePrint_ItemColor(fl), strpad(-w, substring(s2, 0, w)));
717                 }
718
719         print_to(to, s);
720 }
721
722 void Score_NicePrint_Player(entity to, entity p, float w)
723 {
724         string s;
725         float i;
726         entity sk;
727         float fl, sc;
728         s = "  ";
729
730         sk = p.scorekeeper;
731
732         s = strcat(s, p.netname);
733         for(;;)
734         {
735                 i = strlennocol(s) - NAMEWIDTH;
736                 if(i > 0)
737                         s = substring(s, 0, strlen(s) - i);
738                 else
739                 {
740                         s = strcat(s, strpad(i, ""));
741                         break;
742                 }
743         }
744         
745         for(i = 0; i < MAX_SCORE; ++i)
746                 if(scores_label[i] != "")
747                 {
748                         fl = scores_flags[i];
749                         sc = sk.(scores[i]);
750                         s = strcat(s, " ", Score_NicePrint_ItemColor(fl), strpad(-w, ScoreString(fl, sc)));
751                 }
752
753         print_to(to, s);
754 }
755
756 void Score_NicePrint_Spectators(entity to)
757 {
758         print_to(to, "Spectators:");
759 }
760
761 void Score_NicePrint_Spectator(entity to, entity p)
762 {
763         print_to(to, strcat("  ", p.netname));
764 }
765
766 .float score_dummyfield;
767 void Score_NicePrint(entity to)
768 {
769         entity p;
770         float t, i;
771         float w;
772
773         t = 0;
774         for(i = 0; i < MAX_SCORE; ++i)
775                 if(scores_label[i] != "")
776                         ++t;
777         w = bound(6, floor(SCORESWIDTH / t - 1), 9);
778
779         p = PlayerScore_Sort(score_dummyfield);
780         t = -1;
781
782         if(!teamscores_entities_count)
783                 Score_NicePrint_Team(to, t, w);
784         while(p)
785         {
786                 if(teamscores_entities_count)
787                         if(t != p.team)
788                                 Score_NicePrint_Team(to, p.team, w);
789                 Score_NicePrint_Player(to, p, w);
790                 t = p.team;
791                 p = p.chain;
792         }
793         
794         t = 0;
795         FOR_EACH_CLIENT(p)
796         if(p.classname != "player")
797         {
798                 if not(t)
799                         Score_NicePrint_Spectators(to);
800                 Score_NicePrint_Spectator(to, p);
801                 t = 1;
802         }
803 }
804