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 /*-Copyright (c) Robert Patrick Rankin, 2012. */
4 /* NetHack may be freely redistributed. See license for details. */
6 /* JNetHack Copyright */
7 /* (c) Issei Numata, Naoki Hamada, Shigehiro Miyashita, 1994-2000 */
8 /* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2020 */
9 /* JNetHack may be freely redistributed. See license for details. */
13 #ifdef SHORT_FILENAMES
16 #include "patchlevel.h"
20 /* We don't want to rewrite the whole file, because that entails
21 creating a new version which requires that the old one be deletable. */
22 #define UPDATE_RECORD_IN_PLACE
26 * Updating in place can leave junk at the end of the file in some
27 * circumstances (if it shrinks and the O.S. doesn't have a straightforward
28 * way to truncate it). The trailing junk is harmless and the code
29 * which reads the scores will ignore it.
31 #ifdef UPDATE_RECORD_IN_PLACE
32 static long final_fpos;
35 #define done_stopprint program_state.stopprint
37 #define newttentry() (struct toptenentry *) alloc(sizeof (struct toptenentry))
38 #define dealloc_ttentry(ttent) free((genericptr_t) (ttent))
40 /* Changing NAMSZ can break your existing record/logfile */
47 struct toptenentry *tt_next;
48 #ifdef UPDATE_RECORD_IN_PLACE
52 int deathdnum, deathlev;
53 int maxlvl, hp, maxhp, deaths;
54 int ver_major, ver_minor, patchlevel;
55 long deathdate, birthdate;
57 char plrole[ROLESZ + 1];
58 char plrace[ROLESZ + 1];
59 char plgend[ROLESZ + 1];
60 char plalign[ROLESZ + 1];
62 char death[DTHSZ + 1];
64 /* size big enough to read in all the string fields at once; includes
65 room for separating space or trailing newline plus string terminator */
66 #define SCANBUFSZ (4 * (ROLESZ + 1) + (NAMSZ + 1) + (DTHSZ + 1) + 1)
68 STATIC_DCL void FDECL(topten_print, (const char *));
69 STATIC_DCL void FDECL(topten_print_bold, (const char *));
70 STATIC_DCL void NDECL(outheader);
71 STATIC_DCL void FDECL(outentry, (int, struct toptenentry *, BOOLEAN_P));
72 STATIC_DCL void FDECL(discardexcess, (FILE *));
73 STATIC_DCL void FDECL(readentry, (FILE *, struct toptenentry *));
74 STATIC_DCL void FDECL(writeentry, (FILE *, struct toptenentry *));
76 STATIC_DCL void FDECL(writexlentry, (FILE *, struct toptenentry *, int));
77 STATIC_DCL long NDECL(encodexlogflags);
78 STATIC_DCL long NDECL(encodeconduct);
79 STATIC_DCL long NDECL(encodeachieve);
81 STATIC_DCL void FDECL(free_ttlist, (struct toptenentry *));
82 STATIC_DCL int FDECL(classmon, (char *, BOOLEAN_P));
83 STATIC_DCL int FDECL(score_wanted, (BOOLEAN_P, int, struct toptenentry *, int,
86 STATIC_DCL void FDECL(nsb_mung_line, (char *));
87 STATIC_DCL void FDECL(nsb_unmung_line, (char *));
90 static winid toptenwin = WIN_ERR;
92 /* "killed by",&c ["an"] 'killer.name' */
94 formatkiller(buf, siz, how, incl_helpless)
98 boolean incl_helpless;
100 static NEARDATA const char *const killed_by_prefix[] = {
101 /* DIED, CHOKING, POISONING, STARVING, */
103 "killed by ", "choked on ", "poisoned by ", "died of ",
105 "
\8e\80\82ñ
\82¾", "
\82Å
\92\82\91§
\82µ
\82½", "
\82Ì
\93Å
\82Å
\8e\80\82ñ
\82¾", "",
106 /* DROWNING, BURNING, DISSOLVED, CRUSHING, */
108 "drowned in ", "burned by ", "dissolved in ", "crushed to death by ",
110 "
\93M
\8e\80\82µ
\82½","
\8fÄ
\8e\80\82µ
\82½", "
\97n
\8aâ
\82É
\97n
\82¯
\82½", "
\89\9f\82µ
\92×
\82³
\82ê
\82½",
111 /* STONING, TURNED_SLIME, GENOCIDED, */
113 "petrified by ", "turned to slime by ", "killed by ",
115 "
\90Î
\82É
\82È
\82Á
\82½", "
\82É
\83X
\83\89\83C
\83\80\82É
\82³
\82ê
\82½", "
\8bs
\8eE
\82³
\82ê
\82½",
116 /* PANICKED, TRICKED, QUIT, ESCAPED, ASCENDED */
121 char c, *kname = killer.name;
123 char *kname = killer.name;
126 buf[0] = '\0'; /* lint suppression */
127 #if 1 /*JP*//*
\90æ
\82É
\91Î
\8fÛ
\82ð
\83R
\83s
\81[*/
128 strncat(buf, kname, siz - 1);
131 switch (killer.format) {
133 impossible("bad killer format? (%d)", killer.format);
135 case NO_KILLER_PREFIX:
138 #if 0 /*JP*//*
\93ú
\96{
\8cê
\82Å
\82Í
\95s
\97v*/
144 (void) strncat(buf, killed_by_prefix[how], siz - 1);
147 #else /*JP:
\8aù
\82É
\91Î
\8fÛ
\82ð
\83R
\83s
\81[
\82µ
\82Ä
\82¢
\82é
\82Ì
\82Å
\92P
\82É
\92Ç
\89Á*/
148 (void) strncat(buf, killed_by_prefix[how], siz - 1);
153 (void) strncat(buf, "
\82É
\8eE
\82³
\82ê
\82½", siz - 1);
156 #if 0 /*JP*//*
\8aù
\82É
\83R
\83s
\81[
\8dÏ
\82Ý*//*JP:TODO:
\83T
\83j
\83^
\83C
\83Y
\82ª
\95K
\97v*/
157 /* Copy kname into buf[].
158 * Object names and named fruit have already been sanitized, but
159 * monsters can have "called 'arbitrary text'" attached to them,
160 * so make sure that that text can't confuse field splitting when
161 * record, logfile, or xlogfile is re-read at some later point.
169 /* 'xlogfile' doesn't really need protection for '=', but
170 fixrecord.awk for corrupted 3.6.0 'record' does (only
171 if using xlogfile rather than logfile to repair record) */
174 /* tab is not possible due to use of mungspaces() when naming;
175 it would disrupt xlogfile parsing if it were present */
183 #if 0 /*JP*//*JP:TODO:
\8e\80\88ö
\82Ì
\91O
\82É
\8e\9d\82Á
\82Ä
\8ds
\82©
\82È
\82¯
\82ê
\82Î
\82È
\82ç
\82È
\82¢*/
184 if (incl_helpless && multi) {
185 /* X <= siz: 'sizeof "string"' includes 1 for '\0' terminator */
186 if (multi_reason && strlen(multi_reason) + sizeof ", while " <= siz)
187 Sprintf(buf, ", while %s", multi_reason);
188 /* either multi_reason wasn't specified or wouldn't fit */
189 else if (sizeof ", while helpless" <= siz)
190 Strcpy(buf, ", while helpless");
191 /* else extra death info won't fit, so leave it out */
200 if (toptenwin == WIN_ERR)
203 putstr(toptenwin, ATR_NONE, x);
210 if (toptenwin == WIN_ERR)
213 putstr(toptenwin, ATR_BOLD, x);
217 observable_depth(lev)
221 /* if we ever randomize the order of the elemental planes, we
222 must use a constant external representation in the record file */
223 if (In_endgame(lev)) {
224 if (Is_astralevel(lev))
226 else if (Is_waterlevel(lev))
228 else if (Is_firelevel(lev))
230 else if (Is_airlevel(lev))
232 else if (Is_earthlevel(lev))
241 /* throw away characters until current record has been entirely consumed */
250 } while (c != '\n' && c != EOF);
256 struct toptenentry *tt;
258 char inbuf[SCANBUFSZ], s1[SCANBUFSZ], s2[SCANBUFSZ], s3[SCANBUFSZ],
259 s4[SCANBUFSZ], s5[SCANBUFSZ], s6[SCANBUFSZ];
261 #ifdef NO_SCAN_BRACK /* Version_ Pts DgnLevs_ Hp___ Died__Born id */
262 static const char fmt[] = "%d %d %d %ld %d %d %d %d %d %d %ld %ld %d%*c";
263 static const char fmt32[] = "%c%c %s %s%*c";
264 static const char fmt33[] = "%s %s %s %s %s %s%*c";
266 static const char fmt[] = "%d.%d.%d %ld %d %d %d %d %d %d %ld %ld %d ";
267 static const char fmt32[] = "%c%c %[^,],%[^\n]%*c";
268 static const char fmt33[] = "%s %s %s %s %[^,],%[^\n]%*c";
271 #ifdef UPDATE_RECORD_IN_PLACE
272 /* note: input below must read the record's terminating newline */
273 final_fpos = tt->fpos = ftell(rfile);
276 if (fscanf(rfile, fmt, &tt->ver_major, &tt->ver_minor, &tt->patchlevel,
277 &tt->points, &tt->deathdnum, &tt->deathlev, &tt->maxlvl,
278 &tt->hp, &tt->maxhp, &tt->deaths, &tt->deathdate,
279 &tt->birthdate, &tt->uid) != TTFIELDS) {
282 discardexcess(rfile);
284 /* load remainder of record into a local buffer;
285 this imposes an implicit length limit of SCANBUFSZ
286 on every string field extracted from the buffer */
287 if (!fgets(inbuf, sizeof inbuf, rfile)) {
288 /* sscanf will fail and tt->points will be set to 0 */
290 } else if (!index(inbuf, '\n')) {
291 Strcpy(&inbuf[sizeof inbuf - 2], "\n");
292 discardexcess(rfile);
294 /* Check for backwards compatibility */
295 if (tt->ver_major < 3 || (tt->ver_major == 3 && tt->ver_minor < 3)) {
298 if (sscanf(inbuf, fmt32, tt->plrole, tt->plgend, s1, s2) == 4) {
299 tt->plrole[1] = tt->plgend[1] = '\0'; /* read via %c */
300 copynchars(tt->name, s1, (int) (sizeof tt->name) - 1);
301 copynchars(tt->death, s2, (int) (sizeof tt->death) - 1);
304 tt->plrole[1] = '\0';
305 if ((i = str2role(tt->plrole)) >= 0)
306 Strcpy(tt->plrole, roles[i].filecode);
307 Strcpy(tt->plrace, "?");
308 Strcpy(tt->plgend, (tt->plgend[0] == 'M') ? "Mal" : "Fem");
309 Strcpy(tt->plalign, "?");
310 } else if (sscanf(inbuf, fmt33, s1, s2, s3, s4, s5, s6) == 6) {
311 copynchars(tt->plrole, s1, (int) (sizeof tt->plrole) - 1);
312 copynchars(tt->plrace, s2, (int) (sizeof tt->plrace) - 1);
313 copynchars(tt->plgend, s3, (int) (sizeof tt->plgend) - 1);
314 copynchars(tt->plalign, s4, (int) (sizeof tt->plalign) - 1);
315 copynchars(tt->name, s5, (int) (sizeof tt->name) - 1);
316 copynchars(tt->death, s6, (int) (sizeof tt->death) - 1);
320 if (tt->points > 0) {
321 nsb_unmung_line(tt->name);
322 nsb_unmung_line(tt->death);
327 /* check old score entries for Y2K problem and fix whenever found */
328 if (tt->points > 0) {
329 if (tt->birthdate < 19000000L)
330 tt->birthdate += 19000000L;
331 if (tt->deathdate < 19000000L)
332 tt->deathdate += 19000000L;
337 writeentry(rfile, tt)
339 struct toptenentry *tt;
341 static const char fmt32[] = "%c%c "; /* role,gender */
342 static const char fmt33[] = "%s %s %s %s "; /* role,race,gndr,algn */
343 #ifndef NO_SCAN_BRACK
344 static const char fmt0[] = "%d.%d.%d %ld %d %d %d %d %d %d %ld %ld %d ";
345 static const char fmtX[] = "%s,%s\n";
346 #else /* NO_SCAN_BRACK */
347 static const char fmt0[] = "%d %d %d %ld %d %d %d %d %d %d %ld %ld %d ";
348 static const char fmtX[] = "%s %s\n";
350 nsb_mung_line(tt->name);
351 nsb_mung_line(tt->death);
354 (void) fprintf(rfile, fmt0, tt->ver_major, tt->ver_minor, tt->patchlevel,
355 tt->points, tt->deathdnum, tt->deathlev, tt->maxlvl,
356 tt->hp, tt->maxhp, tt->deaths, tt->deathdate,
357 tt->birthdate, tt->uid);
358 if (tt->ver_major < 3 || (tt->ver_major == 3 && tt->ver_minor < 3))
359 (void) fprintf(rfile, fmt32, tt->plrole[0], tt->plgend[0]);
361 (void) fprintf(rfile, fmt33, tt->plrole, tt->plrace, tt->plgend,
363 (void) fprintf(rfile, fmtX, onlyspace(tt->name) ? "_" : tt->name,
367 nsb_unmung_line(tt->name);
368 nsb_unmung_line(tt->death);
374 /* as tab is never used in eg. plname or death, no need to mangle those. */
376 writexlentry(rfile, tt, how)
378 struct toptenentry *tt;
381 #define Fprintf (void) fprintf
382 #define XLOG_SEP '\t' /* xlogfile field separator. */
383 char buf[BUFSZ], tmpbuf[DTHSZ + 1];
385 Sprintf(buf, "version=%d.%d.%d", tt->ver_major, tt->ver_minor,
387 Sprintf(eos(buf), "%cpoints=%ld%cdeathdnum=%d%cdeathlev=%d", XLOG_SEP,
388 tt->points, XLOG_SEP, tt->deathdnum, XLOG_SEP, tt->deathlev);
389 Sprintf(eos(buf), "%cmaxlvl=%d%chp=%d%cmaxhp=%d", XLOG_SEP, tt->maxlvl,
390 XLOG_SEP, tt->hp, XLOG_SEP, tt->maxhp);
391 Sprintf(eos(buf), "%cdeaths=%d%cdeathdate=%ld%cbirthdate=%ld%cuid=%d",
392 XLOG_SEP, tt->deaths, XLOG_SEP, tt->deathdate, XLOG_SEP,
393 tt->birthdate, XLOG_SEP, tt->uid);
394 Fprintf(rfile, "%s", buf);
395 Sprintf(buf, "%crole=%s%crace=%s%cgender=%s%calign=%s", XLOG_SEP,
396 tt->plrole, XLOG_SEP, tt->plrace, XLOG_SEP, tt->plgend, XLOG_SEP,
398 /* make a copy of death reason that doesn't include ", while helpless" */
399 formatkiller(tmpbuf, sizeof tmpbuf, how, FALSE);
400 Fprintf(rfile, "%s%cname=%s%cdeath=%s",
401 buf, /* (already includes separator) */
402 XLOG_SEP, plname, XLOG_SEP, tmpbuf);
405 Fprintf(rfile, "%cwhile=%s", XLOG_SEP,
406 multi_reason ? multi_reason : "helpless");
408 Fprintf(rfile, "%cwhile=%s", XLOG_SEP,
409 multi_reason ? multi_reason : "
\96³
\97Í
\82È
\8aÔ
\82É");
411 Fprintf(rfile, "%cconduct=0x%lx%cturns=%ld%cachieve=0x%lx", XLOG_SEP,
412 encodeconduct(), XLOG_SEP, moves, XLOG_SEP, encodeachieve());
413 Fprintf(rfile, "%crealtime=%ld%cstarttime=%ld%cendtime=%ld", XLOG_SEP,
414 (long) urealtime.realtime, XLOG_SEP,
415 (long) ubirthday, XLOG_SEP, (long) urealtime.finish_time);
416 Fprintf(rfile, "%cgender0=%s%calign0=%s", XLOG_SEP,
417 genders[flags.initgend].filecode, XLOG_SEP,
418 aligns[1 - u.ualignbase[A_ORIGINAL]].filecode);
419 Fprintf(rfile, "%cflags=0x%lx", XLOG_SEP, encodexlogflags());
420 Fprintf(rfile, "\n");
433 if (!u.uroleplay.numbones)
444 if (!u.uconduct.food)
446 if (!u.uconduct.unvegan)
448 if (!u.uconduct.unvegetarian)
450 if (!u.uconduct.gnostic)
452 if (!u.uconduct.weaphit)
454 if (!u.uconduct.killer)
456 if (!u.uconduct.literate)
458 if (!u.uconduct.polypiles)
460 if (!u.uconduct.polyselfs)
462 if (!u.uconduct.wishes)
464 if (!u.uconduct.wisharti)
466 if (!num_genocides())
479 if (u.uachieve.enter_gehennom)
481 if (u.uachieve.menorah)
485 if (u.uevent.invoked)
487 if (u.uachieve.amulet)
489 if (In_endgame(&u.uz))
491 if (Is_astralevel(&u.uz))
493 if (u.uachieve.ascended)
495 if (u.uachieve.mines_luckstone)
497 if (u.uachieve.finish_sokoban)
499 if (u.uachieve.killed_medusa)
501 if (u.uroleplay.blind)
503 if (u.uroleplay.nudist)
509 #endif /* XLOGFILE */
513 struct toptenentry *tt;
515 struct toptenentry *ttnext;
517 while (tt->points > 0) {
518 ttnext = tt->tt_next;
531 int rank, rank0 = -1, rank1 = 0;
532 int occ_cnt = sysopt.persmax;
533 register struct toptenentry *t0, *tprev;
534 struct toptenentry *t1;
536 register int flg = 0;
543 #endif /* XLOGFILE */
546 /* Under DICE 3.0, this crashes the system consistently, apparently due to
547 * corruption of *rfile somewhere. Until I figure this out, just cut out
548 * topten support entirely - at least then the game exits cleanly. --AC
553 /* If we are in the midst of a panic, cut out topten entirely.
554 * topten uses alloc() several times, which will lead to
555 * problems if the panic was the result of an alloc() failure.
557 if (program_state.panicking)
560 if (iflags.toptenwin) {
561 toptenwin = create_nhwindow(NHW_TEXT);
564 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
565 #define HUP if (!program_state.done_hup)
571 restore_colors(); /* make sure the screen is black on white */
573 /* create a new 'topten' entry */
576 t0->ver_major = VERSION_MAJOR;
577 t0->ver_minor = VERSION_MINOR;
578 t0->patchlevel = PATCHLEVEL;
579 t0->points = u.urexp;
580 t0->deathdnum = u.uz.dnum;
581 /* deepest_lev_reached() is in terms of depth(), and reporting the
582 * deepest level reached in the dungeon death occurred in doesn't
583 * seem right, so we have to report the death level in depth() terms
584 * as well (which also seems reasonable since that's all the player
585 * sees on the screen anyway)
587 t0->deathlev = observable_depth(&u.uz);
588 t0->maxlvl = deepest_lev_reached(TRUE);
590 t0->maxhp = u.uhpmax;
591 t0->deaths = u.umortality;
593 copynchars(t0->plrole, urole.filecode, ROLESZ);
594 copynchars(t0->plrace, urace.filecode, ROLESZ);
595 copynchars(t0->plgend, genders[flags.female].filecode, ROLESZ);
596 copynchars(t0->plalign, aligns[1 - u.ualign.type].filecode, ROLESZ);
597 copynchars(t0->name, plname, NAMSZ);
598 formatkiller(t0->death, sizeof t0->death, how, TRUE);
599 t0->birthdate = yyyymmdd(ubirthday);
600 t0->deathdate = yyyymmdd(when);
602 #ifdef UPDATE_RECORD_IN_PLACE
606 #ifdef LOGFILE /* used for debugging (who dies of what, where) */
607 if (lock_file(LOGFILE, SCOREPREFIX, 10)) {
608 if (!(lfile = fopen_datafile(LOGFILE, "a", SCOREPREFIX))) {
609 HUP raw_print("Cannot open log file!");
611 writeentry(lfile, t0);
612 (void) fclose(lfile);
614 unlock_file(LOGFILE);
618 if (lock_file(XLOGFILE, SCOREPREFIX, 10)) {
619 if (!(xlfile = fopen_datafile(XLOGFILE, "a", SCOREPREFIX))) {
620 HUP raw_print("Cannot open extended log file!");
622 writexlentry(xlfile, t0, how);
623 (void) fclose(xlfile);
625 unlock_file(XLOGFILE);
627 #endif /* XLOGFILE */
629 if (wizard || discover) {
637 "Since you were in %s mode, the score list will not be checked.",
638 wizard ? "wizard" : "discover");
641 "%s
\83\82\81[
\83h
\82Å
\83v
\83\8c\83C
\82µ
\82½
\82Ì
\82Å
\83X
\83R
\83A
\83\8a\83X
\83g
\82É
\82Í
\8dÚ
\82ç
\82È
\82¢
\81D",
642 wizard ? "
\83E
\83B
\83U
\81[
\83h" : "
\94
\8c©");
649 if (!lock_file(RECORD, SCOREPREFIX, 60))
652 #ifdef UPDATE_RECORD_IN_PLACE
653 rfile = fopen_datafile(RECORD, "r+", SCOREPREFIX);
655 rfile = fopen_datafile(RECORD, "r", SCOREPREFIX);
659 HUP raw_print("Cannot open record file!");
664 HUP topten_print("");
666 /* assure minimum number of points */
667 if (t0->points < sysopt.pointsmin)
670 t1 = tt_head = newttentry();
672 /* rank0: -1 undefined, 0 not_on_list, n n_th on list */
674 readentry(rfile, t1);
675 if (t1->points < sysopt.pointsmin)
677 if (rank0 < 0 && t1->points < t0->points) {
684 #ifdef UPDATE_RECORD_IN_PLACE
685 t0->fpos = t1->fpos; /* insert here */
689 flg++; /* ask for a rewrite */
695 if ((sysopt.pers_is_uid ? t1->uid == t0->uid
696 : strncmp(t1->name, t0->name, NAMSZ) == 0)
697 && !strncmp(t1->plrole, t0->plrole, ROLESZ) && --occ_cnt <= 0) {
706 "You didn't beat your previous score of %ld points.",
708 "
\82 \82È
\82½
\82Í
\88È
\91O
\82Ì%ld
\83|
\83C
\83\93\83g
\82Ì
\83X
\83R
\83A
\82É
\93Í
\82©
\82È
\82©
\82Á
\82½
\81D",
719 if (rank <= sysopt.entrymax) {
720 t1->tt_next = newttentry();
724 if (rank > sysopt.entrymax) {
729 if (flg) { /* rewrite record file */
730 #ifdef UPDATE_RECORD_IN_PLACE
731 (void) fseek(rfile, (t0->fpos >= 0 ? t0->fpos : final_fpos),
734 (void) fclose(rfile);
735 if (!(rfile = fopen_datafile(RECORD, "w", SCOREPREFIX))) {
736 HUP raw_print("Cannot write record file");
738 free_ttlist(tt_head);
741 #endif /* UPDATE_RECORD_IN_PLACE */
746 topten_print("You made the top ten list!");
748 topten_print("
\82 \82È
\82½
\82Í
\83g
\83b
\83v10
\83\8a\83X
\83g
\82É
\8dÚ
\82Á
\82½
\81I");
754 "You reached the %d%s place on the top %d list.",
755 rank0, ordin(rank0), sysopt.entrymax);
758 "
\82 \82È
\82½
\82Í
\81C
\83g
\83b
\83v%d
\83\8a\83X
\83g
\82Ì%d
\88Ê
\82É
\8dÚ
\82Á
\82½
\81D",
759 sysopt.entrymax, rank0);
773 for (rank = 1; t1->points != 0; rank++, t1 = t1->tt_next) {
775 #ifdef UPDATE_RECORD_IN_PLACE
779 writeentry(rfile, t1);
782 if (rank > flags.end_top && (rank < rank0 - flags.end_around
783 || rank > rank0 + flags.end_around)
785 || (sysopt.pers_is_uid
787 : strncmp(t1->name, t0->name, NAMSZ) == 0)))
789 if (rank == rank0 - flags.end_around
790 && rank0 > flags.end_top + flags.end_around + 1 && !flags.end_own)
793 outentry(rank, t1, FALSE);
795 outentry(rank, t1, TRUE);
797 outentry(rank, t1, TRUE);
798 outentry(0, t0, TRUE);
803 outentry(0, t0, TRUE);
804 #ifdef UPDATE_RECORD_IN_PLACE
807 /* if a reasonable way to truncate a file exists, use it */
808 truncate_file(rfile);
810 /* use sentinel record rather than relying on truncation */
811 t1->points = 0L; /* terminates file when read back in */
812 t1->ver_major = t1->ver_minor = t1->patchlevel = 0;
813 t1->uid = t1->deathdnum = t1->deathlev = 0;
814 t1->maxlvl = t1->hp = t1->maxhp = t1->deaths = 0;
815 t1->plrole[0] = t1->plrace[0] = t1->plgend[0] = t1->plalign[0] = '-';
816 t1->plrole[1] = t1->plrace[1] = t1->plgend[1] = t1->plalign[1] = 0;
817 t1->birthdate = t1->deathdate = yyyymmdd((time_t) 0L);
818 Strcpy(t1->name, "@");
819 Strcpy(t1->death, "<eod>\n");
820 writeentry(rfile, t1);
821 (void) fflush(rfile);
822 #endif /* TRUNCATE_FILE */
824 #endif /* UPDATE_RECORD_IN_PLACE */
825 (void) fclose(rfile);
827 free_ttlist(tt_head);
830 if (iflags.toptenwin && !done_stopprint)
831 display_nhwindow(toptenwin, 1);
835 if (iflags.toptenwin) {
836 destroy_nhwindow(toptenwin);
847 Strcpy(linebuf, " No Points Name");
849 while (bp < linebuf + COLNO - 9)
851 Strcpy(bp, "Hp [max]");
852 topten_print(linebuf);
855 /* so>0: standout line; so=0: ordinary line */
857 outentry(rank, t1, so)
858 struct toptenentry *t1;
862 boolean second_line = TRUE;
865 char *bp, hpbuf[24], linebuf3[BUFSZ];
886 Sprintf(eos(linebuf), "%3d", rank);
888 Strcat(linebuf, " ");
890 Sprintf(eos(linebuf), " %10ld %.10s", t1->points ? t1->points : u.urexp,
892 Sprintf(eos(linebuf), "-%s", t1->plrole);
893 if (t1->plrace[0] != '?')
894 Sprintf(eos(linebuf), "-%s", t1->plrace);
895 /* Printing of gender and alignment is intentional. It has been
896 * part of the NetHack Geek Code, and illustrates a proper way to
897 * specify a character from the command line.
899 Sprintf(eos(linebuf), "-%s", t1->plgend);
900 if (t1->plalign[0] != '?')
902 Sprintf(eos(linebuf), "-%s ", t1->plalign);
904 Sprintf(eos(linebuf), "-%s", t1->plalign);
907 Strcat(linebuf, " ");
911 Strcat(linebuf, "
\82Í");
912 /*JP:
\93ú
\96{
\8cê
\82Å
\82Í
\81u
\81\9b\81\9b\82ð
\8eè
\82É
\81v
\82ð
\90æ
\82É
\92Ç
\89Á
\82µ
\82È
\82¢
\82Æ
\95s
\8e©
\91R */
914 if (!strncmp(jdeath, "
\96\82\8f\9c\82¯
\82ð
\8eè
\82É", 12))
916 else if (!strncmp(jdeath, "
\93V
\8fã
\82Å
\92p
\90J
\82ð
\8eó
\82¯", 16))
918 else if (!strncmp(jdeath, "
\8bU
\95¨
\82Ì
\96\82\8f\9c\82¯
\82ð
\92Í
\82Ü
\82³
\82ê", 24))
922 if (!strncmp("escaped", t1->death, 7)) {
924 if (!strncmp("
\92E
\8fo
\82µ
\82½", jdeath, 8)
925 || !strncmp("escaped", jdeath, 7)) {
928 Sprintf(eos(linebuf), "escaped the dungeon %s[max level %d]",
929 !strncmp(" (", t1->death + 7, 2) ? t1->death + 7 + 2 : "",
931 /* fixup for closing paren in "escaped... with...Amulet)[max..." */
932 if ((bp = index(linebuf, ')')) != 0)
933 *bp = (t1->deathdnum == astral_level.dnum) ? '\0' : ' ';
936 strncpy(jbuf, t1->death, jdeath - t1->death);
937 jbuf[jdeath - t1->death] = '\0';
938 Sprintf(action, "%s
\96À
\8b{
\82©
\82ç
\92E
\8fo
\82µ
\82½[
\8dÅ
\91å
\92n
\89º%d
\8aK]",
943 } else if (!strncmp("ascended", t1->death, 8)) {
945 } else if (!strncmp("
\8f¸
\93V
\82µ
\82½", jdeath, 8)
946 || !strncmp("ascended", jdeath, 8)) {
949 Sprintf(eos(linebuf), "ascended to demigod%s-hood",
950 (t1->plgend[0] == 'F') ? "dess" : "");
952 Sprintf(action, "
\8f¸
\93V
\82µ%s
\90_
\82Æ
\82È
\82Á
\82½",
953 (t1->plgend[0] == 'F') ? "
\8f\97" : "");
958 if (!strncmp(t1->death, "quit", 4)) {
960 if (!strncmp(jdeath, "
\94²
\82¯
\82½", 4)) {
962 Strcat(linebuf, "quit");
964 Strcat(action, t1->death);
968 } else if (!strncmp(t1->death, "died of st", 10)) {
969 Strcat(linebuf, "starved to death");
971 } else if (!strncmp(t1->death, "choked", 6)) {
972 Sprintf(eos(linebuf), "choked on h%s food",
973 (t1->plgend[0] == 'F') ? "er" : "is");
974 } else if (!strncmp(t1->death, "poisoned", 8)) {
975 Strcat(linebuf, "was poisoned");
976 } else if (!strncmp(t1->death, "crushed", 7)) {
977 Strcat(linebuf, "was crushed to death");
978 } else if (!strncmp(t1->death, "petrified by ", 13)) {
979 Strcat(linebuf, "turned to stone");
981 Strcat(linebuf, "died");
986 if (t1->deathdnum == astral_level.dnum) {
988 const char *arg, *fmt = " on the Plane of %s";
993 switch (t1->deathlev) {
996 fmt = " on the %s Plane";
1001 arg = "
\93V
\8fã
\8aE";
1007 arg = "
\90\85\82Ì
\90¸
\97ì
\8aE";
1013 arg = "
\89Î
\82Ì
\90¸
\97ì
\8aE";
1019 arg = "
\95\97\82Ì
\90¸
\97ì
\8aE";
1025 arg = "
\92n
\82Ì
\90¸
\97ì
\8aE";
1032 Sprintf(eos(linebuf), fmt, arg);
1034 Sprintf(where, "%s
\82É
\82Ä", arg);
1038 Sprintf(eos(linebuf), " in %s", dungeons[t1->deathdnum].dname);
1040 Sprintf(eos(linebuf), "%s", dungeons[t1->deathdnum].dname);
1041 if (t1->deathdnum != knox_level.dnum)
1043 Sprintf(eos(linebuf), " on level %d", t1->deathlev);
1045 Sprintf(eos(linebuf), "
\82Ì
\92n
\89º%d
\8aK
\82É
\82Ä", t1->deathlev);
1046 if (t1->deathlev != t1->maxlvl)
1048 Sprintf(eos(linebuf), " [max %d]", t1->maxlvl);
1050 Sprintf(eos(where), "[
\8dÅ
\91å
\92n
\89º%d
\8aK]", t1->maxlvl);
1053 #if 0 /*JP*//*
\93ú
\96{
\8cê
\82Å
\82Í
\8d×
\8dH
\95s
\97v */
1054 /* kludge for "quit while already on Charon's boat" */
1055 if (!strncmp(t1->death, "quit ", 5))
1056 Strcat(linebuf, t1->death + 4);
1060 Strcat(linebuf, ".");
1063 /* Quit, starved, ascended, and escaped contain no second line */
1066 Sprintf(eos(linebuf), " %c%s.", highc(*(t1->death)), t1->death + 1);
1068 Sprintf(action, "%s", t1->death);
1071 Sprintf(eos(linebuf), "%s%s%s
\81D", who, where, action);
1073 lngr = (int) strlen(linebuf);
1075 hpbuf[0] = '-', hpbuf[1] = '\0';
1077 Sprintf(hpbuf, "%d", t1->hp);
1078 /* beginning of hp column after padding (not actually padded yet) */
1079 hppos = COLNO - (sizeof(" Hp [max]") - 1); /* sizeof(str) includes \0 */
1081 while (lngr >= hppos) {
1082 /*JP hppos
\82æ
\82è
\91O
\82Ì
\93K
\93\96\82È
\88Ê
\92u
\82Å
\95ª
\8a\84\82·
\82é
\81D*/
1085 split_japanese(linebuf, car, cdr, hppos);
1089 while (bp < car + (COLNO-1)) *bp++ = ' ';
1091 topten_print_bold(car);
1095 Sprintf(linebuf, "%15s %s", "", cdr);
1096 lngr = (int)strlen(linebuf);
1098 /*JP:
\93ú
\96{
\8cê
\82ª
\93ü
\82é
\82Æ
\95¶
\8e\9a\97ñ
\82ð
\8cã
\82©
\82ç
\8c©
\82Ä
\82¢
\82
\82±
\82Æ
\82Í
\82Å
\82«
\82È
\82¢
\82½
\82ß
\83R
\83\81\83\93\83g
\83A
\83E
\83g*/
1100 while (lngr >= hppos) {
1101 for (bp = eos(linebuf); !(*bp == ' ' && (bp - linebuf < hppos)); bp--)
1103 /* special case: word is too long, wrap in the middle */
1104 if (linebuf + 15 >= bp)
1105 bp = linebuf + hppos - 1;
1106 /* special case: if about to wrap in the middle of maximum
1107 dungeon depth reached, wrap in front of it instead */
1108 if (bp > linebuf + 5 && !strncmp(bp - 5, " [max", 5))
1111 Strcpy(linebuf3, bp);
1113 Strcpy(linebuf3, bp + 1);
1116 while (bp < linebuf + (COLNO - 1))
1119 topten_print_bold(linebuf);
1121 topten_print(linebuf);
1122 Sprintf(linebuf, "%15s %s", "", linebuf3);
1123 lngr = strlen(linebuf);
1126 /* beginning of hp column not including padding */
1127 hppos = COLNO - 7 - (int) strlen(hpbuf);
1130 if (bp <= linebuf + hppos) {
1131 /* pad any necessary blanks to the hit point entry */
1132 while (bp < linebuf + hppos)
1135 Sprintf(eos(bp), " %s[%d]",
1136 (t1->maxhp < 10) ? " " : (t1->maxhp < 100) ? " " : "",
1144 while (bp < linebuf + so)
1147 topten_print_bold(linebuf);
1149 topten_print(linebuf);
1153 score_wanted(current_ver, rank, t1, playerct, players, uid)
1154 boolean current_ver;
1156 struct toptenentry *t1;
1158 const char **players;
1164 && (t1->ver_major != VERSION_MAJOR || t1->ver_minor != VERSION_MINOR
1165 || t1->patchlevel != PATCHLEVEL))
1168 if (sysopt.pers_is_uid && !playerct && t1->uid == uid)
1171 for (i = 0; i < playerct; i++) {
1172 if (players[i][0] == '-' && index("pr", players[i][1])
1173 && players[i][2] == 0 && i + 1 < playerct) {
1174 const char *arg = players[i + 1];
1175 if ((players[i][1] == 'p'
1176 && str2role(arg) == str2role(t1->plrole))
1177 || (players[i][1] == 'r'
1178 && str2race(arg) == str2race(t1->plrace)))
1181 } else if (strcmp(players[i], "all") == 0
1182 || strncmp(t1->name, players[i], NAMSZ) == 0
1183 || (players[i][0] == '-' && players[i][1] == t1->plrole[0]
1184 && players[i][2] == 0)
1185 || (digit(players[i][0]) && rank <= atoi(players[i])))
1192 * print selected parts of score list.
1193 * argc >= 2, with argv[0] untrustworthy (directory names, et al.),
1194 * and argv[1] starting with "-s".
1195 * caveat: some shells might allow argv elements to be arbitrarily long.
1202 const char **players;
1204 boolean current_ver = TRUE, init_done = FALSE;
1205 register struct toptenentry *t1;
1207 boolean match_found = FALSE;
1211 const char *player0;
1213 if (argc < 2 || strncmp(argv[1], "-s", 2)) {
1214 raw_printf("prscore: bad arguments (%d)", argc);
1218 rfile = fopen_datafile(RECORD, "r", SCOREPREFIX);
1220 raw_print("Cannot open record file!");
1226 extern winid amii_rawprwin;
1228 init_nhwindows(&argc, argv);
1229 amii_rawprwin = create_nhwindow(NHW_TEXT);
1233 /* If the score list isn't after a game, we never went through
1234 * initialization. */
1235 if (wiz1_level.dlevel == 0) {
1241 if (!argv[1][2]) { /* plain "-s" */
1247 if (argc > 1 && !strcmp(argv[1], "-v")) {
1248 current_ver = FALSE;
1254 if (sysopt.pers_is_uid) {
1257 players = (const char **) 0;
1262 player0 = "all"; /* single user system */
1264 player0 = "hackplayer";
1272 players = (const char **) ++argv;
1277 players = (const char **)alloc(sizeof(char *) * argc + 1);
1278 for (j = 0; j < argc; j++) {
1279 char *p = (char *)str2ic(argv[j]);
1280 players[j] = (char *)alloc(strlen(p) + 1);
1281 strcpy((void *)players[j], p);
1288 t1 = tt_head = newttentry();
1289 for (rank = 1;; rank++) {
1290 readentry(rfile, t1);
1291 if (t1->points == 0)
1294 && score_wanted(current_ver, rank, t1, playerct, players, uid))
1296 t1->tt_next = newttentry();
1300 (void) fclose(rfile);
1309 for (rank = 1; t1->points != 0; rank++, t1 = t1->tt_next) {
1310 if (score_wanted(current_ver, rank, t1, playerct, players, uid))
1311 (void) outentry(rank, t1, FALSE);
1314 Sprintf(pbuf, "Cannot find any %sentries for ",
1315 current_ver ? "current " : "");
1317 Strcat(pbuf, "you.");
1320 Strcat(pbuf, "any of ");
1321 for (i = 0; i < playerct; i++) {
1322 /* stop printing players if there are too many to fit */
1323 if (strlen(pbuf) + strlen(players[i]) + 2 >= BUFSZ) {
1324 if (strlen(pbuf) < BUFSZ - 4)
1325 Strcat(pbuf, "...");
1327 Strcpy(pbuf + strlen(pbuf) - 4, "...");
1330 Strcat(pbuf, players[i]);
1331 if (i < playerct - 1) {
1332 if (players[i][0] == '-' && index("pr", players[i][1])
1333 && players[i][2] == 0)
1341 raw_printf("Usage: %s -s [-v] <playertypes> [maxrank] [playernames]",
1344 raw_printf("Player types are: [-p role] [-r race]");
1346 free_ttlist(tt_head);
1349 extern winid amii_rawprwin;
1351 display_nhwindow(amii_rawprwin, 1);
1352 destroy_nhwindow(amii_rawprwin);
1353 amii_rawprwin = WIN_ERR;
1365 /* Look for this role in the role table */
1366 for (i = 0; roles[i].name.m; i++)
1367 if (!strncmp(plch, roles[i].filecode, ROLESZ)) {
1368 if (fem && roles[i].femalenum != NON_PM)
1369 return roles[i].femalenum;
1370 else if (roles[i].malenum != NON_PM)
1371 return roles[i].malenum;
1375 /* this might be from a 3.2.x score for former Elf class */
1376 if (!strcmp(plch, "E"))
1379 impossible("What weird role is this? (%s)", plch);
1380 return PM_HUMAN_MUMMY;
1384 * Get a random player name and class from the high score list,
1386 struct toptenentry *
1387 get_rnd_toptenentry()
1391 register struct toptenentry *tt;
1392 static struct toptenentry tt_buf;
1394 rfile = fopen_datafile(RECORD, "r", SCOREPREFIX);
1396 impossible("Cannot open record file!");
1401 rank = rnd(sysopt.tt_oname_maxrank);
1403 for (i = rank; i; i--) {
1404 readentry(rfile, tt);
1405 if (tt->points == 0)
1409 if (tt->points == 0) {
1418 (void) fclose(rfile);
1424 * Attach random player name and class from high score list
1425 * to an object (for statues or morgue corpses).
1431 struct toptenentry *tt;
1433 return (struct obj *) 0;
1435 tt = get_rnd_toptenentry();
1438 return (struct obj *) 0;
1440 set_corpsenm(otmp, classmon(tt->plrole, (tt->plgend[0] == 'F')));
1441 otmp = oname(otmp, tt->name);
1446 #ifdef NO_SCAN_BRACK
1447 /* Lattice scanf isn't up to reading the scorefile. What */
1448 /* follows deals with that; I admit it's ugly. (KL) */
1449 /* Now generally available (KL) */
1454 while ((p = index(p, ' ')) != 0)
1462 while ((p = index(p, '|')) != 0)
1465 #endif /* NO_SCAN_BRACK */