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-2022 */
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*//*
\91O
\82É
\8e\9d\82Á
\82Ä
\82
\82é*/
128 if (incl_helpless && multi) {
130 Sprintf(buf, "%s
\81C", multi_reason);
132 Strcpy(buf, "
\8f\95\82¯
\82ð
\8eó
\82¯
\82ç
\82ê
\82È
\82¢
\8aÔ
\82É
\81C");
135 #if 1 /*JP*//*
\90æ
\82É
\91Î
\8fÛ
\82ð
\83R
\83s
\81[*/
136 strncat(buf, kname, siz - 1);
139 switch (killer.format) {
141 impossible("bad killer format? (%d)", killer.format);
143 case NO_KILLER_PREFIX:
146 #if 0 /*JP*//*
\93ú
\96{
\8cê
\82Å
\82Í
\95s
\97v*/
152 (void) strncat(buf, killed_by_prefix[how], siz - 1);
155 #else /*JP:
\8aù
\82É
\91Î
\8fÛ
\82ð
\83R
\83s
\81[
\82µ
\82Ä
\82¢
\82é
\82Ì
\82Å
\92P
\82É
\92Ç
\89Á*/
156 (void) strncat(buf, killed_by_prefix[how], siz - 1);
161 (void) strncat(buf, "
\82É
\8eE
\82³
\82ê
\82½", siz - 1);
164 #if 0 /*JP*//*
\8aù
\82É
\83R
\83s
\81[
\8dÏ
\82Ý*//*JP:TODO:
\83T
\83j
\83^
\83C
\83Y
\82ª
\95K
\97v*/
165 /* Copy kname into buf[].
166 * Object names and named fruit have already been sanitized, but
167 * monsters can have "called 'arbitrary text'" attached to them,
168 * so make sure that that text can't confuse field splitting when
169 * record, logfile, or xlogfile is re-read at some later point.
177 /* 'xlogfile' doesn't really need protection for '=', but
178 fixrecord.awk for corrupted 3.6.0 'record' does (only
179 if using xlogfile rather than logfile to repair record) */
182 /* tab is not possible due to use of mungspaces() when naming;
183 it would disrupt xlogfile parsing if it were present */
191 #if 0 /*JP*//*
\91O
\82É
\8e\9d\82Á
\82Ä
\8ds
\82*/
192 if (incl_helpless && multi) {
193 /* X <= siz: 'sizeof "string"' includes 1 for '\0' terminator */
194 if (multi_reason && strlen(multi_reason) + sizeof ", while " <= siz)
195 Sprintf(buf, ", while %s", multi_reason);
196 /* either multi_reason wasn't specified or wouldn't fit */
197 else if (sizeof ", while helpless" <= siz)
198 Strcpy(buf, ", while helpless");
199 /* else extra death info won't fit, so leave it out */
208 if (toptenwin == WIN_ERR)
211 putstr(toptenwin, ATR_NONE, x);
218 if (toptenwin == WIN_ERR)
221 putstr(toptenwin, ATR_BOLD, x);
225 observable_depth(lev)
229 /* if we ever randomize the order of the elemental planes, we
230 must use a constant external representation in the record file */
231 if (In_endgame(lev)) {
232 if (Is_astralevel(lev))
234 else if (Is_waterlevel(lev))
236 else if (Is_firelevel(lev))
238 else if (Is_airlevel(lev))
240 else if (Is_earthlevel(lev))
249 /* throw away characters until current record has been entirely consumed */
258 } while (c != '\n' && c != EOF);
264 struct toptenentry *tt;
266 char inbuf[SCANBUFSZ], s1[SCANBUFSZ], s2[SCANBUFSZ], s3[SCANBUFSZ],
267 s4[SCANBUFSZ], s5[SCANBUFSZ], s6[SCANBUFSZ];
269 #ifdef NO_SCAN_BRACK /* Version_ Pts DgnLevs_ Hp___ Died__Born id */
270 static const char fmt[] = "%d %d %d %ld %d %d %d %d %d %d %ld %ld %d%*c";
271 static const char fmt32[] = "%c%c %s %s%*c";
272 static const char fmt33[] = "%s %s %s %s %s %s%*c";
274 static const char fmt[] = "%d.%d.%d %ld %d %d %d %d %d %d %ld %ld %d ";
275 static const char fmt32[] = "%c%c %[^,],%[^\n]%*c";
276 static const char fmt33[] = "%s %s %s %s %[^,],%[^\n]%*c";
279 #ifdef UPDATE_RECORD_IN_PLACE
280 /* note: input below must read the record's terminating newline */
281 final_fpos = tt->fpos = ftell(rfile);
284 if (fscanf(rfile, fmt, &tt->ver_major, &tt->ver_minor, &tt->patchlevel,
285 &tt->points, &tt->deathdnum, &tt->deathlev, &tt->maxlvl,
286 &tt->hp, &tt->maxhp, &tt->deaths, &tt->deathdate,
287 &tt->birthdate, &tt->uid) != TTFIELDS) {
290 discardexcess(rfile);
292 /* load remainder of record into a local buffer;
293 this imposes an implicit length limit of SCANBUFSZ
294 on every string field extracted from the buffer */
295 if (!fgets(inbuf, sizeof inbuf, rfile)) {
296 /* sscanf will fail and tt->points will be set to 0 */
298 } else if (!index(inbuf, '\n')) {
299 Strcpy(&inbuf[sizeof inbuf - 2], "\n");
300 discardexcess(rfile);
302 /* Check for backwards compatibility */
303 if (tt->ver_major < 3 || (tt->ver_major == 3 && tt->ver_minor < 3)) {
306 if (sscanf(inbuf, fmt32, tt->plrole, tt->plgend, s1, s2) == 4) {
307 tt->plrole[1] = tt->plgend[1] = '\0'; /* read via %c */
308 copynchars(tt->name, s1, (int) (sizeof tt->name) - 1);
309 copynchars(tt->death, s2, (int) (sizeof tt->death) - 1);
312 tt->plrole[1] = '\0';
313 if ((i = str2role(tt->plrole)) >= 0)
314 Strcpy(tt->plrole, roles[i].filecode);
315 Strcpy(tt->plrace, "?");
316 Strcpy(tt->plgend, (tt->plgend[0] == 'M') ? "Mal" : "Fem");
317 Strcpy(tt->plalign, "?");
318 } else if (sscanf(inbuf, fmt33, s1, s2, s3, s4, s5, s6) == 6) {
319 copynchars(tt->plrole, s1, (int) (sizeof tt->plrole) - 1);
320 copynchars(tt->plrace, s2, (int) (sizeof tt->plrace) - 1);
321 copynchars(tt->plgend, s3, (int) (sizeof tt->plgend) - 1);
322 copynchars(tt->plalign, s4, (int) (sizeof tt->plalign) - 1);
323 copynchars(tt->name, s5, (int) (sizeof tt->name) - 1);
324 copynchars(tt->death, s6, (int) (sizeof tt->death) - 1);
328 if (tt->points > 0) {
329 nsb_unmung_line(tt->name);
330 nsb_unmung_line(tt->death);
335 /* check old score entries for Y2K problem and fix whenever found */
336 if (tt->points > 0) {
337 if (tt->birthdate < 19000000L)
338 tt->birthdate += 19000000L;
339 if (tt->deathdate < 19000000L)
340 tt->deathdate += 19000000L;
345 writeentry(rfile, tt)
347 struct toptenentry *tt;
349 static const char fmt32[] = "%c%c "; /* role,gender */
350 static const char fmt33[] = "%s %s %s %s "; /* role,race,gndr,algn */
351 #ifndef NO_SCAN_BRACK
352 static const char fmt0[] = "%d.%d.%d %ld %d %d %d %d %d %d %ld %ld %d ";
353 static const char fmtX[] = "%s,%s\n";
354 #else /* NO_SCAN_BRACK */
355 static const char fmt0[] = "%d %d %d %ld %d %d %d %d %d %d %ld %ld %d ";
356 static const char fmtX[] = "%s %s\n";
358 nsb_mung_line(tt->name);
359 nsb_mung_line(tt->death);
362 (void) fprintf(rfile, fmt0, tt->ver_major, tt->ver_minor, tt->patchlevel,
363 tt->points, tt->deathdnum, tt->deathlev, tt->maxlvl,
364 tt->hp, tt->maxhp, tt->deaths, tt->deathdate,
365 tt->birthdate, tt->uid);
366 if (tt->ver_major < 3 || (tt->ver_major == 3 && tt->ver_minor < 3))
367 (void) fprintf(rfile, fmt32, tt->plrole[0], tt->plgend[0]);
369 (void) fprintf(rfile, fmt33, tt->plrole, tt->plrace, tt->plgend,
371 (void) fprintf(rfile, fmtX, onlyspace(tt->name) ? "_" : tt->name,
375 nsb_unmung_line(tt->name);
376 nsb_unmung_line(tt->death);
382 /* as tab is never used in eg. plname or death, no need to mangle those. */
384 writexlentry(rfile, tt, how)
386 struct toptenentry *tt;
389 #define Fprintf (void) fprintf
390 #define XLOG_SEP '\t' /* xlogfile field separator. */
391 char buf[BUFSZ], tmpbuf[DTHSZ + 1];
393 Sprintf(buf, "version=%d.%d.%d", tt->ver_major, tt->ver_minor,
395 Sprintf(eos(buf), "%cpoints=%ld%cdeathdnum=%d%cdeathlev=%d", XLOG_SEP,
396 tt->points, XLOG_SEP, tt->deathdnum, XLOG_SEP, tt->deathlev);
397 Sprintf(eos(buf), "%cmaxlvl=%d%chp=%d%cmaxhp=%d", XLOG_SEP, tt->maxlvl,
398 XLOG_SEP, tt->hp, XLOG_SEP, tt->maxhp);
399 Sprintf(eos(buf), "%cdeaths=%d%cdeathdate=%ld%cbirthdate=%ld%cuid=%d",
400 XLOG_SEP, tt->deaths, XLOG_SEP, tt->deathdate, XLOG_SEP,
401 tt->birthdate, XLOG_SEP, tt->uid);
402 Fprintf(rfile, "%s", buf);
403 Sprintf(buf, "%crole=%s%crace=%s%cgender=%s%calign=%s", XLOG_SEP,
404 tt->plrole, XLOG_SEP, tt->plrace, XLOG_SEP, tt->plgend, XLOG_SEP,
406 /* make a copy of death reason that doesn't include ", while helpless" */
407 formatkiller(tmpbuf, sizeof tmpbuf, how, FALSE);
408 Fprintf(rfile, "%s%cname=%s%cdeath=%s",
409 buf, /* (already includes separator) */
410 XLOG_SEP, plname, XLOG_SEP, tmpbuf);
413 Fprintf(rfile, "%cwhile=%s", XLOG_SEP,
414 multi_reason ? multi_reason : "helpless");
416 Fprintf(rfile, "%cwhile=%s", XLOG_SEP,
417 multi_reason ? multi_reason : "
\96³
\97Í
\82È
\8aÔ
\82É");
419 Fprintf(rfile, "%cconduct=0x%lx%cturns=%ld%cachieve=0x%lx", XLOG_SEP,
420 encodeconduct(), XLOG_SEP, moves, XLOG_SEP, encodeachieve());
421 Fprintf(rfile, "%crealtime=%ld%cstarttime=%ld%cendtime=%ld", XLOG_SEP,
422 (long) urealtime.realtime, XLOG_SEP,
423 (long) ubirthday, XLOG_SEP, (long) urealtime.finish_time);
424 Fprintf(rfile, "%cgender0=%s%calign0=%s", XLOG_SEP,
425 genders[flags.initgend].filecode, XLOG_SEP,
426 aligns[1 - u.ualignbase[A_ORIGINAL]].filecode);
427 Fprintf(rfile, "%cflags=0x%lx", XLOG_SEP, encodexlogflags());
428 Fprintf(rfile, "\n");
441 if (!u.uroleplay.numbones)
452 if (!u.uconduct.food)
454 if (!u.uconduct.unvegan)
456 if (!u.uconduct.unvegetarian)
458 if (!u.uconduct.gnostic)
460 if (!u.uconduct.weaphit)
462 if (!u.uconduct.killer)
464 if (!u.uconduct.literate)
466 if (!u.uconduct.polypiles)
468 if (!u.uconduct.polyselfs)
470 if (!u.uconduct.wishes)
472 if (!u.uconduct.wisharti)
474 if (!num_genocides())
487 if (u.uachieve.enter_gehennom)
489 if (u.uachieve.menorah)
493 if (u.uevent.invoked)
495 if (u.uachieve.amulet)
497 if (In_endgame(&u.uz))
499 if (Is_astralevel(&u.uz))
501 if (u.uachieve.ascended)
503 if (u.uachieve.mines_luckstone)
505 if (u.uachieve.finish_sokoban)
507 if (u.uachieve.killed_medusa)
509 if (u.uroleplay.blind)
511 if (u.uroleplay.nudist)
517 #endif /* XLOGFILE */
521 struct toptenentry *tt;
523 struct toptenentry *ttnext;
525 while (tt->points > 0) {
526 ttnext = tt->tt_next;
539 int rank, rank0 = -1, rank1 = 0;
540 int occ_cnt = sysopt.persmax;
541 register struct toptenentry *t0, *tprev;
542 struct toptenentry *t1;
544 register int flg = 0;
551 #endif /* XLOGFILE */
554 /* Under DICE 3.0, this crashes the system consistently, apparently due to
555 * corruption of *rfile somewhere. Until I figure this out, just cut out
556 * topten support entirely - at least then the game exits cleanly. --AC
561 /* If we are in the midst of a panic, cut out topten entirely.
562 * topten uses alloc() several times, which will lead to
563 * problems if the panic was the result of an alloc() failure.
565 if (program_state.panicking)
568 if (iflags.toptenwin) {
569 toptenwin = create_nhwindow(NHW_TEXT);
572 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
573 #define HUP if (!program_state.done_hup)
579 restore_colors(); /* make sure the screen is black on white */
581 /* create a new 'topten' entry */
584 t0->ver_major = VERSION_MAJOR;
585 t0->ver_minor = VERSION_MINOR;
586 t0->patchlevel = PATCHLEVEL;
587 t0->points = u.urexp;
588 t0->deathdnum = u.uz.dnum;
589 /* deepest_lev_reached() is in terms of depth(), and reporting the
590 * deepest level reached in the dungeon death occurred in doesn't
591 * seem right, so we have to report the death level in depth() terms
592 * as well (which also seems reasonable since that's all the player
593 * sees on the screen anyway)
595 t0->deathlev = observable_depth(&u.uz);
596 t0->maxlvl = deepest_lev_reached(TRUE);
598 t0->maxhp = u.uhpmax;
599 t0->deaths = u.umortality;
601 copynchars(t0->plrole, urole.filecode, ROLESZ);
602 copynchars(t0->plrace, urace.filecode, ROLESZ);
603 copynchars(t0->plgend, genders[flags.female].filecode, ROLESZ);
604 copynchars(t0->plalign, aligns[1 - u.ualign.type].filecode, ROLESZ);
605 copynchars(t0->name, plname, NAMSZ);
606 formatkiller(t0->death, sizeof t0->death, how, TRUE);
607 t0->birthdate = yyyymmdd(ubirthday);
608 t0->deathdate = yyyymmdd(when);
610 #ifdef UPDATE_RECORD_IN_PLACE
614 #ifdef LOGFILE /* used for debugging (who dies of what, where) */
615 if (lock_file(LOGFILE, SCOREPREFIX, 10)) {
616 if (!(lfile = fopen_datafile(LOGFILE, "a", SCOREPREFIX))) {
617 HUP raw_print("Cannot open log file!");
619 writeentry(lfile, t0);
620 (void) fclose(lfile);
622 unlock_file(LOGFILE);
626 if (lock_file(XLOGFILE, SCOREPREFIX, 10)) {
627 if (!(xlfile = fopen_datafile(XLOGFILE, "a", SCOREPREFIX))) {
628 HUP raw_print("Cannot open extended log file!");
630 writexlentry(xlfile, t0, how);
631 (void) fclose(xlfile);
633 unlock_file(XLOGFILE);
635 #endif /* XLOGFILE */
637 if (wizard || discover) {
645 "Since you were in %s mode, the score list will not be checked.",
646 wizard ? "wizard" : "discover");
649 "%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",
650 wizard ? "
\83E
\83B
\83U
\81[
\83h" : "
\94
\8c©");
657 if (!lock_file(RECORD, SCOREPREFIX, 60))
660 #ifdef UPDATE_RECORD_IN_PLACE
661 rfile = fopen_datafile(RECORD, "r+", SCOREPREFIX);
663 rfile = fopen_datafile(RECORD, "r", SCOREPREFIX);
667 HUP raw_print("Cannot open record file!");
672 HUP topten_print("");
674 /* assure minimum number of points */
675 if (t0->points < sysopt.pointsmin)
678 t1 = tt_head = newttentry();
680 /* rank0: -1 undefined, 0 not_on_list, n n_th on list */
682 readentry(rfile, t1);
683 if (t1->points < sysopt.pointsmin)
685 if (rank0 < 0 && t1->points < t0->points) {
692 #ifdef UPDATE_RECORD_IN_PLACE
693 t0->fpos = t1->fpos; /* insert here */
697 flg++; /* ask for a rewrite */
703 if ((sysopt.pers_is_uid ? t1->uid == t0->uid
704 : strncmp(t1->name, t0->name, NAMSZ) == 0)
705 && !strncmp(t1->plrole, t0->plrole, ROLESZ) && --occ_cnt <= 0) {
714 "You didn't beat your previous score of %ld points.",
716 "
\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",
727 if (rank <= sysopt.entrymax) {
728 t1->tt_next = newttentry();
732 if (rank > sysopt.entrymax) {
737 if (flg) { /* rewrite record file */
738 #ifdef UPDATE_RECORD_IN_PLACE
739 (void) fseek(rfile, (t0->fpos >= 0 ? t0->fpos : final_fpos),
742 (void) fclose(rfile);
743 if (!(rfile = fopen_datafile(RECORD, "w", SCOREPREFIX))) {
744 HUP raw_print("Cannot write record file");
746 free_ttlist(tt_head);
749 #endif /* UPDATE_RECORD_IN_PLACE */
754 topten_print("You made the top ten list!");
756 topten_print("
\82 \82È
\82½
\82Í
\83g
\83b
\83v10
\83\8a\83X
\83g
\82É
\8dÚ
\82Á
\82½
\81I");
762 "You reached the %d%s place on the top %d list.",
763 rank0, ordin(rank0), sysopt.entrymax);
766 "
\82 \82È
\82½
\82Í
\81C
\83g
\83b
\83v%d
\83\8a\83X
\83g
\82Ì%d
\88Ê
\82É
\8dÚ
\82Á
\82½
\81D",
767 sysopt.entrymax, rank0);
781 for (rank = 1; t1->points != 0; rank++, t1 = t1->tt_next) {
783 #ifdef UPDATE_RECORD_IN_PLACE
787 writeentry(rfile, t1);
790 if (rank > flags.end_top && (rank < rank0 - flags.end_around
791 || rank > rank0 + flags.end_around)
793 || (sysopt.pers_is_uid
795 : strncmp(t1->name, t0->name, NAMSZ) == 0)))
797 if (rank == rank0 - flags.end_around
798 && rank0 > flags.end_top + flags.end_around + 1 && !flags.end_own)
801 outentry(rank, t1, FALSE);
803 outentry(rank, t1, TRUE);
805 outentry(rank, t1, TRUE);
806 outentry(0, t0, TRUE);
811 outentry(0, t0, TRUE);
812 #ifdef UPDATE_RECORD_IN_PLACE
815 /* if a reasonable way to truncate a file exists, use it */
816 truncate_file(rfile);
818 /* use sentinel record rather than relying on truncation */
819 t1->points = 0L; /* terminates file when read back in */
820 t1->ver_major = t1->ver_minor = t1->patchlevel = 0;
821 t1->uid = t1->deathdnum = t1->deathlev = 0;
822 t1->maxlvl = t1->hp = t1->maxhp = t1->deaths = 0;
823 t1->plrole[0] = t1->plrace[0] = t1->plgend[0] = t1->plalign[0] = '-';
824 t1->plrole[1] = t1->plrace[1] = t1->plgend[1] = t1->plalign[1] = 0;
825 t1->birthdate = t1->deathdate = yyyymmdd((time_t) 0L);
826 Strcpy(t1->name, "@");
827 Strcpy(t1->death, "<eod>\n");
828 writeentry(rfile, t1);
829 (void) fflush(rfile);
830 #endif /* TRUNCATE_FILE */
832 #endif /* UPDATE_RECORD_IN_PLACE */
833 (void) fclose(rfile);
835 free_ttlist(tt_head);
838 if (iflags.toptenwin && !done_stopprint)
839 display_nhwindow(toptenwin, 1);
843 if (iflags.toptenwin) {
844 destroy_nhwindow(toptenwin);
855 Strcpy(linebuf, " No Points Name");
857 while (bp < linebuf + COLNO - 9)
859 Strcpy(bp, "Hp [max]");
860 topten_print(linebuf);
863 /* so>0: standout line; so=0: ordinary line */
865 outentry(rank, t1, so)
866 struct toptenentry *t1;
870 boolean second_line = TRUE;
873 char *bp, hpbuf[24], linebuf3[BUFSZ];
894 Sprintf(eos(linebuf), "%3d", rank);
896 Strcat(linebuf, " ");
898 Sprintf(eos(linebuf), " %10ld %.10s", t1->points ? t1->points : u.urexp,
900 Sprintf(eos(linebuf), "-%s", t1->plrole);
901 if (t1->plrace[0] != '?')
902 Sprintf(eos(linebuf), "-%s", t1->plrace);
903 /* Printing of gender and alignment is intentional. It has been
904 * part of the NetHack Geek Code, and illustrates a proper way to
905 * specify a character from the command line.
907 Sprintf(eos(linebuf), "-%s", t1->plgend);
908 if (t1->plalign[0] != '?')
910 Sprintf(eos(linebuf), "-%s ", t1->plalign);
912 Sprintf(eos(linebuf), "-%s", t1->plalign);
915 Strcat(linebuf, " ");
919 Strcat(linebuf, "
\82Í");
920 /*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 */
922 if (!STRNCMP2(jdeath, "
\96\82\8f\9c\82¯
\82ð
\8eè
\82É"))
924 else if (!STRNCMP2(jdeath, "
\93V
\8fã
\82Å
\92p
\90J
\82ð
\8eó
\82¯"))
926 else if (!STRNCMP2(jdeath, "
\8bU
\95¨
\82Ì
\96\82\8f\9c\82¯
\82ð
\92Í
\82Ü
\82³
\82ê"))
930 if (!strncmp("escaped", t1->death, 7)) {
932 if (!STRNCMP2("
\92E
\8fo
\82µ
\82½", jdeath)
933 || !strncmp("escaped", jdeath, 7)) {
936 Sprintf(eos(linebuf), "escaped the dungeon %s[max level %d]",
937 !strncmp(" (", t1->death + 7, 2) ? t1->death + 7 + 2 : "",
939 /* fixup for closing paren in "escaped... with...Amulet)[max..." */
940 if ((bp = index(linebuf, ')')) != 0)
941 *bp = (t1->deathdnum == astral_level.dnum) ? '\0' : ' ';
944 strncpy(jbuf, t1->death, jdeath - t1->death);
945 jbuf[jdeath - t1->death] = '\0';
946 Sprintf(action, "%s
\96À
\8b{
\82©
\82ç
\92E
\8fo
\82µ
\82½[
\8dÅ
\91å
\92n
\89º%d
\8aK]",
951 } else if (!strncmp("ascended", t1->death, 8)) {
953 } else if (!STRNCMP2("
\8f¸
\93V
\82µ
\82½", jdeath)
954 || !strncmp("ascended", jdeath, 8)) {
957 Sprintf(eos(linebuf), "ascended to demigod%s-hood",
958 (t1->plgend[0] == 'F') ? "dess" : "");
960 Sprintf(action, "
\8f¸
\93V
\82µ%s
\90_
\82Æ
\82È
\82Á
\82½",
961 (t1->plgend[0] == 'F') ? "
\8f\97" : "");
966 if (!strncmp(t1->death, "quit", 4)) {
968 if (!STRNCMP2(jdeath, "
\94²
\82¯
\82½")) {
970 Strcat(linebuf, "quit");
972 Strcat(action, t1->death);
976 } else if (!strncmp(t1->death, "died of st", 10)) {
977 Strcat(linebuf, "starved to death");
979 } else if (!strncmp(t1->death, "choked", 6)) {
980 Sprintf(eos(linebuf), "choked on h%s food",
981 (t1->plgend[0] == 'F') ? "er" : "is");
982 } else if (!strncmp(t1->death, "poisoned", 8)) {
983 Strcat(linebuf, "was poisoned");
984 } else if (!strncmp(t1->death, "crushed", 7)) {
985 Strcat(linebuf, "was crushed to death");
986 } else if (!strncmp(t1->death, "petrified by ", 13)) {
987 Strcat(linebuf, "turned to stone");
989 Strcat(linebuf, "died");
994 if (t1->deathdnum == astral_level.dnum) {
996 const char *arg, *fmt = " on the Plane of %s";
1001 switch (t1->deathlev) {
1004 fmt = " on the %s Plane";
1009 arg = "
\93V
\8fã
\8aE";
1015 arg = "
\90\85\82Ì
\90¸
\97ì
\8aE";
1021 arg = "
\89Î
\82Ì
\90¸
\97ì
\8aE";
1027 arg = "
\95\97\82Ì
\90¸
\97ì
\8aE";
1033 arg = "
\92n
\82Ì
\90¸
\97ì
\8aE";
1040 Sprintf(eos(linebuf), fmt, arg);
1042 Sprintf(where, "%s
\82É
\82Ä", arg);
1046 Sprintf(eos(linebuf), " in %s", dungeons[t1->deathdnum].dname);
1048 Sprintf(eos(linebuf), "%s", dungeons[t1->deathdnum].dname);
1049 if (t1->deathdnum != knox_level.dnum)
1051 Sprintf(eos(linebuf), " on level %d", t1->deathlev);
1053 Sprintf(eos(linebuf), "
\82Ì
\92n
\89º%d
\8aK
\82É
\82Ä", t1->deathlev);
1054 if (t1->deathlev != t1->maxlvl)
1056 Sprintf(eos(linebuf), " [max %d]", t1->maxlvl);
1058 Sprintf(eos(where), "[
\8dÅ
\91å
\92n
\89º%d
\8aK]", t1->maxlvl);
1061 #if 0 /*JP*//*
\93ú
\96{
\8cê
\82Å
\82Í
\8d×
\8dH
\95s
\97v */
1062 /* kludge for "quit while already on Charon's boat" */
1063 if (!strncmp(t1->death, "quit ", 5))
1064 Strcat(linebuf, t1->death + 4);
1068 Strcat(linebuf, ".");
1071 /* Quit, starved, ascended, and escaped contain no second line */
1074 Sprintf(eos(linebuf), " %c%s.", highc(*(t1->death)), t1->death + 1);
1076 Sprintf(action, "%s", t1->death);
1079 Sprintf(eos(linebuf), "%s%s%s
\81D", who, where, action);
1081 lngr = (int) strlen(linebuf);
1083 hpbuf[0] = '-', hpbuf[1] = '\0';
1085 Sprintf(hpbuf, "%d", t1->hp);
1086 /* beginning of hp column after padding (not actually padded yet) */
1087 hppos = COLNO - (sizeof(" Hp [max]") - 1); /* sizeof(str) includes \0 */
1089 while (lngr >= hppos) {
1090 /*JP hppos
\82æ
\82è
\91O
\82Ì
\93K
\93\96\82È
\88Ê
\92u
\82Å
\95ª
\8a\84\82·
\82é
\81D*/
1093 split_japanese(linebuf, car, cdr, hppos);
1097 while (bp < car + (COLNO-1)) *bp++ = ' ';
1099 topten_print_bold(car);
1103 Sprintf(linebuf, "%15s %s", "", cdr);
1104 lngr = (int)strlen(linebuf);
1106 /*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*/
1108 while (lngr >= hppos) {
1109 for (bp = eos(linebuf); !(*bp == ' ' && (bp - linebuf < hppos)); bp--)
1111 /* special case: word is too long, wrap in the middle */
1112 if (linebuf + 15 >= bp)
1113 bp = linebuf + hppos - 1;
1114 /* special case: if about to wrap in the middle of maximum
1115 dungeon depth reached, wrap in front of it instead */
1116 if (bp > linebuf + 5 && !strncmp(bp - 5, " [max", 5))
1119 Strcpy(linebuf3, bp);
1121 Strcpy(linebuf3, bp + 1);
1124 while (bp < linebuf + (COLNO - 1))
1127 topten_print_bold(linebuf);
1129 topten_print(linebuf);
1130 Sprintf(linebuf, "%15s %s", "", linebuf3);
1131 lngr = strlen(linebuf);
1134 /* beginning of hp column not including padding */
1135 hppos = COLNO - 7 - (int) strlen(hpbuf);
1138 if (bp <= linebuf + hppos) {
1139 /* pad any necessary blanks to the hit point entry */
1140 while (bp < linebuf + hppos)
1143 Sprintf(eos(bp), " %s[%d]",
1144 (t1->maxhp < 10) ? " " : (t1->maxhp < 100) ? " " : "",
1152 while (bp < linebuf + so)
1155 topten_print_bold(linebuf);
1157 topten_print(linebuf);
1161 score_wanted(current_ver, rank, t1, playerct, players, uid)
1162 boolean current_ver;
1164 struct toptenentry *t1;
1166 const char **players;
1172 && (t1->ver_major != VERSION_MAJOR || t1->ver_minor != VERSION_MINOR
1173 || t1->patchlevel != PATCHLEVEL))
1176 if (sysopt.pers_is_uid && !playerct && t1->uid == uid)
1179 for (i = 0; i < playerct; i++) {
1180 if (players[i][0] == '-' && index("pr", players[i][1])
1181 && players[i][2] == 0 && i + 1 < playerct) {
1182 const char *arg = players[i + 1];
1183 if ((players[i][1] == 'p'
1184 && str2role(arg) == str2role(t1->plrole))
1185 || (players[i][1] == 'r'
1186 && str2race(arg) == str2race(t1->plrace)))
1189 } else if (strcmp(players[i], "all") == 0
1190 || strncmp(t1->name, players[i], NAMSZ) == 0
1191 || (players[i][0] == '-' && players[i][1] == t1->plrole[0]
1192 && players[i][2] == 0)
1193 || (digit(players[i][0]) && rank <= atoi(players[i])))
1200 * print selected parts of score list.
1201 * argc >= 2, with argv[0] untrustworthy (directory names, et al.),
1202 * and argv[1] starting with "-s".
1203 * caveat: some shells might allow argv elements to be arbitrarily long.
1210 const char **players;
1212 boolean current_ver = TRUE, init_done = FALSE;
1213 register struct toptenentry *t1;
1215 boolean match_found = FALSE;
1219 const char *player0;
1221 if (argc < 2 || strncmp(argv[1], "-s", 2)) {
1222 raw_printf("prscore: bad arguments (%d)", argc);
1226 rfile = fopen_datafile(RECORD, "r", SCOREPREFIX);
1228 raw_print("Cannot open record file!");
1234 extern winid amii_rawprwin;
1236 init_nhwindows(&argc, argv);
1237 amii_rawprwin = create_nhwindow(NHW_TEXT);
1241 /* If the score list isn't after a game, we never went through
1242 * initialization. */
1243 if (wiz1_level.dlevel == 0) {
1249 if (!argv[1][2]) { /* plain "-s" */
1255 if (argc > 1 && !strcmp(argv[1], "-v")) {
1256 current_ver = FALSE;
1262 if (sysopt.pers_is_uid) {
1265 players = (const char **) 0;
1270 player0 = "all"; /* single user system */
1272 player0 = "hackplayer";
1280 players = (const char **) ++argv;
1285 players = (const char **)alloc(sizeof(char *) * argc + 1);
1286 for (j = 0; j < argc; j++) {
1287 char *p = (char *)str2ic(argv[j]);
1288 players[j] = (char *)alloc(strlen(p) + 1);
1289 strcpy((void *)players[j], p);
1296 t1 = tt_head = newttentry();
1297 for (rank = 1;; rank++) {
1298 readentry(rfile, t1);
1299 if (t1->points == 0)
1302 && score_wanted(current_ver, rank, t1, playerct, players, uid))
1304 t1->tt_next = newttentry();
1308 (void) fclose(rfile);
1317 for (rank = 1; t1->points != 0; rank++, t1 = t1->tt_next) {
1318 if (score_wanted(current_ver, rank, t1, playerct, players, uid))
1319 (void) outentry(rank, t1, FALSE);
1322 Sprintf(pbuf, "Cannot find any %sentries for ",
1323 current_ver ? "current " : "");
1325 Strcat(pbuf, "you.");
1328 Strcat(pbuf, "any of ");
1329 for (i = 0; i < playerct; i++) {
1330 /* stop printing players if there are too many to fit */
1331 if (strlen(pbuf) + strlen(players[i]) + 2 >= BUFSZ) {
1332 if (strlen(pbuf) < BUFSZ - 4)
1333 Strcat(pbuf, "...");
1335 Strcpy(pbuf + strlen(pbuf) - 4, "...");
1338 Strcat(pbuf, players[i]);
1339 if (i < playerct - 1) {
1340 if (players[i][0] == '-' && index("pr", players[i][1])
1341 && players[i][2] == 0)
1349 raw_printf("Usage: %s -s [-v] <playertypes> [maxrank] [playernames]",
1352 raw_printf("Player types are: [-p role] [-r race]");
1354 free_ttlist(tt_head);
1357 extern winid amii_rawprwin;
1359 display_nhwindow(amii_rawprwin, 1);
1360 destroy_nhwindow(amii_rawprwin);
1361 amii_rawprwin = WIN_ERR;
1373 /* Look for this role in the role table */
1374 for (i = 0; roles[i].name.m; i++)
1375 if (!strncmp(plch, roles[i].filecode, ROLESZ)) {
1376 if (fem && roles[i].femalenum != NON_PM)
1377 return roles[i].femalenum;
1378 else if (roles[i].malenum != NON_PM)
1379 return roles[i].malenum;
1383 /* this might be from a 3.2.x score for former Elf class */
1384 if (!strcmp(plch, "E"))
1387 impossible("What weird role is this? (%s)", plch);
1388 return PM_HUMAN_MUMMY;
1392 * Get a random player name and class from the high score list,
1394 struct toptenentry *
1395 get_rnd_toptenentry()
1399 register struct toptenentry *tt;
1400 static struct toptenentry tt_buf;
1402 rfile = fopen_datafile(RECORD, "r", SCOREPREFIX);
1404 impossible("Cannot open record file!");
1409 rank = rnd(sysopt.tt_oname_maxrank);
1411 for (i = rank; i; i--) {
1412 readentry(rfile, tt);
1413 if (tt->points == 0)
1417 if (tt->points == 0) {
1426 (void) fclose(rfile);
1432 * Attach random player name and class from high score list
1433 * to an object (for statues or morgue corpses).
1439 struct toptenentry *tt;
1441 return (struct obj *) 0;
1443 tt = get_rnd_toptenentry();
1446 return (struct obj *) 0;
1448 set_corpsenm(otmp, classmon(tt->plrole, (tt->plgend[0] == 'F')));
1449 otmp = oname(otmp, tt->name);
1454 #ifdef NO_SCAN_BRACK
1455 /* Lattice scanf isn't up to reading the scorefile. What */
1456 /* follows deals with that; I admit it's ugly. (KL) */
1457 /* Now generally available (KL) */
1462 while ((p = index(p, ' ')) != 0)
1470 while ((p = index(p, '|')) != 0)
1473 #endif /* NO_SCAN_BRACK */