1 /* NetHack 3.6 topten.c $NHDT-Date: 1450451497 2015/12/18 15:11:37 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.44 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
10 #include "patchlevel.h"
14 /* We don't want to rewrite the whole file, because that entails
15 creating a new version which requires that the old one be deletable. */
16 #define UPDATE_RECORD_IN_PLACE
20 * Updating in place can leave junk at the end of the file in some
21 * circumstances (if it shrinks and the O.S. doesn't have a straightforward
22 * way to truncate it). The trailing junk is harmless and the code
23 * which reads the scores will ignore it.
25 #ifdef UPDATE_RECORD_IN_PLACE
26 static long final_fpos
;
29 #define done_stopprint program_state.stopprint
31 #define newttentry() (struct toptenentry *) alloc(sizeof (struct toptenentry))
32 #define dealloc_ttentry(ttent) free((genericptr_t) (ttent))
34 /* Changing NAMSZ can break your existing record/logfile */
41 struct toptenentry
*tt_next
;
42 #ifdef UPDATE_RECORD_IN_PLACE
46 int deathdnum
, deathlev
;
47 int maxlvl
, hp
, maxhp
, deaths
;
48 int ver_major
, ver_minor
, patchlevel
;
49 long deathdate
, birthdate
;
51 char plrole
[ROLESZ
+ 1];
52 char plrace
[ROLESZ
+ 1];
53 char plgend
[ROLESZ
+ 1];
54 char plalign
[ROLESZ
+ 1];
56 char death
[DTHSZ
+ 1];
58 /* size big enough to read in all the string fields at once; includes
59 room for separating space or trailing newline plus string terminator */
60 #define SCANBUFSZ (4 * (ROLESZ + 1) + (NAMSZ + 1) + (DTHSZ + 1) + 1)
62 STATIC_DCL
void FDECL(topten_print
, (const char *));
63 STATIC_DCL
void FDECL(topten_print_bold
, (const char *));
64 STATIC_DCL xchar
FDECL(observable_depth
, (d_level
*));
65 STATIC_DCL
void NDECL(outheader
);
66 STATIC_DCL
void FDECL(outentry
, (int, struct toptenentry
*, BOOLEAN_P
));
67 STATIC_DCL
void FDECL(discardexcess
, (FILE *));
68 STATIC_DCL
void FDECL(readentry
, (FILE *, struct toptenentry
*));
69 STATIC_DCL
void FDECL(writeentry
, (FILE *, struct toptenentry
*));
71 STATIC_DCL
void FDECL(writexlentry
, (FILE *, struct toptenentry
*, int));
72 STATIC_DCL
long NDECL(encodexlogflags
);
73 STATIC_DCL
long NDECL(encodeconduct
);
74 STATIC_DCL
long NDECL(encodeachieve
);
76 STATIC_DCL
void FDECL(free_ttlist
, (struct toptenentry
*));
77 STATIC_DCL
int FDECL(classmon
, (char *, BOOLEAN_P
));
78 STATIC_DCL
int FDECL(score_wanted
, (BOOLEAN_P
, int, struct toptenentry
*, int,
81 STATIC_DCL
void FDECL(nsb_mung_line
, (char *));
82 STATIC_DCL
void FDECL(nsb_unmung_line
, (char *));
85 static winid toptenwin
= WIN_ERR
;
87 /* "killed by",&c ["an"] 'killer.name' */
89 formatkiller(buf
, siz
, how
, incl_helpless
)
93 boolean incl_helpless
;
95 static NEARDATA
const char *const killed_by_prefix
[] = {
96 /* DIED, CHOKING, POISONING, STARVING, */
97 "killed by ", "choked on ", "poisoned by ", "died of ",
98 /* DROWNING, BURNING, DISSOLVED, CRUSHING, */
99 "drowned in ", "burned by ", "dissolved in ", "crushed to death by ",
100 /* STONING, TURNED_SLIME, GENOCIDED, */
101 "petrified by ", "turned to slime by ", "killed by ",
102 /* PANICKED, TRICKED, QUIT, ESCAPED, ASCENDED */
106 char c
, *kname
= killer
.name
;
108 buf
[0] = '\0'; /* lint suppression */
109 switch (killer
.format
) {
111 impossible("bad killer format? (%d)", killer
.format
);
113 case NO_KILLER_PREFIX
:
119 (void) strncat(buf
, killed_by_prefix
[how
], siz
- 1);
124 /* Copy kname into buf[].
125 * Object names and named fruit have already been sanitized, but
126 * monsters can have "called 'arbitrary text'" attached to them,
127 * so make sure that that text can't confuse field splitting when
128 * record, logfile, or xlogfile is re-read at some later point.
134 /* 'xlogfile' doesn't really need protection for '=', but
135 fixrecord.awk for corrupted 3.6.0 'record' does (only
136 if using xlogfile rather than logfile to repair record) */
139 /* tab is not possible due to use of mungspaces() when naming;
140 it would disrupt xlogfile parsing if it were present */
147 if (incl_helpless
&& multi
) {
148 /* X <= siz: 'sizeof "string"' includes 1 for '\0' terminator */
149 if (multi_reason
&& strlen(multi_reason
) + sizeof ", while " <= siz
)
150 Sprintf(buf
, ", while %s", multi_reason
);
151 /* either multi_reason wasn't specified or wouldn't fit */
152 else if (sizeof ", while helpless" <= siz
)
153 Strcpy(buf
, ", while helpless");
154 /* else extra death info won't fit, so leave it out */
162 if (toptenwin
== WIN_ERR
)
165 putstr(toptenwin
, ATR_NONE
, x
);
172 if (toptenwin
== WIN_ERR
)
175 putstr(toptenwin
, ATR_BOLD
, x
);
179 observable_depth(lev
)
183 /* if we ever randomize the order of the elemental planes, we
184 must use a constant external representation in the record file */
185 if (In_endgame(lev
)) {
186 if (Is_astralevel(lev
))
188 else if (Is_waterlevel(lev
))
190 else if (Is_firelevel(lev
))
192 else if (Is_airlevel(lev
))
194 else if (Is_earthlevel(lev
))
203 /* throw away characters until current record has been entirely consumed */
212 } while (c
!= '\n' && c
!= EOF
);
218 struct toptenentry
*tt
;
220 char inbuf
[SCANBUFSZ
], s1
[SCANBUFSZ
], s2
[SCANBUFSZ
], s3
[SCANBUFSZ
],
221 s4
[SCANBUFSZ
], s5
[SCANBUFSZ
], s6
[SCANBUFSZ
];
223 #ifdef NO_SCAN_BRACK /* Version_ Pts DgnLevs_ Hp___ Died__Born id */
224 static const char fmt
[] = "%d %d %d %ld %d %d %d %d %d %d %ld %ld %d%*c";
225 static const char fmt32
[] = "%c%c %s %s%*c";
226 static const char fmt33
[] = "%s %s %s %s %s %s%*c";
228 static const char fmt
[] = "%d.%d.%d %ld %d %d %d %d %d %d %ld %ld %d ";
229 static const char fmt32
[] = "%c%c %[^,],%[^\n]%*c";
230 static const char fmt33
[] = "%s %s %s %s %[^,],%[^\n]%*c";
233 #ifdef UPDATE_RECORD_IN_PLACE
234 /* note: input below must read the record's terminating newline */
235 final_fpos
= tt
->fpos
= ftell(rfile
);
238 if (fscanf(rfile
, fmt
, &tt
->ver_major
, &tt
->ver_minor
, &tt
->patchlevel
,
239 &tt
->points
, &tt
->deathdnum
, &tt
->deathlev
, &tt
->maxlvl
,
240 &tt
->hp
, &tt
->maxhp
, &tt
->deaths
, &tt
->deathdate
,
241 &tt
->birthdate
, &tt
->uid
) != TTFIELDS
) {
244 discardexcess(rfile
);
246 /* load remainder of record into a local buffer;
247 this imposes an implicit length limit of SCANBUFSZ
248 on every string field extracted from the buffer */
249 if (!fgets(inbuf
, sizeof inbuf
, rfile
)) {
250 /* sscanf will fail and tt->points will be set to 0 */
252 } else if (!index(inbuf
, '\n')) {
253 Strcpy(&inbuf
[sizeof inbuf
- 2], "\n");
254 discardexcess(rfile
);
256 /* Check for backwards compatibility */
257 if (tt
->ver_major
< 3 || (tt
->ver_major
== 3 && tt
->ver_minor
< 3)) {
260 if (sscanf(inbuf
, fmt32
, tt
->plrole
, tt
->plgend
, s1
, s2
) == 4) {
261 tt
->plrole
[1] = tt
->plgend
[1] = '\0'; /* read via %c */
262 copynchars(tt
->name
, s1
, (int) (sizeof tt
->name
) - 1);
263 copynchars(tt
->death
, s2
, (int) (sizeof tt
->death
) - 1);
266 tt
->plrole
[1] = '\0';
267 if ((i
= str2role(tt
->plrole
)) >= 0)
268 Strcpy(tt
->plrole
, roles
[i
].filecode
);
269 Strcpy(tt
->plrace
, "?");
270 Strcpy(tt
->plgend
, (tt
->plgend
[0] == 'M') ? "Mal" : "Fem");
271 Strcpy(tt
->plalign
, "?");
272 } else if (sscanf(inbuf
, fmt33
, s1
, s2
, s3
, s4
, s5
, s6
) == 6) {
273 copynchars(tt
->plrole
, s1
, (int) (sizeof tt
->plrole
) - 1);
274 copynchars(tt
->plrace
, s2
, (int) (sizeof tt
->plrace
) - 1);
275 copynchars(tt
->plgend
, s3
, (int) (sizeof tt
->plgend
) - 1);
276 copynchars(tt
->plalign
, s4
, (int) (sizeof tt
->plalign
) - 1);
277 copynchars(tt
->name
, s5
, (int) (sizeof tt
->name
) - 1);
278 copynchars(tt
->death
, s6
, (int) (sizeof tt
->death
) - 1);
282 if (tt
->points
> 0) {
283 nsb_unmung_line(tt
->name
);
284 nsb_unmung_line(tt
->death
);
289 /* check old score entries for Y2K problem and fix whenever found */
290 if (tt
->points
> 0) {
291 if (tt
->birthdate
< 19000000L)
292 tt
->birthdate
+= 19000000L;
293 if (tt
->deathdate
< 19000000L)
294 tt
->deathdate
+= 19000000L;
299 writeentry(rfile
, tt
)
301 struct toptenentry
*tt
;
303 static const char fmt32
[] = "%c%c "; /* role,gender */
304 static const char fmt33
[] = "%s %s %s %s "; /* role,race,gndr,algn */
305 #ifndef NO_SCAN_BRACK
306 static const char fmt0
[] = "%d.%d.%d %ld %d %d %d %d %d %d %ld %ld %d ";
307 static const char fmtX
[] = "%s,%s\n";
308 #else /* NO_SCAN_BRACK */
309 static const char fmt0
[] = "%d %d %d %ld %d %d %d %d %d %d %ld %ld %d ";
310 static const char fmtX
[] = "%s %s\n";
312 nsb_mung_line(tt
->name
);
313 nsb_mung_line(tt
->death
);
316 (void) fprintf(rfile
, fmt0
, tt
->ver_major
, tt
->ver_minor
, tt
->patchlevel
,
317 tt
->points
, tt
->deathdnum
, tt
->deathlev
, tt
->maxlvl
,
318 tt
->hp
, tt
->maxhp
, tt
->deaths
, tt
->deathdate
,
319 tt
->birthdate
, tt
->uid
);
320 if (tt
->ver_major
< 3 || (tt
->ver_major
== 3 && tt
->ver_minor
< 3))
321 (void) fprintf(rfile
, fmt32
, tt
->plrole
[0], tt
->plgend
[0]);
323 (void) fprintf(rfile
, fmt33
, tt
->plrole
, tt
->plrace
, tt
->plgend
,
325 (void) fprintf(rfile
, fmtX
, onlyspace(tt
->name
) ? "_" : tt
->name
,
329 nsb_unmung_line(tt
->name
);
330 nsb_unmung_line(tt
->death
);
336 /* as tab is never used in eg. plname or death, no need to mangle those. */
338 writexlentry(rfile
, tt
, how
)
340 struct toptenentry
*tt
;
343 #define Fprintf (void) fprintf
344 #define XLOG_SEP '\t' /* xlogfile field separator. */
345 char buf
[BUFSZ
], tmpbuf
[DTHSZ
+ 1];
347 Sprintf(buf
, "version=%d.%d.%d", tt
->ver_major
, tt
->ver_minor
,
349 Sprintf(eos(buf
), "%cpoints=%ld%cdeathdnum=%d%cdeathlev=%d", XLOG_SEP
,
350 tt
->points
, XLOG_SEP
, tt
->deathdnum
, XLOG_SEP
, tt
->deathlev
);
351 Sprintf(eos(buf
), "%cmaxlvl=%d%chp=%d%cmaxhp=%d", XLOG_SEP
, tt
->maxlvl
,
352 XLOG_SEP
, tt
->hp
, XLOG_SEP
, tt
->maxhp
);
353 Sprintf(eos(buf
), "%cdeaths=%d%cdeathdate=%ld%cbirthdate=%ld%cuid=%d",
354 XLOG_SEP
, tt
->deaths
, XLOG_SEP
, tt
->deathdate
, XLOG_SEP
,
355 tt
->birthdate
, XLOG_SEP
, tt
->uid
);
356 Fprintf(rfile
, "%s", buf
);
357 Sprintf(buf
, "%crole=%s%crace=%s%cgender=%s%calign=%s", XLOG_SEP
,
358 tt
->plrole
, XLOG_SEP
, tt
->plrace
, XLOG_SEP
, tt
->plgend
, XLOG_SEP
,
360 /* make a copy of death reason that doesn't include ", while helpless" */
361 formatkiller(tmpbuf
, sizeof tmpbuf
, how
, FALSE
);
362 Fprintf(rfile
, "%s%cname=%s%cdeath=%s",
363 buf
, /* (already includes separator) */
364 XLOG_SEP
, plname
, XLOG_SEP
, tmpbuf
);
366 Fprintf(rfile
, "%cwhile=%s", XLOG_SEP
,
367 multi_reason
? multi_reason
: "helpless");
368 Fprintf(rfile
, "%cconduct=0x%lx%cturns=%ld%cachieve=0x%lx", XLOG_SEP
,
369 encodeconduct(), XLOG_SEP
, moves
, XLOG_SEP
, encodeachieve());
370 Fprintf(rfile
, "%crealtime=%ld%cstarttime=%ld%cendtime=%ld", XLOG_SEP
,
371 (long) urealtime
.realtime
, XLOG_SEP
,
372 (long) ubirthday
, XLOG_SEP
, (long) urealtime
.finish_time
);
373 Fprintf(rfile
, "%cgender0=%s%calign0=%s", XLOG_SEP
,
374 genders
[flags
.initgend
].filecode
, XLOG_SEP
,
375 aligns
[1 - u
.ualignbase
[A_ORIGINAL
]].filecode
);
376 Fprintf(rfile
, "%cflags=0x%lx", XLOG_SEP
, encodexlogflags());
377 Fprintf(rfile
, "\n");
390 if (!u
.uroleplay
.numbones
)
401 if (!u
.uconduct
.food
)
403 if (!u
.uconduct
.unvegan
)
405 if (!u
.uconduct
.unvegetarian
)
407 if (!u
.uconduct
.gnostic
)
409 if (!u
.uconduct
.weaphit
)
411 if (!u
.uconduct
.killer
)
413 if (!u
.uconduct
.literate
)
415 if (!u
.uconduct
.polypiles
)
417 if (!u
.uconduct
.polyselfs
)
419 if (!u
.uconduct
.wishes
)
421 if (!u
.uconduct
.wisharti
)
423 if (!num_genocides())
436 if (u
.uachieve
.enter_gehennom
)
438 if (u
.uachieve
.menorah
)
442 if (u
.uevent
.invoked
)
444 if (u
.uachieve
.amulet
)
446 if (In_endgame(&u
.uz
))
448 if (Is_astralevel(&u
.uz
))
450 if (u
.uachieve
.ascended
)
452 if (u
.uachieve
.mines_luckstone
)
454 if (u
.uachieve
.finish_sokoban
)
456 if (u
.uachieve
.killed_medusa
)
458 if (u
.uroleplay
.blind
)
460 if (u
.uroleplay
.nudist
)
466 #endif /* XLOGFILE */
470 struct toptenentry
*tt
;
472 struct toptenentry
*ttnext
;
474 while (tt
->points
> 0) {
475 ttnext
= tt
->tt_next
;
488 int rank
, rank0
= -1, rank1
= 0;
489 int occ_cnt
= sysopt
.persmax
;
490 register struct toptenentry
*t0
, *tprev
;
491 struct toptenentry
*t1
;
493 register int flg
= 0;
500 #endif /* XLOGFILE */
503 /* Under DICE 3.0, this crashes the system consistently, apparently due to
504 * corruption of *rfile somewhere. Until I figure this out, just cut out
505 * topten support entirely - at least then the game exits cleanly. --AC
510 /* If we are in the midst of a panic, cut out topten entirely.
511 * topten uses alloc() several times, which will lead to
512 * problems if the panic was the result of an alloc() failure.
514 if (program_state
.panicking
)
517 if (iflags
.toptenwin
) {
518 toptenwin
= create_nhwindow(NHW_TEXT
);
521 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
522 #define HUP if (!program_state.done_hup)
528 restore_colors(); /* make sure the screen is black on white */
530 /* create a new 'topten' entry */
533 t0
->ver_major
= VERSION_MAJOR
;
534 t0
->ver_minor
= VERSION_MINOR
;
535 t0
->patchlevel
= PATCHLEVEL
;
536 t0
->points
= u
.urexp
;
537 t0
->deathdnum
= u
.uz
.dnum
;
538 /* deepest_lev_reached() is in terms of depth(), and reporting the
539 * deepest level reached in the dungeon death occurred in doesn't
540 * seem right, so we have to report the death level in depth() terms
541 * as well (which also seems reasonable since that's all the player
542 * sees on the screen anyway)
544 t0
->deathlev
= observable_depth(&u
.uz
);
545 t0
->maxlvl
= deepest_lev_reached(TRUE
);
547 t0
->maxhp
= u
.uhpmax
;
548 t0
->deaths
= u
.umortality
;
550 copynchars(t0
->plrole
, urole
.filecode
, ROLESZ
);
551 copynchars(t0
->plrace
, urace
.filecode
, ROLESZ
);
552 copynchars(t0
->plgend
, genders
[flags
.female
].filecode
, ROLESZ
);
553 copynchars(t0
->plalign
, aligns
[1 - u
.ualign
.type
].filecode
, ROLESZ
);
554 copynchars(t0
->name
, plname
, NAMSZ
);
555 formatkiller(t0
->death
, sizeof t0
->death
, how
, TRUE
);
556 t0
->birthdate
= yyyymmdd(ubirthday
);
557 t0
->deathdate
= yyyymmdd(when
);
559 #ifdef UPDATE_RECORD_IN_PLACE
563 #ifdef LOGFILE /* used for debugging (who dies of what, where) */
564 if (lock_file(LOGFILE
, SCOREPREFIX
, 10)) {
565 if (!(lfile
= fopen_datafile(LOGFILE
, "a", SCOREPREFIX
))) {
566 HUP
raw_print("Cannot open log file!");
568 writeentry(lfile
, t0
);
569 (void) fclose(lfile
);
571 unlock_file(LOGFILE
);
575 if (lock_file(XLOGFILE
, SCOREPREFIX
, 10)) {
576 if (!(xlfile
= fopen_datafile(XLOGFILE
, "a", SCOREPREFIX
))) {
577 HUP
raw_print("Cannot open extended log file!");
579 writexlentry(xlfile
, t0
, how
);
580 (void) fclose(xlfile
);
582 unlock_file(XLOGFILE
);
584 #endif /* XLOGFILE */
586 if (wizard
|| discover
) {
593 "Since you were in %s mode, the score list will not be checked.",
594 wizard
? "wizard" : "discover");
600 if (!lock_file(RECORD
, SCOREPREFIX
, 60))
603 #ifdef UPDATE_RECORD_IN_PLACE
604 rfile
= fopen_datafile(RECORD
, "r+", SCOREPREFIX
);
606 rfile
= fopen_datafile(RECORD
, "r", SCOREPREFIX
);
610 HUP
raw_print("Cannot open record file!");
615 HUP
topten_print("");
617 /* assure minimum number of points */
618 if (t0
->points
< sysopt
.pointsmin
)
621 t1
= tt_head
= newttentry();
623 /* rank0: -1 undefined, 0 not_on_list, n n_th on list */
625 readentry(rfile
, t1
);
626 if (t1
->points
< sysopt
.pointsmin
)
628 if (rank0
< 0 && t1
->points
< t0
->points
) {
635 #ifdef UPDATE_RECORD_IN_PLACE
636 t0
->fpos
= t1
->fpos
; /* insert here */
640 flg
++; /* ask for a rewrite */
646 if ((sysopt
.pers_is_uid
? t1
->uid
== t0
->uid
647 : strncmp(t1
->name
, t0
->name
, NAMSZ
) == 0)
648 && !strncmp(t1
->plrole
, t0
->plrole
, ROLESZ
) && --occ_cnt
<= 0) {
656 "You didn't beat your previous score of %ld points.",
667 if (rank
<= sysopt
.entrymax
) {
668 t1
->tt_next
= newttentry();
672 if (rank
> sysopt
.entrymax
) {
677 if (flg
) { /* rewrite record file */
678 #ifdef UPDATE_RECORD_IN_PLACE
679 (void) fseek(rfile
, (t0
->fpos
>= 0 ? t0
->fpos
: final_fpos
),
682 (void) fclose(rfile
);
683 if (!(rfile
= fopen_datafile(RECORD
, "w", SCOREPREFIX
))) {
684 HUP
raw_print("Cannot write record file");
686 free_ttlist(tt_head
);
689 #endif /* UPDATE_RECORD_IN_PLACE */
693 topten_print("You made the top ten list!");
698 "You reached the %d%s place on the top %d list.",
699 rank0
, ordin(rank0
), sysopt
.entrymax
);
712 for (rank
= 1; t1
->points
!= 0; rank
++, t1
= t1
->tt_next
) {
714 #ifdef UPDATE_RECORD_IN_PLACE
718 writeentry(rfile
, t1
);
721 if (rank
> flags
.end_top
&& (rank
< rank0
- flags
.end_around
722 || rank
> rank0
+ flags
.end_around
)
724 || (sysopt
.pers_is_uid
726 : strncmp(t1
->name
, t0
->name
, NAMSZ
) == 0)))
728 if (rank
== rank0
- flags
.end_around
729 && rank0
> flags
.end_top
+ flags
.end_around
+ 1 && !flags
.end_own
)
732 outentry(rank
, t1
, FALSE
);
734 outentry(rank
, t1
, TRUE
);
736 outentry(rank
, t1
, TRUE
);
737 outentry(0, t0
, TRUE
);
742 outentry(0, t0
, TRUE
);
743 #ifdef UPDATE_RECORD_IN_PLACE
746 /* if a reasonable way to truncate a file exists, use it */
747 truncate_file(rfile
);
749 /* use sentinel record rather than relying on truncation */
750 t1
->points
= 0L; /* terminates file when read back in */
751 t1
->ver_major
= t1
->ver_minor
= t1
->patchlevel
= 0;
752 t1
->uid
= t1
->deathdnum
= t1
->deathlev
= 0;
753 t1
->maxlvl
= t1
->hp
= t1
->maxhp
= t1
->deaths
= 0;
754 t1
->plrole
[0] = t1
->plrace
[0] = t1
->plgend
[0] = t1
->plalign
[0] = '-';
755 t1
->plrole
[1] = t1
->plrace
[1] = t1
->plgend
[1] = t1
->plalign
[1] = 0;
756 t1
->birthdate
= t1
->deathdate
= yyyymmdd((time_t) 0L);
757 Strcpy(t1
->name
, "@");
758 Strcpy(t1
->death
, "<eod>\n");
759 writeentry(rfile
, t1
);
760 (void) fflush(rfile
);
761 #endif /* TRUNCATE_FILE */
763 #endif /* UPDATE_RECORD_IN_PLACE */
764 (void) fclose(rfile
);
766 free_ttlist(tt_head
);
769 if (iflags
.toptenwin
&& !done_stopprint
)
770 display_nhwindow(toptenwin
, 1);
774 if (iflags
.toptenwin
) {
775 destroy_nhwindow(toptenwin
);
786 Strcpy(linebuf
, " No Points Name");
788 while (bp
< linebuf
+ COLNO
- 9)
790 Strcpy(bp
, "Hp [max]");
791 topten_print(linebuf
);
794 /* so>0: standout line; so=0: ordinary line */
796 outentry(rank
, t1
, so
)
797 struct toptenentry
*t1
;
801 boolean second_line
= TRUE
;
803 char *bp
, hpbuf
[24], linebuf3
[BUFSZ
];
808 Sprintf(eos(linebuf
), "%3d", rank
);
810 Strcat(linebuf
, " ");
812 Sprintf(eos(linebuf
), " %10ld %.10s", t1
->points
? t1
->points
: u
.urexp
,
814 Sprintf(eos(linebuf
), "-%s", t1
->plrole
);
815 if (t1
->plrace
[0] != '?')
816 Sprintf(eos(linebuf
), "-%s", t1
->plrace
);
817 /* Printing of gender and alignment is intentional. It has been
818 * part of the NetHack Geek Code, and illustrates a proper way to
819 * specify a character from the command line.
821 Sprintf(eos(linebuf
), "-%s", t1
->plgend
);
822 if (t1
->plalign
[0] != '?')
823 Sprintf(eos(linebuf
), "-%s ", t1
->plalign
);
825 Strcat(linebuf
, " ");
826 if (!strncmp("escaped", t1
->death
, 7)) {
827 Sprintf(eos(linebuf
), "escaped the dungeon %s[max level %d]",
828 !strncmp(" (", t1
->death
+ 7, 2) ? t1
->death
+ 7 + 2 : "",
830 /* fixup for closing paren in "escaped... with...Amulet)[max..." */
831 if ((bp
= index(linebuf
, ')')) != 0)
832 *bp
= (t1
->deathdnum
== astral_level
.dnum
) ? '\0' : ' ';
834 } else if (!strncmp("ascended", t1
->death
, 8)) {
835 Sprintf(eos(linebuf
), "ascended to demigod%s-hood",
836 (t1
->plgend
[0] == 'F') ? "dess" : "");
839 if (!strncmp(t1
->death
, "quit", 4)) {
840 Strcat(linebuf
, "quit");
842 } else if (!strncmp(t1
->death
, "died of st", 10)) {
843 Strcat(linebuf
, "starved to death");
845 } else if (!strncmp(t1
->death
, "choked", 6)) {
846 Sprintf(eos(linebuf
), "choked on h%s food",
847 (t1
->plgend
[0] == 'F') ? "er" : "is");
848 } else if (!strncmp(t1
->death
, "poisoned", 8)) {
849 Strcat(linebuf
, "was poisoned");
850 } else if (!strncmp(t1
->death
, "crushed", 7)) {
851 Strcat(linebuf
, "was crushed to death");
852 } else if (!strncmp(t1
->death
, "petrified by ", 13)) {
853 Strcat(linebuf
, "turned to stone");
855 Strcat(linebuf
, "died");
857 if (t1
->deathdnum
== astral_level
.dnum
) {
858 const char *arg
, *fmt
= " on the Plane of %s";
860 switch (t1
->deathlev
) {
862 fmt
= " on the %s Plane";
881 Sprintf(eos(linebuf
), fmt
, arg
);
883 Sprintf(eos(linebuf
), " in %s", dungeons
[t1
->deathdnum
].dname
);
884 if (t1
->deathdnum
!= knox_level
.dnum
)
885 Sprintf(eos(linebuf
), " on level %d", t1
->deathlev
);
886 if (t1
->deathlev
!= t1
->maxlvl
)
887 Sprintf(eos(linebuf
), " [max %d]", t1
->maxlvl
);
890 /* kludge for "quit while already on Charon's boat" */
891 if (!strncmp(t1
->death
, "quit ", 5))
892 Strcat(linebuf
, t1
->death
+ 4);
894 Strcat(linebuf
, ".");
896 /* Quit, starved, ascended, and escaped contain no second line */
898 Sprintf(eos(linebuf
), " %c%s.", highc(*(t1
->death
)), t1
->death
+ 1);
900 lngr
= (int) strlen(linebuf
);
902 hpbuf
[0] = '-', hpbuf
[1] = '\0';
904 Sprintf(hpbuf
, "%d", t1
->hp
);
905 /* beginning of hp column after padding (not actually padded yet) */
906 hppos
= COLNO
- (sizeof(" Hp [max]") - 1); /* sizeof(str) includes \0 */
907 while (lngr
>= hppos
) {
908 for (bp
= eos(linebuf
); !(*bp
== ' ' && (bp
- linebuf
< hppos
)); bp
--)
910 /* special case: word is too long, wrap in the middle */
911 if (linebuf
+ 15 >= bp
)
912 bp
= linebuf
+ hppos
- 1;
913 /* special case: if about to wrap in the middle of maximum
914 dungeon depth reached, wrap in front of it instead */
915 if (bp
> linebuf
+ 5 && !strncmp(bp
- 5, " [max", 5))
918 Strcpy(linebuf3
, bp
);
920 Strcpy(linebuf3
, bp
+ 1);
923 while (bp
< linebuf
+ (COLNO
- 1))
926 topten_print_bold(linebuf
);
928 topten_print(linebuf
);
929 Sprintf(linebuf
, "%15s %s", "", linebuf3
);
930 lngr
= strlen(linebuf
);
932 /* beginning of hp column not including padding */
933 hppos
= COLNO
- 7 - (int) strlen(hpbuf
);
936 if (bp
<= linebuf
+ hppos
) {
937 /* pad any necessary blanks to the hit point entry */
938 while (bp
< linebuf
+ hppos
)
941 Sprintf(eos(bp
), " %s[%d]",
942 (t1
->maxhp
< 10) ? " " : (t1
->maxhp
< 100) ? " " : "",
950 while (bp
< linebuf
+ so
)
953 topten_print_bold(linebuf
);
955 topten_print(linebuf
);
959 score_wanted(current_ver
, rank
, t1
, playerct
, players
, uid
)
962 struct toptenentry
*t1
;
964 const char **players
;
970 && (t1
->ver_major
!= VERSION_MAJOR
|| t1
->ver_minor
!= VERSION_MINOR
971 || t1
->patchlevel
!= PATCHLEVEL
))
974 if (sysopt
.pers_is_uid
&& !playerct
&& t1
->uid
== uid
)
977 for (i
= 0; i
< playerct
; i
++) {
978 if (players
[i
][0] == '-' && index("pr", players
[i
][1])
979 && players
[i
][2] == 0 && i
+ 1 < playerct
) {
980 const char *arg
= players
[i
+ 1];
981 if ((players
[i
][1] == 'p'
982 && str2role(arg
) == str2role(t1
->plrole
))
983 || (players
[i
][1] == 'r'
984 && str2race(arg
) == str2race(t1
->plrace
)))
987 } else if (strcmp(players
[i
], "all") == 0
988 || strncmp(t1
->name
, players
[i
], NAMSZ
) == 0
989 || (players
[i
][0] == '-' && players
[i
][1] == t1
->plrole
[0]
990 && players
[i
][2] == 0)
991 || (digit(players
[i
][0]) && rank
<= atoi(players
[i
])))
998 * print selected parts of score list.
999 * argc >= 2, with argv[0] untrustworthy (directory names, et al.),
1000 * and argv[1] starting with "-s".
1007 const char **players
;
1009 boolean current_ver
= TRUE
, init_done
= FALSE
;
1010 register struct toptenentry
*t1
;
1012 boolean match_found
= FALSE
;
1016 const char *player0
;
1018 if (argc
< 2 || strncmp(argv
[1], "-s", 2)) {
1019 raw_printf("prscore: bad arguments (%d)", argc
);
1023 rfile
= fopen_datafile(RECORD
, "r", SCOREPREFIX
);
1025 raw_print("Cannot open record file!");
1031 extern winid amii_rawprwin
;
1033 init_nhwindows(&argc
, argv
);
1034 amii_rawprwin
= create_nhwindow(NHW_TEXT
);
1038 /* If the score list isn't after a game, we never went through
1039 * initialization. */
1040 if (wiz1_level
.dlevel
== 0) {
1046 if (!argv
[1][2]) { /* plain "-s" */
1052 if (argc
> 1 && !strcmp(argv
[1], "-v")) {
1053 current_ver
= FALSE
;
1059 if (sysopt
.pers_is_uid
) {
1062 players
= (const char **) 0;
1067 player0
= "all"; /* single user system */
1069 player0
= "hackplayer";
1076 players
= (const char **) ++argv
;
1080 t1
= tt_head
= newttentry();
1081 for (rank
= 1;; rank
++) {
1082 readentry(rfile
, t1
);
1083 if (t1
->points
== 0)
1086 && score_wanted(current_ver
, rank
, t1
, playerct
, players
, uid
))
1088 t1
->tt_next
= newttentry();
1092 (void) fclose(rfile
);
1101 for (rank
= 1; t1
->points
!= 0; rank
++, t1
= t1
->tt_next
) {
1102 if (score_wanted(current_ver
, rank
, t1
, playerct
, players
, uid
))
1103 (void) outentry(rank
, t1
, FALSE
);
1106 Sprintf(pbuf
, "Cannot find any %sentries for ",
1107 current_ver
? "current " : "");
1109 Strcat(pbuf
, "you.");
1112 Strcat(pbuf
, "any of ");
1113 for (i
= 0; i
< playerct
; i
++) {
1114 /* stop printing players if there are too many to fit */
1115 if (strlen(pbuf
) + strlen(players
[i
]) + 2 >= BUFSZ
) {
1116 if (strlen(pbuf
) < BUFSZ
- 4)
1117 Strcat(pbuf
, "...");
1119 Strcpy(pbuf
+ strlen(pbuf
) - 4, "...");
1122 Strcat(pbuf
, players
[i
]);
1123 if (i
< playerct
- 1) {
1124 if (players
[i
][0] == '-' && index("pr", players
[i
][1])
1125 && players
[i
][2] == 0)
1133 raw_printf("Usage: %s -s [-v] <playertypes> [maxrank] [playernames]",
1136 raw_printf("Player types are: [-p role] [-r race]");
1138 free_ttlist(tt_head
);
1141 extern winid amii_rawprwin
;
1143 display_nhwindow(amii_rawprwin
, 1);
1144 destroy_nhwindow(amii_rawprwin
);
1145 amii_rawprwin
= WIN_ERR
;
1157 /* Look for this role in the role table */
1158 for (i
= 0; roles
[i
].name
.m
; i
++)
1159 if (!strncmp(plch
, roles
[i
].filecode
, ROLESZ
)) {
1160 if (fem
&& roles
[i
].femalenum
!= NON_PM
)
1161 return roles
[i
].femalenum
;
1162 else if (roles
[i
].malenum
!= NON_PM
)
1163 return roles
[i
].malenum
;
1167 /* this might be from a 3.2.x score for former Elf class */
1168 if (!strcmp(plch
, "E"))
1171 impossible("What weird role is this? (%s)", plch
);
1172 return PM_HUMAN_MUMMY
;
1176 * Get a random player name and class from the high score list,
1178 struct toptenentry
*
1179 get_rnd_toptenentry()
1183 register struct toptenentry
*tt
;
1184 static struct toptenentry tt_buf
;
1186 rfile
= fopen_datafile(RECORD
, "r", SCOREPREFIX
);
1188 impossible("Cannot open record file!");
1193 rank
= rnd(sysopt
.tt_oname_maxrank
);
1195 for (i
= rank
; i
; i
--) {
1196 readentry(rfile
, tt
);
1197 if (tt
->points
== 0)
1201 if (tt
->points
== 0) {
1210 (void) fclose(rfile
);
1216 * Attach random player name and class from high score list
1217 * to an object (for statues or morgue corpses).
1223 struct toptenentry
*tt
;
1225 return (struct obj
*) 0;
1227 tt
= get_rnd_toptenentry();
1230 return (struct obj
*) 0;
1232 set_corpsenm(otmp
, classmon(tt
->plrole
, (tt
->plgend
[0] == 'F')));
1233 otmp
= oname(otmp
, tt
->name
);
1238 #ifdef NO_SCAN_BRACK
1239 /* Lattice scanf isn't up to reading the scorefile. What */
1240 /* follows deals with that; I admit it's ugly. (KL) */
1241 /* Now generally available (KL) */
1246 while ((p
= index(p
, ' ')) != 0)
1254 while ((p
= index(p
, '|')) != 0)
1257 #endif /* NO_SCAN_BRACK */