1 /* NetHack 3.6 topten.c $NHDT-Date: 1448117546 2015/11/21 14:52:26 $ $NHDT-Branch: master $:$NHDT-Revision: 1.40 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
5 /* JNetHack Copyright */
6 /* (c) Issei Numata, Naoki Hamada, Shigehiro Miyashita, 1994-2000 */
7 /* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2016 */
8 /* JNetHack may be freely redistributed. See license for details. */
12 #ifdef SHORT_FILENAMES
15 #include "patchlevel.h"
19 /* We don't want to rewrite the whole file, because that entails
20 creating a new version which requires that the old one be deletable. */
21 #define UPDATE_RECORD_IN_PLACE
25 * Updating in place can leave junk at the end of the file in some
26 * circumstances (if it shrinks and the O.S. doesn't have a straightforward
27 * way to truncate it). The trailing junk is harmless and the code
28 * which reads the scores will ignore it.
30 #ifdef UPDATE_RECORD_IN_PLACE
31 static long final_fpos;
34 #define done_stopprint program_state.stopprint
36 #define newttentry() (struct toptenentry *) alloc(sizeof (struct toptenentry))
37 #define dealloc_ttentry(ttent) free((genericptr_t) (ttent))
43 struct toptenentry *tt_next;
44 #ifdef UPDATE_RECORD_IN_PLACE
48 int deathdnum, deathlev;
49 int maxlvl, hp, maxhp, deaths;
50 int ver_major, ver_minor, patchlevel;
51 long deathdate, birthdate;
53 char plrole[ROLESZ + 1];
54 char plrace[ROLESZ + 1];
55 char plgend[ROLESZ + 1];
56 char plalign[ROLESZ + 1];
58 char death[DTHSZ + 1];
60 /* size big enough to read in all the string fields at once; includes
61 room for separating space or trailing newline plus string terminator */
62 #define SCANBUFSZ (4 * (ROLESZ + 1) + (NAMSZ + 1) + (DTHSZ + 1) + 1)
64 STATIC_DCL void FDECL(topten_print, (const char *));
65 STATIC_DCL void FDECL(topten_print_bold, (const char *));
66 STATIC_DCL xchar FDECL(observable_depth, (d_level *));
67 STATIC_DCL void NDECL(outheader);
68 STATIC_DCL void FDECL(outentry, (int, struct toptenentry *, BOOLEAN_P));
69 STATIC_DCL void FDECL(discardexcess, (FILE *));
70 STATIC_DCL void FDECL(readentry, (FILE *, struct toptenentry *));
71 STATIC_DCL void FDECL(writeentry, (FILE *, struct toptenentry *));
72 STATIC_DCL void FDECL(writexlentry, (FILE *, struct toptenentry *));
73 STATIC_DCL long NDECL(encodexlogflags);
74 STATIC_DCL long NDECL(encodeconduct);
75 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)
94 static NEARDATA const char *const killed_by_prefix[] = {
95 /* DIED, CHOKING, POISONING, STARVING, */
97 "killed by ", "choked on ", "poisoned by ", "died of ",
99 "
\8e\80\82ñ
\82¾", "
\82Å
\92\82\91§
\82µ
\82½", "
\82Ì
\93Å
\82Å
\8e\80\82ñ
\82¾", "",
100 /* DROWNING, BURNING, DISSOLVED, CRUSHING, */
102 "drowned in ", "burned by ", "dissolved in ", "crushed to death by ",
104 "
\93M
\8e\80\82µ
\82½","
\8fÄ
\8e\80\82µ
\82½", "
\97n
\8aâ
\82É
\97n
\82¯
\82½", "
\89\9f\82µ
\92×
\82³
\82ê
\82½",
105 /* STONING, TURNED_SLIME, GENOCIDED, */
107 "petrified by ", "turned to slime by ", "killed by ",
109 "
\90Î
\82É
\82È
\82Á
\82½", "
\82É
\83X
\83\89\83C
\83\80\82É
\82³
\82ê
\82½", "
\8bs
\8eE
\82³
\82ê
\82½",
110 /* PANICKED, TRICKED, QUIT, ESCAPED, ASCENDED */
116 char *kname = killer.name;
118 buf[0] = '\0'; /* so strncat() can find the end */
119 #if 1 /*JP*//*
\90æ
\82É
\91Î
\8fÛ
\82ð
\83R
\83s
\81[*/
120 strncat(buf, kname, siz - 1);
123 switch (killer.format) {
125 impossible("bad killer format? (%d)", killer.format);
127 case NO_KILLER_PREFIX:
130 #if 0 /*JP*//*
\93ú
\96{
\8cê
\82Å
\82Í
\95s
\97v*/
136 (void) strncat(buf, killed_by_prefix[how], siz - 1);
139 #else /*JP:
\8aù
\82É
\91Î
\8fÛ
\82ð
\83R
\83s
\81[
\82µ
\82Ä
\82¢
\82é
\82Ì
\82Å
\92P
\82É
\92Ç
\89Á*/
140 (void) strncat(buf, killed_by_prefix[how], siz - 1);
145 (void) strncat(buf, "
\82É
\8eE
\82³
\82ê
\82½", siz - 1);
148 #if 0 /*JP*//*
\8aù
\82É
\83R
\83s
\81[
\8dÏ
\82Ý*/
149 /* we're writing into buf[0] (after possibly advancing buf) rather than
150 appending, but strncat() appends a terminator and strncpy() doesn't */
151 (void) strncat(buf, kname, siz - 1);
159 if (toptenwin == WIN_ERR)
162 putstr(toptenwin, ATR_NONE, x);
169 if (toptenwin == WIN_ERR)
172 putstr(toptenwin, ATR_BOLD, x);
176 observable_depth(lev)
180 /* if we ever randomize the order of the elemental planes, we
181 must use a constant external representation in the record file */
182 if (In_endgame(lev)) {
183 if (Is_astralevel(lev))
185 else if (Is_waterlevel(lev))
187 else if (Is_firelevel(lev))
189 else if (Is_airlevel(lev))
191 else if (Is_earthlevel(lev))
200 /* throw away characters until current record has been entirely consumed */
209 } while (c != '\n' && c != EOF);
215 struct toptenentry *tt;
217 char inbuf[SCANBUFSZ], s1[SCANBUFSZ], s2[SCANBUFSZ], s3[SCANBUFSZ],
218 s4[SCANBUFSZ], s5[SCANBUFSZ], s6[SCANBUFSZ];
220 #ifdef NO_SCAN_BRACK /* Version_ Pts DgnLevs_ Hp___ Died__Born id */
221 static const char fmt[] = "%d %d %d %ld %d %d %d %d %d %d %ld %ld %d%*c";
222 static const char fmt32[] = "%c%c %s %s%*c";
223 static const char fmt33[] = "%s %s %s %s %s %s%*c";
225 static const char fmt[] = "%d.%d.%d %ld %d %d %d %d %d %d %ld %ld %d ";
226 static const char fmt32[] = "%c%c %[^,],%[^\n]%*c";
227 static const char fmt33[] = "%s %s %s %s %[^,],%[^\n]%*c";
230 #ifdef UPDATE_RECORD_IN_PLACE
231 /* note: input below must read the record's terminating newline */
232 final_fpos = tt->fpos = ftell(rfile);
235 if (fscanf(rfile, fmt, &tt->ver_major, &tt->ver_minor, &tt->patchlevel,
236 &tt->points, &tt->deathdnum, &tt->deathlev, &tt->maxlvl,
237 &tt->hp, &tt->maxhp, &tt->deaths, &tt->deathdate,
238 &tt->birthdate, &tt->uid) != TTFIELDS) {
241 discardexcess(rfile);
243 /* load remainder of record into a local buffer;
244 this imposes an implicit length limit of SCANBUFSZ
245 on every string field extracted from the buffer */
246 if (!fgets(inbuf, sizeof inbuf, rfile)) {
247 /* sscanf will fail and tt->points will be set to 0 */
249 } else if (!index(inbuf, '\n')) {
250 Strcpy(&inbuf[sizeof inbuf - 2], "\n");
251 discardexcess(rfile);
253 /* Check for backwards compatibility */
254 if (tt->ver_major < 3 || (tt->ver_major == 3 && tt->ver_minor < 3)) {
257 if (sscanf(inbuf, fmt32, tt->plrole, tt->plgend, s1, s2) == 4) {
258 tt->plrole[1] = tt->plgend[1] = '\0'; /* read via %c */
259 copynchars(tt->name, s1, (int) (sizeof tt->name) - 1);
260 copynchars(tt->death, s2, (int) (sizeof tt->death) - 1);
263 tt->plrole[1] = '\0';
264 if ((i = str2role(tt->plrole)) >= 0)
265 Strcpy(tt->plrole, roles[i].filecode);
266 Strcpy(tt->plrace, "?");
267 Strcpy(tt->plgend, (tt->plgend[0] == 'M') ? "Mal" : "Fem");
268 Strcpy(tt->plalign, "?");
269 } else if (sscanf(inbuf, fmt33, s1, s2, s3, s4, s5, s6) == 6) {
270 copynchars(tt->plrole, s1, (int) (sizeof tt->plrole) - 1);
271 copynchars(tt->plrace, s2, (int) (sizeof tt->plrace) - 1);
272 copynchars(tt->plgend, s3, (int) (sizeof tt->plgend) - 1);
273 copynchars(tt->plalign, s4, (int) (sizeof tt->plalign) - 1);
274 copynchars(tt->name, s5, (int) (sizeof tt->name) - 1);
275 copynchars(tt->death, s6, (int) (sizeof tt->death) - 1);
279 if (tt->points > 0) {
280 nsb_unmung_line(tt->name);
281 nsb_unmung_line(tt->death);
286 /* check old score entries for Y2K problem and fix whenever found */
287 if (tt->points > 0) {
288 if (tt->birthdate < 19000000L)
289 tt->birthdate += 19000000L;
290 if (tt->deathdate < 19000000L)
291 tt->deathdate += 19000000L;
296 writeentry(rfile, tt)
298 struct toptenentry *tt;
300 static const char fmt32[] = "%c%c "; /* role,gender */
301 static const char fmt33[] = "%s %s %s %s "; /* role,race,gndr,algn */
302 #ifndef NO_SCAN_BRACK
303 static const char fmt0[] = "%d.%d.%d %ld %d %d %d %d %d %d %ld %ld %d ";
304 static const char fmtX[] = "%s,%s%s%s\n";
305 #else /* 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%s%s\n";
309 nsb_mung_line(tt->name);
310 nsb_mung_line(tt->death);
313 (void) fprintf(rfile, fmt0, tt->ver_major, tt->ver_minor, tt->patchlevel,
314 tt->points, tt->deathdnum, tt->deathlev, tt->maxlvl,
315 tt->hp, tt->maxhp, tt->deaths, tt->deathdate,
316 tt->birthdate, tt->uid);
317 if (tt->ver_major < 3 || (tt->ver_major == 3 && tt->ver_minor < 3))
318 (void) fprintf(rfile, fmt32, tt->plrole[0], tt->plgend[0]);
320 (void) fprintf(rfile, fmt33, tt->plrole, tt->plrace, tt->plgend,
323 (void) fprintf(rfile, fmtX, onlyspace(tt->name) ? "_" : tt->name,
325 (multi ? ", while " : ""),
326 (multi ? (multi_reason ? multi_reason : "helpless") : ""));
328 (void) fprintf(rfile, fmtX, onlyspace(tt->name) ? "_" : tt->name,
329 (multi ? (multi_reason ? multi_reason : "
\96³
\97Í
\82È
\8aÔ
\82É") : ""),
335 nsb_unmung_line(tt->name);
336 nsb_unmung_line(tt->death);
340 /* as tab is never used in eg. plname or death, no need to mangle those. */
342 writexlentry(rfile, tt)
344 struct toptenentry *tt;
346 #define Fprintf (void) fprintf
347 #define XLOG_SEP '\t' /* xlogfile field separator. */
350 Sprintf(buf, "version=%d.%d.%d", tt->ver_major, tt->ver_minor,
352 Sprintf(eos(buf), "%cpoints=%ld%cdeathdnum=%d%cdeathlev=%d", XLOG_SEP,
353 tt->points, XLOG_SEP, tt->deathdnum, XLOG_SEP, tt->deathlev);
354 Sprintf(eos(buf), "%cmaxlvl=%d%chp=%d%cmaxhp=%d", XLOG_SEP, tt->maxlvl,
355 XLOG_SEP, tt->hp, XLOG_SEP, tt->maxhp);
356 Sprintf(eos(buf), "%cdeaths=%d%cdeathdate=%ld%cbirthdate=%ld%cuid=%d",
357 XLOG_SEP, tt->deaths, XLOG_SEP, tt->deathdate, XLOG_SEP,
358 tt->birthdate, XLOG_SEP, tt->uid);
359 Fprintf(rfile, "%s", buf);
360 Sprintf(buf, "%crole=%s%crace=%s%cgender=%s%calign=%s", XLOG_SEP,
361 tt->plrole, XLOG_SEP, tt->plrace, XLOG_SEP, tt->plgend, XLOG_SEP,
363 Fprintf(rfile, "%s%cname=%s%cdeath=%s",
364 buf, /* (already includes separator) */
365 XLOG_SEP, plname, XLOG_SEP, tt->death);
368 Fprintf(rfile, "%cwhile=%s", XLOG_SEP,
369 multi_reason ? multi_reason : "helpless");
371 Fprintf(rfile, "%cwhile=%s", XLOG_SEP,
372 multi_reason ? multi_reason : "
\96³
\97Í
\82È
\8aÔ
\82É");
374 Fprintf(rfile, "%cconduct=0x%lx%cturns=%ld%cachieve=0x%lx", XLOG_SEP,
375 encodeconduct(), XLOG_SEP, moves, XLOG_SEP, encodeachieve());
376 Fprintf(rfile, "%crealtime=%ld%cstarttime=%ld%cendtime=%ld", XLOG_SEP,
378 (long) urealtime.realtime, XLOG_SEP, (long) ubirthday, XLOG_SEP,
379 (long) urealtime.endtime);
381 (long) urealtime.realtime, XLOG_SEP,
382 (long) ubirthday, XLOG_SEP, (long) urealtime.finish_time);
384 Fprintf(rfile, "%cgender0=%s%calign0=%s", XLOG_SEP,
385 genders[flags.initgend].filecode, XLOG_SEP,
386 aligns[1 - u.ualignbase[A_ORIGINAL]].filecode);
387 Fprintf(rfile, "%cflags=0x%lx", XLOG_SEP, encodexlogflags());
388 Fprintf(rfile, "\n");
401 if (!u.uroleplay.numbones)
412 if (!u.uconduct.food)
414 if (!u.uconduct.unvegan)
416 if (!u.uconduct.unvegetarian)
418 if (!u.uconduct.gnostic)
420 if (!u.uconduct.weaphit)
422 if (!u.uconduct.killer)
424 if (!u.uconduct.literate)
426 if (!u.uconduct.polypiles)
428 if (!u.uconduct.polyselfs)
430 if (!u.uconduct.wishes)
432 if (!u.uconduct.wisharti)
434 if (!num_genocides())
447 if (u.uachieve.enter_gehennom)
449 if (u.uachieve.menorah)
453 if (u.uevent.invoked)
455 if (u.uachieve.amulet)
457 if (In_endgame(&u.uz))
459 if (Is_astralevel(&u.uz))
461 if (u.uachieve.ascended)
463 if (u.uachieve.mines_luckstone)
465 if (u.uachieve.finish_sokoban)
467 if (u.uachieve.killed_medusa)
469 if (u.uroleplay.blind)
471 if (u.uroleplay.nudist)
479 struct toptenentry *tt;
481 struct toptenentry *ttnext;
483 while (tt->points > 0) {
484 ttnext = tt->tt_next;
497 int rank, rank0 = -1, rank1 = 0;
498 int occ_cnt = sysopt.persmax;
499 register struct toptenentry *t0, *tprev;
500 struct toptenentry *t1;
502 register int flg = 0;
509 #endif /* XLOGFILE */
512 /* Under DICE 3.0, this crashes the system consistently, apparently due to
513 * corruption of *rfile somewhere. Until I figure this out, just cut out
514 * topten support entirely - at least then the game exits cleanly. --AC
519 /* If we are in the midst of a panic, cut out topten entirely.
520 * topten uses alloc() several times, which will lead to
521 * problems if the panic was the result of an alloc() failure.
523 if (program_state.panicking)
526 if (iflags.toptenwin) {
527 toptenwin = create_nhwindow(NHW_TEXT);
530 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
531 #define HUP if (!program_state.done_hup)
537 restore_colors(); /* make sure the screen is black on white */
539 /* create a new 'topten' entry */
542 t0->ver_major = VERSION_MAJOR;
543 t0->ver_minor = VERSION_MINOR;
544 t0->patchlevel = PATCHLEVEL;
545 t0->points = u.urexp;
546 t0->deathdnum = u.uz.dnum;
547 /* deepest_lev_reached() is in terms of depth(), and reporting the
548 * deepest level reached in the dungeon death occurred in doesn't
549 * seem right, so we have to report the death level in depth() terms
550 * as well (which also seems reasonable since that's all the player
551 * sees on the screen anyway)
553 t0->deathlev = observable_depth(&u.uz);
554 t0->maxlvl = deepest_lev_reached(TRUE);
556 t0->maxhp = u.uhpmax;
557 t0->deaths = u.umortality;
559 copynchars(t0->plrole, urole.filecode, ROLESZ);
560 copynchars(t0->plrace, urace.filecode, ROLESZ);
561 copynchars(t0->plgend, genders[flags.female].filecode, ROLESZ);
562 copynchars(t0->plalign, aligns[1 - u.ualign.type].filecode, ROLESZ);
563 copynchars(t0->name, plname, NAMSZ);
564 formatkiller(t0->death, sizeof t0->death, how);
565 t0->birthdate = yyyymmdd(ubirthday);
566 t0->deathdate = yyyymmdd(when);
569 urealtime.endtime = when;
571 #ifdef UPDATE_RECORD_IN_PLACE
575 #ifdef LOGFILE /* used for debugging (who dies of what, where) */
576 if (lock_file(LOGFILE, SCOREPREFIX, 10)) {
577 if (!(lfile = fopen_datafile(LOGFILE, "a", SCOREPREFIX))) {
578 HUP raw_print("Cannot open log file!");
580 writeentry(lfile, t0);
581 (void) fclose(lfile);
583 unlock_file(LOGFILE);
587 if (lock_file(XLOGFILE, SCOREPREFIX, 10)) {
588 if (!(xlfile = fopen_datafile(XLOGFILE, "a", SCOREPREFIX))) {
589 HUP raw_print("Cannot open extended log file!");
591 writexlentry(xlfile, t0);
592 (void) fclose(xlfile);
594 unlock_file(XLOGFILE);
596 #endif /* XLOGFILE */
598 if (wizard || discover) {
605 "Since you were in %s mode, the score list will not be checked.",
606 wizard ? "wizard" : "discover");
608 "%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",
609 wizard ? "
\83E
\83B
\83U
\81[
\83h" : "
\94
\8c©");
616 if (!lock_file(RECORD, SCOREPREFIX, 60))
619 #ifdef UPDATE_RECORD_IN_PLACE
620 rfile = fopen_datafile(RECORD, "r+", SCOREPREFIX);
622 rfile = fopen_datafile(RECORD, "r", SCOREPREFIX);
626 HUP raw_print("Cannot open record file!");
631 HUP topten_print("");
633 /* assure minimum number of points */
634 if (t0->points < sysopt.pointsmin)
637 t1 = tt_head = newttentry();
639 /* rank0: -1 undefined, 0 not_on_list, n n_th on list */
641 readentry(rfile, t1);
642 if (t1->points < sysopt.pointsmin)
644 if (rank0 < 0 && t1->points < t0->points) {
651 #ifdef UPDATE_RECORD_IN_PLACE
652 t0->fpos = t1->fpos; /* insert here */
656 flg++; /* ask for a rewrite */
662 if ((sysopt.pers_is_uid ? t1->uid == t0->uid
663 : strncmp(t1->name, t0->name, NAMSZ) == 0)
664 && !strncmp(t1->plrole, t0->plrole, ROLESZ) && --occ_cnt <= 0) {
673 "You didn't beat your previous score of %ld points.",
675 "
\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",
686 if (rank <= sysopt.entrymax) {
687 t1->tt_next = newttentry();
691 if (rank > sysopt.entrymax) {
696 if (flg) { /* rewrite record file */
697 #ifdef UPDATE_RECORD_IN_PLACE
698 (void) fseek(rfile, (t0->fpos >= 0 ? t0->fpos : final_fpos),
701 (void) fclose(rfile);
702 if (!(rfile = fopen_datafile(RECORD, "w", SCOREPREFIX))) {
703 HUP raw_print("Cannot write record file");
705 free_ttlist(tt_head);
708 #endif /* UPDATE_RECORD_IN_PLACE */
713 topten_print("You made the top ten list!");
715 topten_print("
\82 \82È
\82½
\82Í
\83g
\83b
\83v10
\83\8a\83X
\83g
\82É
\8dÚ
\82Á
\82½
\81I");
721 "You reached the %d%s place on the top %d list.",
722 rank0, ordin(rank0), sysopt.entrymax);
725 "
\82 \82È
\82½
\82Í
\81C
\83g
\83b
\83v%d
\83\8a\83X
\83g
\82Ì%d
\88Ê
\82É
\8dÚ
\82Á
\82½
\81D",
726 sysopt.entrymax, rank0);
740 for (rank = 1; t1->points != 0; rank++, t1 = t1->tt_next) {
742 #ifdef UPDATE_RECORD_IN_PLACE
746 writeentry(rfile, t1);
749 if (rank > flags.end_top && (rank < rank0 - flags.end_around
750 || rank > rank0 + flags.end_around)
752 || (sysopt.pers_is_uid
754 : strncmp(t1->name, t0->name, NAMSZ) == 0)))
756 if (rank == rank0 - flags.end_around
757 && rank0 > flags.end_top + flags.end_around + 1 && !flags.end_own)
760 outentry(rank, t1, FALSE);
762 outentry(rank, t1, TRUE);
764 outentry(rank, t1, TRUE);
765 outentry(0, t0, TRUE);
770 outentry(0, t0, TRUE);
771 #ifdef UPDATE_RECORD_IN_PLACE
774 /* if a reasonable way to truncate a file exists, use it */
775 truncate_file(rfile);
777 /* use sentinel record rather than relying on truncation */
778 t1->points = 0L; /* terminates file when read back in */
779 t1->ver_major = t1->ver_minor = t1->patchlevel = 0;
780 t1->uid = t1->deathdnum = t1->deathlev = 0;
781 t1->maxlvl = t1->hp = t1->maxhp = t1->deaths = 0;
782 t1->plrole[0] = t1->plrace[0] = t1->plgend[0] = t1->plalign[0] = '-';
783 t1->plrole[1] = t1->plrace[1] = t1->plgend[1] = t1->plalign[1] = 0;
784 t1->birthdate = t1->deathdate = yyyymmdd((time_t) 0L);
785 Strcpy(t1->name, "@");
786 Strcpy(t1->death, "<eod>\n");
787 writeentry(rfile, t1);
788 (void) fflush(rfile);
789 #endif /* TRUNCATE_FILE */
791 #endif /* UPDATE_RECORD_IN_PLACE */
792 (void) fclose(rfile);
794 free_ttlist(tt_head);
797 if (iflags.toptenwin && !done_stopprint)
798 display_nhwindow(toptenwin, 1);
802 if (iflags.toptenwin) {
803 destroy_nhwindow(toptenwin);
814 Strcpy(linebuf, " No Points Name");
816 while (bp < linebuf + COLNO - 9)
818 Strcpy(bp, "Hp [max]");
819 topten_print(linebuf);
822 /* so>0: standout line; so=0: ordinary line */
824 outentry(rank, t1, so)
825 struct toptenentry *t1;
829 boolean second_line = TRUE;
832 char *bp, hpbuf[24], linebuf3[BUFSZ];
853 Sprintf(eos(linebuf), "%3d", rank);
855 Strcat(linebuf, " ");
857 Sprintf(eos(linebuf), " %10ld %.10s", t1->points ? t1->points : u.urexp,
859 Sprintf(eos(linebuf), "-%s", t1->plrole);
860 if (t1->plrace[0] != '?')
861 Sprintf(eos(linebuf), "-%s", t1->plrace);
862 /* Printing of gender and alignment is intentional. It has been
863 * part of the NetHack Geek Code, and illustrates a proper way to
864 * specify a character from the command line.
866 Sprintf(eos(linebuf), "-%s", t1->plgend);
867 if (t1->plalign[0] != '?')
869 Sprintf(eos(linebuf), "-%s ", t1->plalign);
871 Sprintf(eos(linebuf), "-%s", t1->plalign);
874 Strcat(linebuf, " ");
878 Strcat(linebuf, "
\82Í");
879 /*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 */
881 if (!strncmp(jdeath, "
\96\82\8f\9c\82¯
\82ð
\8eè
\82É", 12))
883 else if (!strncmp(jdeath, "
\93V
\8fã
\82Å
\92p
\90J
\82ð
\8eó
\82¯", 16))
885 else if (!strncmp(jdeath, "
\8bU
\95¨
\82Ì
\96\82\8f\9c\82¯
\82ð
\92Í
\82Ü
\82³
\82ê", 24))
889 if (!strncmp("escaped", t1->death, 7)) {
891 if (!strncmp("
\92E
\8fo
\82µ
\82½", jdeath, 8)
892 || !strncmp("escaped", jdeath, 7)) {
895 Sprintf(eos(linebuf), "escaped the dungeon %s[max level %d]",
896 !strncmp(" (", t1->death + 7, 2) ? t1->death + 7 + 2 : "",
898 /* fixup for closing paren in "escaped... with...Amulet)[max..." */
899 if ((bp = index(linebuf, ')')) != 0)
900 *bp = (t1->deathdnum == astral_level.dnum) ? '\0' : ' ';
903 strncpy(jbuf, t1->death, jdeath - t1->death);
904 jbuf[jdeath - t1->death] = '\0';
905 Sprintf(action, "%s
\96À
\8b{
\82©
\82ç
\92E
\8fo
\82µ
\82½[
\8dÅ
\91å
\92n
\89º%d
\8aK]",
910 } else if (!strncmp("ascended", t1->death, 8)) {
912 } else if (!strncmp("
\8f¸
\93V
\82µ
\82½", jdeath, 8)
913 || !strncmp("ascended", jdeath, 8)) {
916 Sprintf(eos(linebuf), "ascended to demigod%s-hood",
917 (t1->plgend[0] == 'F') ? "dess" : "");
919 Sprintf(action, "
\8f¸
\93V
\82µ%s
\90_
\82Æ
\82È
\82Á
\82½",
920 (t1->plgend[0] == 'F') ? "
\8f\97" : "");
925 if (!strncmp(t1->death, "quit", 4)) {
927 if (!strncmp(jdeath, "
\94²
\82¯
\82½", 4)) {
929 Strcat(linebuf, "quit");
931 Strcat(action, t1->death);
937 } else if (!strncmp(t1->death, "died of st", 10)) {
938 Strcat(linebuf, "starved to death");
940 } else if (!strncmp(t1->death, "choked", 6)) {
941 Sprintf(eos(linebuf), "choked on h%s food",
942 (t1->plgend[0] == 'F') ? "er" : "is");
943 } else if (!strncmp(t1->death, "poisoned", 8)) {
944 Strcat(linebuf, "was poisoned");
945 } else if (!strncmp(t1->death, "crushed", 7)) {
946 Strcat(linebuf, "was crushed to death");
947 } else if (!strncmp(t1->death, "petrified by ", 13)) {
948 Strcat(linebuf, "turned to stone");
950 Strcat(linebuf, "died");
953 if (t1->deathdnum == astral_level.dnum) {
954 const char *arg, *fmt = " on the Plane of %s";
956 switch (t1->deathlev) {
959 fmt = " on the %s Plane";
964 arg = "
\93V
\8fã
\8aE";
970 arg = "
\90\85\82Ì
\90¸
\97ì
\8aE";
976 arg = "
\89Î
\82Ì
\90¸
\97ì
\8aE";
982 arg = "
\95\97\82Ì
\90¸
\97ì
\8aE";
988 arg = "
\92n
\82Ì
\90¸
\97ì
\8aE";
995 Sprintf(eos(linebuf), fmt, arg);
997 Sprintf(where, "%s
\82É
\82Ä", arg);
1001 Sprintf(eos(linebuf), " in %s", dungeons[t1->deathdnum].dname);
1003 Sprintf(eos(linebuf), "%s", dungeons[t1->deathdnum].dname);
1004 if (t1->deathdnum != knox_level.dnum)
1006 Sprintf(eos(linebuf), " on level %d", t1->deathlev);
1008 Sprintf(eos(linebuf), "
\82Ì
\92n
\89º%d
\8aK
\82É
\82Ä", t1->deathlev);
1009 if (t1->deathlev != t1->maxlvl)
1011 Sprintf(eos(linebuf), " [max %d]", t1->maxlvl);
1013 Sprintf(eos(where), "[
\8dÅ
\91å
\92n
\89º%d
\8aK]", t1->maxlvl);
1016 /* kludge for "quit while already on Charon's boat" */
1017 if (!strncmp(t1->death, "quit ", 5))
1018 Strcat(linebuf, t1->death + 4);
1021 Strcat(linebuf, ".");
1024 /* Quit, starved, ascended, and escaped contain no second line */
1027 Sprintf(eos(linebuf), " %c%s.", highc(*(t1->death)), t1->death + 1);
1029 Sprintf(action, "%s", t1->death);
1032 Sprintf(eos(linebuf), "%s%s%s
\81D", who, where, action);
1034 lngr = (int) strlen(linebuf);
1036 hpbuf[0] = '-', hpbuf[1] = '\0';
1038 Sprintf(hpbuf, "%d", t1->hp);
1039 /* beginning of hp column after padding (not actually padded yet) */
1040 hppos = COLNO - (sizeof(" Hp [max]") - 1); /* sizeof(str) includes \0 */
1042 while(lngr >= hppos ){
1043 /*JP hppos
\82æ
\82è
\91O
\82Ì
\93K
\93\96\82È
\88Ê
\92u
\82Å
\95ª
\8a\84\82·
\82é
\81D*/
1046 split_japanese(linebuf, car, cdr, hppos);
1050 while (bp < car + (COLNO-1)) *bp++ = ' ';
1052 topten_print_bold(car);
1056 Sprintf(linebuf, "%15s %s", "", cdr);
1057 lngr = (int)strlen(linebuf);
1059 /*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*/
1061 while (lngr >= hppos) {
1062 for (bp = eos(linebuf); !(*bp == ' ' && (bp - linebuf < hppos)); bp--)
1064 /* special case: word is too long, wrap in the middle */
1065 if (linebuf + 15 >= bp)
1066 bp = linebuf + hppos - 1;
1067 /* special case: if about to wrap in the middle of maximum
1068 dungeon depth reached, wrap in front of it instead */
1069 if (bp > linebuf + 5 && !strncmp(bp - 5, " [max", 5))
1072 Strcpy(linebuf3, bp);
1074 Strcpy(linebuf3, bp + 1);
1077 while (bp < linebuf + (COLNO - 1))
1080 topten_print_bold(linebuf);
1082 topten_print(linebuf);
1083 Sprintf(linebuf, "%15s %s", "", linebuf3);
1084 lngr = strlen(linebuf);
1087 /* beginning of hp column not including padding */
1088 hppos = COLNO - 7 - (int) strlen(hpbuf);
1091 if (bp <= linebuf + hppos) {
1092 /* pad any necessary blanks to the hit point entry */
1093 while (bp < linebuf + hppos)
1096 Sprintf(eos(bp), " %s[%d]",
1097 (t1->maxhp < 10) ? " " : (t1->maxhp < 100) ? " " : "",
1105 while (bp < linebuf + so)
1108 topten_print_bold(linebuf);
1110 topten_print(linebuf);
1114 score_wanted(current_ver, rank, t1, playerct, players, uid)
1115 boolean current_ver;
1117 struct toptenentry *t1;
1119 const char **players;
1125 && (t1->ver_major != VERSION_MAJOR || t1->ver_minor != VERSION_MINOR
1126 || t1->patchlevel != PATCHLEVEL))
1129 if (sysopt.pers_is_uid && !playerct && t1->uid == uid)
1132 for (i = 0; i < playerct; i++) {
1133 if (players[i][0] == '-' && index("pr", players[i][1])
1134 && players[i][2] == 0 && i + 1 < playerct) {
1135 const char *arg = players[i + 1];
1136 if ((players[i][1] == 'p'
1137 && str2role(arg) == str2role(t1->plrole))
1138 || (players[i][1] == 'r'
1139 && str2race(arg) == str2race(t1->plrace)))
1142 } else if (strcmp(players[i], "all") == 0
1143 || strncmp(t1->name, players[i], NAMSZ) == 0
1144 || (players[i][0] == '-' && players[i][1] == t1->plrole[0]
1145 && players[i][2] == 0)
1146 || (digit(players[i][0]) && rank <= atoi(players[i])))
1153 * print selected parts of score list.
1154 * argc >= 2, with argv[0] untrustworthy (directory names, et al.),
1155 * and argv[1] starting with "-s".
1162 const char **players;
1164 boolean current_ver = TRUE, init_done = FALSE;
1165 register struct toptenentry *t1;
1167 boolean match_found = FALSE;
1171 const char *player0;
1173 if (argc < 2 || strncmp(argv[1], "-s", 2)) {
1174 raw_printf("prscore: bad arguments (%d)", argc);
1178 rfile = fopen_datafile(RECORD, "r", SCOREPREFIX);
1180 raw_print("Cannot open record file!");
1186 extern winid amii_rawprwin;
1188 init_nhwindows(&argc, argv);
1189 amii_rawprwin = create_nhwindow(NHW_TEXT);
1193 /* If the score list isn't after a game, we never went through
1194 * initialization. */
1195 if (wiz1_level.dlevel == 0) {
1201 if (!argv[1][2]) { /* plain "-s" */
1207 if (argc > 1 && !strcmp(argv[1], "-v")) {
1208 current_ver = FALSE;
1214 if (sysopt.pers_is_uid) {
1217 players = (const char **) 0;
1222 player0 = "all"; /* single user system */
1224 player0 = "hackplayer";
1231 players = (const char **) ++argv;
1235 t1 = tt_head = newttentry();
1236 for (rank = 1;; rank++) {
1237 readentry(rfile, t1);
1238 if (t1->points == 0)
1241 && score_wanted(current_ver, rank, t1, playerct, players, uid))
1243 t1->tt_next = newttentry();
1247 (void) fclose(rfile);
1256 for (rank = 1; t1->points != 0; rank++, t1 = t1->tt_next) {
1257 if (score_wanted(current_ver, rank, t1, playerct, players, uid))
1258 (void) outentry(rank, t1, FALSE);
1261 Sprintf(pbuf, "Cannot find any %sentries for ",
1262 current_ver ? "current " : "");
1264 Strcat(pbuf, "you.");
1267 Strcat(pbuf, "any of ");
1268 for (i = 0; i < playerct; i++) {
1269 /* stop printing players if there are too many to fit */
1270 if (strlen(pbuf) + strlen(players[i]) + 2 >= BUFSZ) {
1271 if (strlen(pbuf) < BUFSZ - 4)
1272 Strcat(pbuf, "...");
1274 Strcpy(pbuf + strlen(pbuf) - 4, "...");
1277 Strcat(pbuf, players[i]);
1278 if (i < playerct - 1) {
1279 if (players[i][0] == '-' && index("pr", players[i][1])
1280 && players[i][2] == 0)
1288 raw_printf("Usage: %s -s [-v] <playertypes> [maxrank] [playernames]",
1291 raw_printf("Player types are: [-p role] [-r race]");
1293 free_ttlist(tt_head);
1296 extern winid amii_rawprwin;
1298 display_nhwindow(amii_rawprwin, 1);
1299 destroy_nhwindow(amii_rawprwin);
1300 amii_rawprwin = WIN_ERR;
1312 /* Look for this role in the role table */
1313 for (i = 0; roles[i].name.m; i++)
1314 if (!strncmp(plch, roles[i].filecode, ROLESZ)) {
1315 if (fem && roles[i].femalenum != NON_PM)
1316 return roles[i].femalenum;
1317 else if (roles[i].malenum != NON_PM)
1318 return roles[i].malenum;
1322 /* this might be from a 3.2.x score for former Elf class */
1323 if (!strcmp(plch, "E"))
1326 impossible("What weird role is this? (%s)", plch);
1327 return PM_HUMAN_MUMMY;
1331 * Get a random player name and class from the high score list,
1332 * and attach them to an object (for statues or morgue corpses).
1340 register struct toptenentry *tt;
1342 struct toptenentry tt_buf;
1345 return (struct obj *) 0;
1347 rfile = fopen_datafile(RECORD, "r", SCOREPREFIX);
1349 impossible("Cannot open record file!");
1350 return (struct obj *) 0;
1354 rank = rnd(sysopt.tt_oname_maxrank);
1356 for (i = rank; i; i--) {
1357 readentry(rfile, tt);
1358 if (tt->points == 0)
1362 if (tt->points == 0) {
1368 otmp = (struct obj *) 0;
1370 set_corpsenm(otmp, classmon(tt->plrole, (tt->plgend[0] == 'F')));
1371 otmp = oname(otmp, tt->name);
1374 (void) fclose(rfile);
1378 #ifdef NO_SCAN_BRACK
1379 /* Lattice scanf isn't up to reading the scorefile. What */
1380 /* follows deals with that; I admit it's ugly. (KL) */
1381 /* Now generally available (KL) */
1386 while ((p = index(p, ' ')) != 0)
1394 while ((p = index(p, '|')) != 0)
1397 #endif /* NO_SCAN_BRACK */