1 /* playerlist.c
2 *
3 * Fairly substantial re-write to do variable player lists: Sept 93 DRG Major
4 * rewrite for demand driven updates -> huge speedup: Oct. 94 [007] Major
5 * rewrite to fix some bugs and speed things up : Jan. 95 [Zork]
6 *
7 *
8 * TODO:
9 *
10 * Sort observers separatly: opserver if (pl->p_flags & PFOBSERV) is true.
11 *
12 */
13
14 #include "config.h"
15 #include "copyright.h"
16
17 #include <stdio.h>
18 #include "Wlib.h"
19 #include "defs.h"
20 #include "struct.h"
21 #include "data.h"
22
23 #include "defaults.h"
24 #include "string_util.h"
25 #include "playerlist.h"
26
27 #define MaxPlistField 18 /* The width of the longest
28 * * * possible plist field */
29
30 #define IsEmpty(x) (!x)
31 #define IsNotEmpty(x) (x)
32 #define IsZero(x) (!x)
33 #define IsNotZero(x) (x)
34 #define TRUE 1
35 #define FALSE 0
36
37 /* Current list of flags */
38 /* ' ' - White Space */
39 /* 'b' - Armies Bombed */
40 /* 'd' - Damage Inflicted (DI) */
41 /* 'k' - Max Kills */
42 /* 'l' - Login Name */
43 /* 'n' - Ship Number */
44 /* 'p' - Planets Taken */
45 /* 'r' - Ratio */
46 /* 's' - Speed */
47 /* 'v' - Deaths per hour */
48 /* 'w' - War staus */
49 /* 'B' - Bombing */
50 /* 'C' - Curt (short) rank */
51 /* 'D' - Defense */
52 /* 'H' - Hours Played */
53 /* 'K' - Kills */
54 /* 'L' - Losses */
55 /* 'M' - Display, Host Machine */
56 /* 'N' - Name */
57 /* 'O' - Offense */
58 /* 'P' - Planets */
59 /* 'R' - Rank */
60 /* 'S' - Total Rating (stats) */
61 /* 'T' - Ship Type */
62 /* 'V' - Kills per hour */
63 /* 'W' - Wins */
64
65 /*
66 * Global Variables
67 *
68 * partitionPlist : Separate the goodies from baddies in the sorted list?
69 * plistCustomLayout : The value of `playerlist' in the defaults file.
70 * plistReorder : True only if the order of the playerlist is out of date.
71 * plistStyle : The current style number for the player list.
72 * plistUpdated : True only if the player list is out of date.
73 * sortMyTeamFirst : Should my team go first or second in the sorted list?
74 * sortPlayers : Should the player list be sorted?
75 * updatePlayer[plr] : The playerlist entry for "plr" is old.
76 *
77 * plistHasHostile : True if "Hostile" is a field in the current list.
78 * plistHasSpeed : True if "Speed" is true in the current playerlist.
79 */
80
81 int partitionPlist = FALSE;
82 char *plistCustomLayout;
83 int plistReorder = FALSE;
84 int plistStyle = 0;
85 int plistUpdated = FALSE;
86 int sortMyTeamFirst = FALSE;
87 int sortPlayers = TRUE;
88 char updatePlayer[MAXPLAYER + 1];
89
90 #ifdef PLIST2
91 int plistHasHostile = FALSE;
92 int plistHasSpeed = FALSE;
93
94 #endif /* PLIST2 */
95
96
97 /*
98 * Local Variables
99 *
100 * plistLayout : The fields in the current playerlist.
101 * plistPos[plr] : The player list row assigned to each player.
102 * plistWidth : The width of the playerlist.
103 * my_classes : The letters to go with each ship type.
104 */
105
106 static char *plistLayout = "";
107 static int plistPos[MAXPLAYER];
108 static int plistWidth = 0;
109 static char *my_classes[NUM_TYPES] =
110 {"SC", "DD", "CA", "BB", "AS", "SB", "GA", "??"};
111
112
113 /* Local Functions */
114
115 static int PlistHeader(char *layout, int doWrite);
116 static void PlistLine(struct player *j, int pos);
117 static void WriteSortedPlist(void);
118 static void WriteUnsortedPlist(void);
119
120
121
InitPlayerList()122 void InitPlayerList()
123 /* Set the global variables associtated with the player list. This
124 * should be called when the defaults file is loaded or reloaded.
125 */
126 {
127 /* Find the default style number for the player list. */
128
129 plistCustomLayout = getdefault("playerlist");
130
131
132
133 /* Select a style number base on the information in the defaults * *
134 * (.xtrekrc) file. */
135
136 plistStyle = intDefault("playerListStyle", -1);
137
138 if (IsZero(plistStyle) && IsEmpty(plistCustomLayout))
139 {
140 fputs(
141 "Warning: `playerListStyle' set to 0 but `playerlist' is not set.\n",
142 stderr);
143 fputs("\tPlease correct your defaults (.xtrekrc) file.\n", stderr);
144 plistStyle = -1;
145 }
146
147 #ifdef ALWAYS_USE_PLIST_CUSTOM_LAYOUT
148 if (IsNotEmpty(plistCustomLayout))
149 plistStyle = 0;
150 else
151 #endif
152
153 if (plistStyle > PLISTLASTSTYLE)
154 {
155 plistStyle = PLISTLASTSTYLE;
156 }
157 else if (plistStyle == -1)
158 {
159 /* Backward compatibility */
160
161 if (IsNotEmpty(plistCustomLayout))
162 {
163 plistStyle = 0;
164 }
165 else if (booleanDefault("newPlist", FALSE))
166 {
167 plistStyle = 2;
168 }
169 else
170 /* use the old playerlist */
171 {
172 plistStyle = 1;
173 }
174 }
175
176
177 /* Read the partitionPlist flag to tell us whether to break up the player *
178 *
179 * * list with blank lines. */
180
181 partitionPlist = booleanDefault("partitionPlist", partitionPlist);
182
183
184 /* Sort the playerlist? */
185
186 sortPlayers = booleanDefault("sortPlayers", sortPlayers);
187
188
189 /* My team goes first (or second)? */
190
191 sortMyTeamFirst = booleanDefault("sortMyTeamFirst", sortMyTeamFirst);
192
193
194 /* plistUpdate[MAXPLAYER] must always be TRUE because thats how we no when
195 * * * to stop looking for a changed player. */
196
197 updatePlayer[MAXPLAYER] = TRUE;
198 RedrawPlayerList();
199 }
200
201
PlistMaxWidth()202 int PlistMaxWidth()
203 /* Calculate the maximum width of all the defined player lists so that the
204 * width of the player list window can be defined. */
205 {
206 plistCustomLayout = getdefault("playerlist");
207
208 if (IsNotEmpty(plistCustomLayout))
209 plistWidth = PlistHeader(plistCustomLayout, FALSE);
210 else
211 plistWidth = 0;
212
213 if (plistWidth < 83)
214 plistWidth = 83;
215
216 return plistWidth;
217 }
218
219
RedrawPlayerList()220 void RedrawPlayerList()
221 /* Completly redraw the player list, rather than incrementally updating the
222 * list as with UpdatePlayerList().
223 *
224 * This function should be called if the plistStyle changes or if the
225 * window has just been revealed.
226 *
227 * This function is called automatically from InitPlayerList. */
228 {
229 int i;
230
231 if (IsEmpty(me) || !W_IsMapped(playerw))
232 return;
233
234
235 /* Translate this style number into a player list layout string. */
236
237 switch (plistStyle)
238 {
239 case 0: /* Custom */
240 if (IsEmpty(plistCustomLayout)) /* this is chacked for in */
241 { /* InitPlayerList */
242 plistLayout = "";
243 printf("??? empty plistLayout? This can't happen!\n");
244 }
245 else
246 plistLayout = plistCustomLayout;
247 break;
248
249 case 1: /* Old Style */
250 plistLayout = "nTRNKWLr O D d ";
251 break;
252
253 case 2: /* COW Style */
254 plistLayout = "nTR N K lrSd";
255 break;
256
257 case 3: /* Kill Watch Style */
258 plistLayout = "nTK RNlr Sd";
259 break;
260
261 case 4: /* BRMH Style */
262 plistLayout = "nTR N K l M";
263 break;
264
265 default:
266 fprintf(stderr, "Unknown plistStyle in ChangedPlistStyle().\n");
267 break;
268 }
269
270
271 /* Redraw the player list. */
272
273 W_ClearWindow(playerw);
274 (void) PlistHeader(plistLayout, TRUE);
275
276 plistReorder = TRUE;
277 plistUpdated = TRUE;
278
279 for (i = 0; i < MAXPLAYER; i++)
280 updatePlayer[i] = TRUE;
281
282 UpdatePlistFn();
283 }
284
285
UpdatePlistFn()286 void UpdatePlistFn()
287 /* Update the player list.
288 *
289 * This function should usually be called through the UpdatePlayerList() macro
290 * (see playerlist.h).
291 *
292 * This function works incrimentally. If a dramatic change has taken place
293 * (i.e. if plistStyle changes) then RedrawPlayerList() should be called
294 * instead. */
295 {
296 int count;
297 char *update;
298
299 plistUpdated = FALSE;
300
301 if (!W_IsMapped(playerw))
302 return;
303
304 if (!plistReorder)
305 {
306 /* Redraw the lines that have changed. */
307
308 #if defined(sgi)
309 #pragma set woff 1167
310 #endif
311 update = updatePlayer-1;
312
313 for (;;)
314 {
315 /* Find the changed player as quickly as possible. */
316
317 do
318 ++update;
319 while (!(*update));
320
321
322 /* Is this a valid player? Remember updatePlayer[MAXPLAYER] is * *
323 * always TRUE to make the above loop more efficient. */
324
325 count = update - updatePlayer;
326 if (count == MAXPLAYER)
327 break;
328
329 *update = FALSE;
330
331
332 /* We should not get updates for free players any more, but just *
333 * * incase a packet arrives late... */
334
335 if (players[count].p_status != PFREE)
336 PlistLine(players + count, plistPos[count]);
337 }
338 }
339 else
340 {
341 /* Reorder the player list. Note that this may not require a full * *
342 * rewrite. */
343
344 plistReorder = FALSE;
345
346 if (sortPlayers)
347 WriteSortedPlist();
348 else
349 WriteUnsortedPlist();
350 }
351 }
352
353
WriteSortedPlist()354 static void WriteSortedPlist()
355 /* Update the order of the players in the list and write out any changes. */
356 {
357 int row, i, last;
358 struct player *current;
359 int teamPos[NUMTEAM + 1];
360 int *pos;
361
362 static int plistLastRow = -1;
363 static int blankLine = -1;
364 static int blankLine2 = -1;
365 static int myTeam = -1;
366
367
368 /* If I have changed team, redraw everything (to get the colors right). */
369
370 if (remap[me->p_team] != myTeam)
371 {
372 myTeam = remap[me->p_team];
373
374 for (pos = plistPos + MAXPLAYER - 1; pos >= plistPos; --pos)
375 *pos = -1;
376 }
377
378
379 /* If partitionPlist is false, reset the blank line markers */
380
381 if (!partitionPlist)
382 {
383 blankLine = -1;
384 blankLine2 = -1;
385 }
386
387
388 /* Count the number of players in each team. */
389
390 for (i = NUMTEAM; i >= 0; --i)
391 teamPos[i] = 0;
392
393 for (current = players + MAXPLAYER - 1; current >= players; --current)
394 {
395 if (current->p_status != PFREE)
396 ++teamPos[remap[current->p_team]];
397 }
398
399
400 /* Find the row after the last player in each team. */
401
402 last = 1; /* The title is line zero */
403
404 if (sortMyTeamFirst) /* My team comes at the top */
405 {
406 last += teamPos[myTeam];
407 teamPos[myTeam] = last;
408
409 if (partitionPlist)
410 {
411 if (blankLine != last)
412 {
413 blankLine = last;
414 W_ClearArea(playerw, 0, last, plistWidth, 1);
415 }
416
417 ++last;
418 }
419 }
420
421 for (i = 1; i <= NUMTEAM; ++i)
422 {
423 if (i != myTeam)
424 {
425 last += teamPos[i];
426 teamPos[i] = last;
427 }
428 }
429
430 if (!sortMyTeamFirst) /* My team comes below the *
431 * others */
432 {
433 if (partitionPlist)
434 {
435 if (blankLine != last)
436 {
437 blankLine = last;
438 W_ClearArea(playerw, 0, last, plistWidth, 1);
439 }
440
441 ++last;
442 }
443
444 last += teamPos[myTeam];
445 teamPos[myTeam] = last;
446 }
447
448 if (myTeam != NOBODY)
449 {
450 /* Separate the goodies from the arriving players. */
451
452 if (partitionPlist)
453 {
454 if (blankLine2 != last)
455 {
456 blankLine2 = last;
457 W_ClearArea(playerw, 0, last, plistWidth, 1);
458 }
459
460 ++last;
461 }
462
463 last += teamPos[NOBODY];
464 teamPos[NOBODY] = last;
465 }
466
467
468 /* Clear some lines if people have left. */
469
470 for (row = last; row < plistLastRow; ++row)
471 W_ClearArea(playerw, 0, row, plistWidth, 1);
472
473 plistLastRow = last;
474
475
476
477 /* Write out each player that has either changed position or has * new *
478 * stats. */
479
480 for (i = MAXPLAYER - 1, current = players + MAXPLAYER - 1;
481 i >= 0;
482 --i, --current)
483 {
484 if (current->p_status == PFREE)
485 {
486 updatePlayer[i] = FALSE;
487 continue;
488 }
489
490 row = --(teamPos[remap[current->p_team]]);
491
492 if ((!updatePlayer[i]) && plistPos[i] == row)
493 continue;
494
495 plistPos[i] = row;
496 updatePlayer[i] = FALSE;
497
498 PlistLine(current, row);
499 }
500
501 }
502
503
WriteUnsortedPlist(void)504 static void WriteUnsortedPlist(void)
505 /*
506 * Update the order of the players in the list and write out any
507 * changes.
508 */
509 {
510 int count;
511 int pos;
512 char *update;
513 static int myTeam = -1;
514
515
516 /*
517 * If I have changed team, redraw everything (to get the colors
518 * right).
519 */
520
521 if (remap[me->p_team] != myTeam)
522 {
523 myTeam = remap[me->p_team];
524
525 for (update = updatePlayer + MAXPLAYER
526 ; update >= updatePlayer
527 ; --update)
528 {
529 *update = TRUE;
530 }
531 }
532
533
534 #if defined(sgi)
535 #pragma set woff 1167
536 #endif
537 update = updatePlayer - 1;
538
539 for (;;)
540 {
541 /* Find the changed player as quickly as possible. */
542
543 do
544 ++update;
545 while (!(*update));
546
547
548 /* Is this a valid player? Remember updatePlayer[MAXPLAYER] * is *
549 * always TRUE to make the above loop more efficient. */
550
551 count = update - updatePlayer;
552 if (count == MAXPLAYER)
553 break;
554
555
556 /* Update the player. */
557
558 *update = FALSE;
559 pos = count + 1;
560 plistPos[count] = pos;
561
562 if (players[count].p_status != PFREE)
563 PlistLine(players + count, pos);
564 else
565 W_ClearArea(playerw, 0, pos, plistWidth, 1);
566 }
567 }
568
569
570
PlistHeader(char * layout,int doWrite)571 static int PlistHeader(char *layout, int doWrite)
572 /* Analyse the heading (field names) for a player list, and set the
573 * plistHasSpeed and plistHasHostile flags.
574 *
575 * If doWrite is TRUE, write the heading to the list.
576 *
577 * RETURN the width of the player list. */
578 {
579 char header[BUFSIZ];
580 int num = 0;
581
582 #ifdef PLIST2
583 plistHasSpeed = FALSE;
584 plistHasHostile = FALSE;
585 #endif /* PLIST2 */
586
587 for (; IsNotZero(*layout); ++layout)
588 {
589 if (num + MaxPlistField >= BUFSIZ)
590 {
591 /* Assume that we have tested the standard layouts so that only * *
592 * custom layouts can be too long. * * If a standard layout is *
593 * found to be too long then some compiler's * code will dump core
594 * * here because of an attempt to write over a * constant string. */
595
596 fprintf(stderr, "Playerlist truncated to fit buffer.\n");
597 layout = '\0';
598 break;
599 }
600
601 switch (*layout)
602 {
603 case 'n': /* Ship Number */
604 STRNCPY(&header[num], " No", 3);
605 num += 3;
606 break;
607 case 'T': /* Ship Type */
608 STRNCPY(&header[num], " Ty", 3);
609 num += 3;
610 break;
611 case 'C': /* Curt (short) Rank */
612 STRNCPY(&header[num], " Rank", 5);
613 num += 5;
614 break;
615 case 'R': /* Rank */
616 STRNCPY(&header[num], " Rank ", 11);
617 num += 11;
618 break;
619 case 'N': /* Name */
620 STRNCPY(&header[num], " Name ", 17);
621 num += 17;
622 break;
623 case 'K': /* Kills */
624 STRNCPY(&header[num], " Kills", 6);
625 num += 6;
626 break;
627 case 'l': /* Login Name */
628 STRNCPY(&header[num], " Login ", 17);
629 num += 17;
630 break;
631 case 'O': /* Offense */
632 STRNCPY(&header[num], " Offse", 6);
633 num += 6;
634 break;
635 case 'W': /* Wins */
636 STRNCPY(&header[num], " Wins", 6);
637 num += 6;
638 break;
639 case 'D': /* Defense */
640 STRNCPY(&header[num], " Defse", 6);
641 num += 6;
642 break;
643 case 'L': /* Losses */
644 STRNCPY(&header[num], " Loss", 6);
645 num += 6;
646 break;
647 case 'S': /* Total Rating (stats) */
648 STRNCPY(&header[num], " Stats", 6);
649 num += 6;
650 break;
651 case 'r': /* Ratio */
652 STRNCPY(&header[num], " Ratio", 6);
653 num += 6;
654 break;
655 case 'd': /* Damage Inflicted (DI) */
656 STRNCPY(&header[num], " DI", 8);
657 num += 8;
658 break;
659 case ' ': /* White Space */
660 header[num] = ' ';
661 num += 1;
662 break;
663
664 #ifdef PLIST1
665 case 'B': /* Bombing */
666 STRNCPY(&header[num], " Bmbng", 6);
667 num += 6;
668 break;
669 case 'b': /* Armies Bombed */
670 STRNCPY(&header[num], " Bmbed", 6);
671 num += 6;
672 break;
673 case 'P': /* Planets */
674 STRNCPY(&header[num], " Plnts", 6);
675 num += 6;
676 break;
677 case 'p': /* Planets Taken */
678 STRNCPY(&header[num], " Plnts", 6);
679 num += 6;
680 break;
681 case 'M': /* Display, Host Machine */
682 STRNCPY(&header[num], " Host Machine ", 17);
683 num += 17;
684 break;
685 case 'H': /* Hours Played */
686 STRNCPY(&header[num], " Hours ", 7);
687 num += 7;
688 break;
689 case 'k': /* Max Kills */
690 STRNCPY(&header[num], " Max K", 6);
691 num += 6;
692 break;
693 case 'V': /* Kills per hour */
694 STRNCPY(&header[num], " KPH", 6);
695 num += 6;
696 break;
697 case 'v': /* Deaths per hour */
698 STRNCPY(&header[num], " DPH", 6);
699 num += 6;
700 break;
701 #endif
702
703 #ifdef PLIST2
704 case 'w': /* War staus */
705 STRNCPY(&header[num], " War Stat", 9);
706 num += 9;
707 plistHasHostile = TRUE;
708 break;
709 case 's': /* Speed */
710 STRNCPY(&header[num], " Sp", 3);
711 num += 3;
712 plistHasSpeed = TRUE;
713 break;
714 #endif
715
716 default:
717 fprintf(stderr,
718 "%c is not an option for the playerlist\n", *layout);
719 break;
720 }
721 }
722
723 header[num] = '\0';
724
725 if (doWrite)
726 W_WriteText(playerw, 0, 0, textColor, header, num, W_RegularFont);
727
728 return num;
729 }
730
731
PlistLine(struct player * j,int pos)732 static void PlistLine(struct player *j, int pos)
733 /* Write the player list entry for player `j' on line `pos'. */
734 {
735 char buf[BUFSIZ];
736 char *ptr;
737 char *buffPoint;
738 int kills, losses, my_ticks;
739 float pRating, oRating, dRating, bRating, Ratings;
740 float KillsPerHour, LossesPerHour; /* Added 12/27/93 ATH */
741 double ratio, max_kills;
742
743 if (j->p_ship.s_type == STARBASE)
744 {
745 kills = j->p_stats.st_sbkills;
746 losses = j->p_stats.st_sblosses;
747 max_kills = j->p_stats.st_sbmaxkills;
748
749 if (SBhours)
750 my_ticks = j->p_stats.st_sbticks;
751 else
752 my_ticks = j->p_stats.st_tticks;
753
754 KillsPerHour = (float) (my_ticks == 0) ? 0.0 :
755 (float) kills *36000.0 / (float) my_ticks;
756
757 LossesPerHour = (float) (my_ticks == 0) ? 0.0 :
758 (float) losses *36000.0 / (float) my_ticks;
759 }
760 else
761 {
762 kills = j->p_stats.st_kills + j->p_stats.st_tkills;
763 losses = j->p_stats.st_losses + j->p_stats.st_tlosses;
764 max_kills = j->p_stats.st_maxkills;
765 my_ticks = j->p_stats.st_tticks;
766 KillsPerHour = (float) (my_ticks == 0) ? 0.0 :
767 (float) j->p_stats.st_tkills * 36000.0 / (float) my_ticks;
768 LossesPerHour = (float) (my_ticks == 0) ? 0.0 :
769 (float) j->p_stats.st_tlosses * 36000.0 / (float) my_ticks;
770 }
771
772 if (losses == 0)
773 ratio = (double) kills;
774 else
775 ratio = (double) kills / (double) losses;
776
777 if (!j->p_stats.st_tticks)
778 {
779 oRating = pRating = bRating = Ratings = 0.0;
780 dRating = defenseRating(j);
781 }
782 else
783 {
784 oRating = offenseRating(j);
785 pRating = planetRating(j);
786 bRating = bombingRating(j);
787 Ratings = oRating + pRating + bRating;
788 dRating = defenseRating(j);
789 if ((j->p_ship.s_type == STARBASE) && (SBhours))
790 { /* If SB, show KPH for * *
791 * offense etc. */
792 oRating = KillsPerHour;
793 dRating = LossesPerHour;
794 }
795 }
796
797
798 buffPoint = buf;
799
800 for (ptr = plistLayout; IsNotZero(*ptr); ++ptr)
801 {
802 *(buffPoint++) = ' ';
803
804 switch (*ptr)
805 {
806 case 'n': /* Ship Number */
807 if (j->p_status != PALIVE)
808 {
809 *(buffPoint++) = ' ';
810 *(buffPoint++) = shipnos[j->p_no];
811 }
812 else
813 {
814 *(buffPoint++) = teamlet[j->p_team];
815 *(buffPoint++) = shipnos[j->p_no];
816 }
817
818 break;
819
820 case 'T': /* Ship Type */
821 if (j->p_status != PALIVE)
822 {
823 *(buffPoint++) = ' ';
824 *(buffPoint++) = ' ';
825 }
826 else
827 {
828 *(buffPoint++) = my_classes[j->p_ship.s_type][0];
829 *(buffPoint++) = my_classes[j->p_ship.s_type][1];
830 }
831
832 break;
833
834 case 'C': /* Curt (short) Rank */
835 format(buffPoint, (j->p_stats.st_rank < nranks) ?
836 ranks[j->p_stats.st_rank].cname : "", 4, 0);
837 buffPoint += 4;
838 break;
839
840 case 'R': /* Rank */
841 format(buffPoint, (j->p_stats.st_rank < nranks) ?
842 ranks[j->p_stats.st_rank].name : "", 10, 0);
843 buffPoint += 10;
844 break;
845
846 case 'N': /* Name */
847 format(buffPoint, j->p_name, 16, 0);
848 buffPoint += 16;
849 break;
850
851 case 'K': /* Kills */
852 if (j->p_kills == 0.0) {
853 format(buffPoint, " ", 5, 0);
854 } else {
855 if (j->p_kills > 100.0)
856 ftoa(j->p_kills, buffPoint - 1, 0, 3, 2);
857 else
858 ftoa(j->p_kills, buffPoint, 0, 2, 2);
859 }
860 buffPoint += 5;
861 break;
862
863 case 'l': /* Login Name */
864 format(buffPoint, j->p_login, 16, 0);
865 buffPoint += 16;
866 break;
867
868 case 'O': /* Offense */
869 ftoa(oRating, buffPoint, 0, 2, 2);
870 buffPoint += 5;
871 break;
872
873 case 'W': /* Wins */
874 itoapad(kills, buffPoint, 0, 5);
875 buffPoint += 5;
876 break;
877
878 case 'D': /* Defense */
879 ftoa(dRating, buffPoint, 0, 2, 2);
880 buffPoint += 5;
881 break;
882
883 case 'L': /* Losses */
884 itoapad(losses, buffPoint, 0, 5);
885 buffPoint += 5;
886 break;
887
888 case 'S': /* Total Rating (stats) */
889 ftoa(Ratings, buffPoint, 0, 2, 2);
890 buffPoint += 5;
891 break;
892
893 case 'r': /* Ratio */
894 ftoa(ratio, buffPoint, 0, 2, 2);
895 buffPoint += 5;
896 break;
897
898 case 'd': /* Damage Inflicted (DI) */
899 ftoa(Ratings * (j->p_stats.st_tticks / 36000.0),
900 buffPoint, 0, 4, 2);
901 buffPoint += 7;
902 break;
903
904 case ' ': /* White Space */
905 break;
906
907 #ifdef PLIST1
908 case 'B': /* Bombing */
909 ftoa(bRating, buffPoint, 0, 2, 2);
910 buffPoint += 5;
911 break;
912
913 case 'b': /* Armies Bombed */
914 itoapad(j->p_stats.st_tarmsbomb + j->p_stats.st_armsbomb,
915 buffPoint, 0, 5);
916 buffPoint += 5;
917 break;
918
919 case 'P': /* Planets */
920 ftoa(pRating, buffPoint, 0, 2, 2);
921 buffPoint += 5;
922 break;
923
924 case 'p': /* Planets Taken */
925 itoapad(j->p_stats.st_tplanets + j->p_stats.st_planets,
926 buffPoint, 0, 5);
927 buffPoint += 5;
928 break;
929
930 case 'M': /* Display, Host Machine */
931 format(buffPoint, j->p_monitor, 16, 0);
932 buffPoint += 16;
933 break;
934
935 case 'H': /* Hours Played */
936 ftoa(my_ticks / 36000.0, buffPoint, 0, 3, 2);
937 buffPoint += 6;
938 break;
939
940 case 'k': /* Max Kills */
941 ftoa(max_kills, buffPoint, 0, 2, 2);
942 buffPoint += 5;
943 break;
944
945 case 'V': /* Kills Per Hour */
946 ftoa(KillsPerHour, buffPoint, 0, 3, 1);
947 buffPoint += 5;
948 break;
949
950 case 'v': /* Deaths Per Hour */
951 ftoa(LossesPerHour, buffPoint, 0, 3, 1);
952 buffPoint += 5;
953 break;
954 #endif
955
956 #ifdef PLIST2
957 case 'w': /* War staus */
958 if (j->p_swar & me->p_team)
959 STRNCPY(buffPoint, "WAR ", 8);
960 else if (j->p_hostile & me->p_team)
961 STRNCPY(buffPoint, "HOSTILE ", 8);
962 else
963 STRNCPY(buffPoint, "PEACEFUL", 8);
964
965 buffPoint += 8;
966 break;
967
968 case 's': /* Speed */
969 itoapad(j->p_speed, buffPoint, 0, 2);
970 buffPoint += 2;
971 break;
972 #endif
973
974 default:
975 break;
976 }
977 }
978
979 *buffPoint = '\0';
980 W_WriteText(playerw, 0, pos, playerColor(j),
981 buf, buffPoint - buf, shipFont(j));
982 }
983