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-2018 */
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 xchar FDECL(observable_depth, (d_level *));
71 STATIC_DCL void NDECL(outheader);
72 STATIC_DCL void FDECL(outentry, (int, struct toptenentry *, BOOLEAN_P));
73 STATIC_DCL void FDECL(discardexcess, (FILE *));
74 STATIC_DCL void FDECL(readentry, (FILE *, struct toptenentry *));
75 STATIC_DCL void FDECL(writeentry, (FILE *, struct toptenentry *));
77 STATIC_DCL void FDECL(writexlentry, (FILE *, struct toptenentry *, int));
78 STATIC_DCL long NDECL(encodexlogflags);
79 STATIC_DCL long NDECL(encodeconduct);
80 STATIC_DCL long NDECL(encodeachieve);
82 STATIC_DCL void FDECL(free_ttlist, (struct toptenentry *));
83 STATIC_DCL int FDECL(classmon, (char *, BOOLEAN_P));
84 STATIC_DCL int FDECL(score_wanted, (BOOLEAN_P, int, struct toptenentry *, int,
87 STATIC_DCL void FDECL(nsb_mung_line, (char *));
88 STATIC_DCL void FDECL(nsb_unmung_line, (char *));
91 static winid toptenwin = WIN_ERR;
93 /* "killed by",&c ["an"] 'killer.name' */
95 formatkiller(buf, siz, how, incl_helpless)
99 boolean incl_helpless;
101 static NEARDATA const char *const killed_by_prefix[] = {
102 /* DIED, CHOKING, POISONING, STARVING, */
104 "killed by ", "choked on ", "poisoned by ", "died of ",
106 "
\8e\80\82ñ
\82¾", "
\82Å
\92\82\91§
\82µ
\82½", "
\82Ì
\93Å
\82Å
\8e\80\82ñ
\82¾", "",
107 /* DROWNING, BURNING, DISSOLVED, CRUSHING, */
109 "drowned in ", "burned by ", "dissolved in ", "crushed to death by ",
111 "
\93M
\8e\80\82µ
\82½","
\8fÄ
\8e\80\82µ
\82½", "
\97n
\8aâ
\82É
\97n
\82¯
\82½", "
\89\9f\82µ
\92×
\82³
\82ê
\82½",
112 /* STONING, TURNED_SLIME, GENOCIDED, */
114 "petrified by ", "turned to slime by ", "killed by ",
116 "
\90Î
\82É
\82È
\82Á
\82½", "
\82É
\83X
\83\89\83C
\83\80\82É
\82³
\82ê
\82½", "
\8bs
\8eE
\82³
\82ê
\82½",
117 /* PANICKED, TRICKED, QUIT, ESCAPED, ASCENDED */
123 char c, *kname = killer.name;
125 buf[0] = '\0'; /* lint suppression */
126 #if 1 /*JP*//*
\90æ
\82É
\91Î
\8fÛ
\82ð
\83R
\83s
\81[*/
127 strncat(buf, kname, siz - 1);
130 switch (killer.format) {
132 impossible("bad killer format? (%d)", killer.format);
134 case NO_KILLER_PREFIX:
137 #if 0 /*JP*//*
\93ú
\96{
\8cê
\82Å
\82Í
\95s
\97v*/
143 (void) strncat(buf, killed_by_prefix[how], siz - 1);
146 #else /*JP:
\8aù
\82É
\91Î
\8fÛ
\82ð
\83R
\83s
\81[
\82µ
\82Ä
\82¢
\82é
\82Ì
\82Å
\92P
\82É
\92Ç
\89Á*/
147 (void) strncat(buf, killed_by_prefix[how], siz - 1);
152 (void) strncat(buf, "
\82É
\8eE
\82³
\82ê
\82½", siz - 1);
155 #if 0 /*JP*//*
\8aù
\82É
\83R
\83s
\81[
\8dÏ
\82Ý*//*JP:TODO:
\83T
\83j
\83^
\83C
\83Y
\82ª
\95K
\97v*/
156 /* Copy kname into buf[].
157 * Object names and named fruit have already been sanitized, but
158 * monsters can have "called 'arbitrary text'" attached to them,
159 * so make sure that that text can't confuse field splitting when
160 * record, logfile, or xlogfile is re-read at some later point.
166 /* 'xlogfile' doesn't really need protection for '=', but
167 fixrecord.awk for corrupted 3.6.0 'record' does (only
168 if using xlogfile rather than logfile to repair record) */
171 /* tab is not possible due to use of mungspaces() when naming;
172 it would disrupt xlogfile parsing if it were present */
180 #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¢*/
181 if (incl_helpless && multi) {
182 /* X <= siz: 'sizeof "string"' includes 1 for '\0' terminator */
183 if (multi_reason && strlen(multi_reason) + sizeof ", while " <= siz)
184 Sprintf(buf, ", while %s", multi_reason);
185 /* either multi_reason wasn't specified or wouldn't fit */
186 else if (sizeof ", while helpless" <= siz)
187 Strcpy(buf, ", while helpless");
188 /* else extra death info won't fit, so leave it out */
197 if (toptenwin == WIN_ERR)
200 putstr(toptenwin, ATR_NONE, x);
207 if (toptenwin == WIN_ERR)
210 putstr(toptenwin, ATR_BOLD, x);
214 observable_depth(lev)
218 /* if we ever randomize the order of the elemental planes, we
219 must use a constant external representation in the record file */
220 if (In_endgame(lev)) {
221 if (Is_astralevel(lev))
223 else if (Is_waterlevel(lev))
225 else if (Is_firelevel(lev))
227 else if (Is_airlevel(lev))
229 else if (Is_earthlevel(lev))
238 /* throw away characters until current record has been entirely consumed */
247 } while (c != '\n' && c != EOF);
253 struct toptenentry *tt;
255 char inbuf[SCANBUFSZ], s1[SCANBUFSZ], s2[SCANBUFSZ], s3[SCANBUFSZ],
256 s4[SCANBUFSZ], s5[SCANBUFSZ], s6[SCANBUFSZ];
258 #ifdef NO_SCAN_BRACK /* Version_ Pts DgnLevs_ Hp___ Died__Born id */
259 static const char fmt[] = "%d %d %d %ld %d %d %d %d %d %d %ld %ld %d%*c";
260 static const char fmt32[] = "%c%c %s %s%*c";
261 static const char fmt33[] = "%s %s %s %s %s %s%*c";
263 static const char fmt[] = "%d.%d.%d %ld %d %d %d %d %d %d %ld %ld %d ";
264 static const char fmt32[] = "%c%c %[^,],%[^\n]%*c";
265 static const char fmt33[] = "%s %s %s %s %[^,],%[^\n]%*c";
268 #ifdef UPDATE_RECORD_IN_PLACE
269 /* note: input below must read the record's terminating newline */
270 final_fpos = tt->fpos = ftell(rfile);
273 if (fscanf(rfile, fmt, &tt->ver_major, &tt->ver_minor, &tt->patchlevel,
274 &tt->points, &tt->deathdnum, &tt->deathlev, &tt->maxlvl,
275 &tt->hp, &tt->maxhp, &tt->deaths, &tt->deathdate,
276 &tt->birthdate, &tt->uid) != TTFIELDS) {
279 discardexcess(rfile);
281 /* load remainder of record into a local buffer;
282 this imposes an implicit length limit of SCANBUFSZ
283 on every string field extracted from the buffer */
284 if (!fgets(inbuf, sizeof inbuf, rfile)) {
285 /* sscanf will fail and tt->points will be set to 0 */
287 } else if (!index(inbuf, '\n')) {
288 Strcpy(&inbuf[sizeof inbuf - 2], "\n");
289 discardexcess(rfile);
291 /* Check for backwards compatibility */
292 if (tt->ver_major < 3 || (tt->ver_major == 3 && tt->ver_minor < 3)) {
295 if (sscanf(inbuf, fmt32, tt->plrole, tt->plgend, s1, s2) == 4) {
296 tt->plrole[1] = tt->plgend[1] = '\0'; /* read via %c */
297 copynchars(tt->name, s1, (int) (sizeof tt->name) - 1);
298 copynchars(tt->death, s2, (int) (sizeof tt->death) - 1);
301 tt->plrole[1] = '\0';
302 if ((i = str2role(tt->plrole)) >= 0)
303 Strcpy(tt->plrole, roles[i].filecode);
304 Strcpy(tt->plrace, "?");
305 Strcpy(tt->plgend, (tt->plgend[0] == 'M') ? "Mal" : "Fem");
306 Strcpy(tt->plalign, "?");
307 } else if (sscanf(inbuf, fmt33, s1, s2, s3, s4, s5, s6) == 6) {
308 copynchars(tt->plrole, s1, (int) (sizeof tt->plrole) - 1);
309 copynchars(tt->plrace, s2, (int) (sizeof tt->plrace) - 1);
310 copynchars(tt->plgend, s3, (int) (sizeof tt->plgend) - 1);
311 copynchars(tt->plalign, s4, (int) (sizeof tt->plalign) - 1);
312 copynchars(tt->name, s5, (int) (sizeof tt->name) - 1);
313 copynchars(tt->death, s6, (int) (sizeof tt->death) - 1);
317 if (tt->points > 0) {
318 nsb_unmung_line(tt->name);
319 nsb_unmung_line(tt->death);
324 /* check old score entries for Y2K problem and fix whenever found */
325 if (tt->points > 0) {
326 if (tt->birthdate < 19000000L)
327 tt->birthdate += 19000000L;
328 if (tt->deathdate < 19000000L)
329 tt->deathdate += 19000000L;
334 writeentry(rfile, tt)
336 struct toptenentry *tt;
338 static const char fmt32[] = "%c%c "; /* role,gender */
339 static const char fmt33[] = "%s %s %s %s "; /* role,race,gndr,algn */
340 #ifndef NO_SCAN_BRACK
341 static const char fmt0[] = "%d.%d.%d %ld %d %d %d %d %d %d %ld %ld %d ";
342 static const char fmtX[] = "%s,%s\n";
343 #else /* 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";
347 nsb_mung_line(tt->name);
348 nsb_mung_line(tt->death);
351 (void) fprintf(rfile, fmt0, tt->ver_major, tt->ver_minor, tt->patchlevel,
352 tt->points, tt->deathdnum, tt->deathlev, tt->maxlvl,
353 tt->hp, tt->maxhp, tt->deaths, tt->deathdate,
354 tt->birthdate, tt->uid);
355 if (tt->ver_major < 3 || (tt->ver_major == 3 && tt->ver_minor < 3))
356 (void) fprintf(rfile, fmt32, tt->plrole[0], tt->plgend[0]);
358 (void) fprintf(rfile, fmt33, tt->plrole, tt->plrace, tt->plgend,
360 (void) fprintf(rfile, fmtX, onlyspace(tt->name) ? "_" : tt->name,
364 nsb_unmung_line(tt->name);
365 nsb_unmung_line(tt->death);
371 /* as tab is never used in eg. plname or death, no need to mangle those. */
373 writexlentry(rfile, tt, how)
375 struct toptenentry *tt;
378 #define Fprintf (void) fprintf
379 #define XLOG_SEP '\t' /* xlogfile field separator. */
380 char buf[BUFSZ], tmpbuf[DTHSZ + 1];
382 Sprintf(buf, "version=%d.%d.%d", tt->ver_major, tt->ver_minor,
384 Sprintf(eos(buf), "%cpoints=%ld%cdeathdnum=%d%cdeathlev=%d", XLOG_SEP,
385 tt->points, XLOG_SEP, tt->deathdnum, XLOG_SEP, tt->deathlev);
386 Sprintf(eos(buf), "%cmaxlvl=%d%chp=%d%cmaxhp=%d", XLOG_SEP, tt->maxlvl,
387 XLOG_SEP, tt->hp, XLOG_SEP, tt->maxhp);
388 Sprintf(eos(buf), "%cdeaths=%d%cdeathdate=%ld%cbirthdate=%ld%cuid=%d",
389 XLOG_SEP, tt->deaths, XLOG_SEP, tt->deathdate, XLOG_SEP,
390 tt->birthdate, XLOG_SEP, tt->uid);
391 Fprintf(rfile, "%s", buf);
392 Sprintf(buf, "%crole=%s%crace=%s%cgender=%s%calign=%s", XLOG_SEP,
393 tt->plrole, XLOG_SEP, tt->plrace, XLOG_SEP, tt->plgend, XLOG_SEP,
395 /* make a copy of death reason that doesn't include ", while helpless" */
396 formatkiller(tmpbuf, sizeof tmpbuf, how, FALSE);
397 Fprintf(rfile, "%s%cname=%s%cdeath=%s",
398 buf, /* (already includes separator) */
399 XLOG_SEP, plname, XLOG_SEP, tmpbuf);
402 Fprintf(rfile, "%cwhile=%s", XLOG_SEP,
403 multi_reason ? multi_reason : "helpless");
405 Fprintf(rfile, "%cwhile=%s", XLOG_SEP,
406 multi_reason ? multi_reason : "
\96³
\97Í
\82È
\8aÔ
\82É");
408 Fprintf(rfile, "%cconduct=0x%lx%cturns=%ld%cachieve=0x%lx", XLOG_SEP,
409 encodeconduct(), XLOG_SEP, moves, XLOG_SEP, encodeachieve());
410 Fprintf(rfile, "%crealtime=%ld%cstarttime=%ld%cendtime=%ld", XLOG_SEP,
411 (long) urealtime.realtime, XLOG_SEP,
412 (long) ubirthday, XLOG_SEP, (long) urealtime.finish_time);
413 Fprintf(rfile, "%cgender0=%s%calign0=%s", XLOG_SEP,
414 genders[flags.initgend].filecode, XLOG_SEP,
415 aligns[1 - u.ualignbase[A_ORIGINAL]].filecode);
416 Fprintf(rfile, "%cflags=0x%lx", XLOG_SEP, encodexlogflags());
417 Fprintf(rfile, "\n");
430 if (!u.uroleplay.numbones)
441 if (!u.uconduct.food)
443 if (!u.uconduct.unvegan)
445 if (!u.uconduct.unvegetarian)
447 if (!u.uconduct.gnostic)
449 if (!u.uconduct.weaphit)
451 if (!u.uconduct.killer)
453 if (!u.uconduct.literate)
455 if (!u.uconduct.polypiles)
457 if (!u.uconduct.polyselfs)
459 if (!u.uconduct.wishes)
461 if (!u.uconduct.wisharti)
463 if (!num_genocides())
476 if (u.uachieve.enter_gehennom)
478 if (u.uachieve.menorah)
482 if (u.uevent.invoked)
484 if (u.uachieve.amulet)
486 if (In_endgame(&u.uz))
488 if (Is_astralevel(&u.uz))
490 if (u.uachieve.ascended)
492 if (u.uachieve.mines_luckstone)
494 if (u.uachieve.finish_sokoban)
496 if (u.uachieve.killed_medusa)
498 if (u.uroleplay.blind)
500 if (u.uroleplay.nudist)
506 #endif /* XLOGFILE */
510 struct toptenentry *tt;
512 struct toptenentry *ttnext;
514 while (tt->points > 0) {
515 ttnext = tt->tt_next;
528 int rank, rank0 = -1, rank1 = 0;
529 int occ_cnt = sysopt.persmax;
530 register struct toptenentry *t0, *tprev;
531 struct toptenentry *t1;
533 register int flg = 0;
540 #endif /* XLOGFILE */
543 /* Under DICE 3.0, this crashes the system consistently, apparently due to
544 * corruption of *rfile somewhere. Until I figure this out, just cut out
545 * topten support entirely - at least then the game exits cleanly. --AC
550 /* If we are in the midst of a panic, cut out topten entirely.
551 * topten uses alloc() several times, which will lead to
552 * problems if the panic was the result of an alloc() failure.
554 if (program_state.panicking)
557 if (iflags.toptenwin) {
558 toptenwin = create_nhwindow(NHW_TEXT);
561 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
562 #define HUP if (!program_state.done_hup)
568 restore_colors(); /* make sure the screen is black on white */
570 /* create a new 'topten' entry */
573 t0->ver_major = VERSION_MAJOR;
574 t0->ver_minor = VERSION_MINOR;
575 t0->patchlevel = PATCHLEVEL;
576 t0->points = u.urexp;
577 t0->deathdnum = u.uz.dnum;
578 /* deepest_lev_reached() is in terms of depth(), and reporting the
579 * deepest level reached in the dungeon death occurred in doesn't
580 * seem right, so we have to report the death level in depth() terms
581 * as well (which also seems reasonable since that's all the player
582 * sees on the screen anyway)
584 t0->deathlev = observable_depth(&u.uz);
585 t0->maxlvl = deepest_lev_reached(TRUE);
587 t0->maxhp = u.uhpmax;
588 t0->deaths = u.umortality;
590 copynchars(t0->plrole, urole.filecode, ROLESZ);
591 copynchars(t0->plrace, urace.filecode, ROLESZ);
592 copynchars(t0->plgend, genders[flags.female].filecode, ROLESZ);
593 copynchars(t0->plalign, aligns[1 - u.ualign.type].filecode, ROLESZ);
594 copynchars(t0->name, plname, NAMSZ);
595 formatkiller(t0->death, sizeof t0->death, how, TRUE);
596 t0->birthdate = yyyymmdd(ubirthday);
597 t0->deathdate = yyyymmdd(when);
599 #ifdef UPDATE_RECORD_IN_PLACE
603 #ifdef LOGFILE /* used for debugging (who dies of what, where) */
604 if (lock_file(LOGFILE, SCOREPREFIX, 10)) {
605 if (!(lfile = fopen_datafile(LOGFILE, "a", SCOREPREFIX))) {
606 HUP raw_print("Cannot open log file!");
608 writeentry(lfile, t0);
609 (void) fclose(lfile);
611 unlock_file(LOGFILE);
615 if (lock_file(XLOGFILE, SCOREPREFIX, 10)) {
616 if (!(xlfile = fopen_datafile(XLOGFILE, "a", SCOREPREFIX))) {
617 HUP raw_print("Cannot open extended log file!");
619 writexlentry(xlfile, t0, how);
620 (void) fclose(xlfile);
622 unlock_file(XLOGFILE);
624 #endif /* XLOGFILE */
626 if (wizard || discover) {
634 "Since you were in %s mode, the score list will not be checked.",
635 wizard ? "wizard" : "discover");
637 "%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",
638 wizard ? "
\83E
\83B
\83U
\81[
\83h" : "
\94
\8c©");
645 if (!lock_file(RECORD, SCOREPREFIX, 60))
648 #ifdef UPDATE_RECORD_IN_PLACE
649 rfile = fopen_datafile(RECORD, "r+", SCOREPREFIX);
651 rfile = fopen_datafile(RECORD, "r", SCOREPREFIX);
655 HUP raw_print("Cannot open record file!");
660 HUP topten_print("");
662 /* assure minimum number of points */
663 if (t0->points < sysopt.pointsmin)
666 t1 = tt_head = newttentry();
668 /* rank0: -1 undefined, 0 not_on_list, n n_th on list */
670 readentry(rfile, t1);
671 if (t1->points < sysopt.pointsmin)
673 if (rank0 < 0 && t1->points < t0->points) {
680 #ifdef UPDATE_RECORD_IN_PLACE
681 t0->fpos = t1->fpos; /* insert here */
685 flg++; /* ask for a rewrite */
691 if ((sysopt.pers_is_uid ? t1->uid == t0->uid
692 : strncmp(t1->name, t0->name, NAMSZ) == 0)
693 && !strncmp(t1->plrole, t0->plrole, ROLESZ) && --occ_cnt <= 0) {
702 "You didn't beat your previous score of %ld points.",
704 "
\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",
715 if (rank <= sysopt.entrymax) {
716 t1->tt_next = newttentry();
720 if (rank > sysopt.entrymax) {
725 if (flg) { /* rewrite record file */
726 #ifdef UPDATE_RECORD_IN_PLACE
727 (void) fseek(rfile, (t0->fpos >= 0 ? t0->fpos : final_fpos),
730 (void) fclose(rfile);
731 if (!(rfile = fopen_datafile(RECORD, "w", SCOREPREFIX))) {
732 HUP raw_print("Cannot write record file");
734 free_ttlist(tt_head);
737 #endif /* UPDATE_RECORD_IN_PLACE */
742 topten_print("You made the top ten list!");
744 topten_print("
\82 \82È
\82½
\82Í
\83g
\83b
\83v10
\83\8a\83X
\83g
\82É
\8dÚ
\82Á
\82½
\81I");
750 "You reached the %d%s place on the top %d list.",
751 rank0, ordin(rank0), sysopt.entrymax);
754 "
\82 \82È
\82½
\82Í
\81C
\83g
\83b
\83v%d
\83\8a\83X
\83g
\82Ì%d
\88Ê
\82É
\8dÚ
\82Á
\82½
\81D",
755 sysopt.entrymax, rank0);
769 for (rank = 1; t1->points != 0; rank++, t1 = t1->tt_next) {
771 #ifdef UPDATE_RECORD_IN_PLACE
775 writeentry(rfile, t1);
778 if (rank > flags.end_top && (rank < rank0 - flags.end_around
779 || rank > rank0 + flags.end_around)
781 || (sysopt.pers_is_uid
783 : strncmp(t1->name, t0->name, NAMSZ) == 0)))
785 if (rank == rank0 - flags.end_around
786 && rank0 > flags.end_top + flags.end_around + 1 && !flags.end_own)
789 outentry(rank, t1, FALSE);
791 outentry(rank, t1, TRUE);
793 outentry(rank, t1, TRUE);
794 outentry(0, t0, TRUE);
799 outentry(0, t0, TRUE);
800 #ifdef UPDATE_RECORD_IN_PLACE
803 /* if a reasonable way to truncate a file exists, use it */
804 truncate_file(rfile);
806 /* use sentinel record rather than relying on truncation */
807 t1->points = 0L; /* terminates file when read back in */
808 t1->ver_major = t1->ver_minor = t1->patchlevel = 0;
809 t1->uid = t1->deathdnum = t1->deathlev = 0;
810 t1->maxlvl = t1->hp = t1->maxhp = t1->deaths = 0;
811 t1->plrole[0] = t1->plrace[0] = t1->plgend[0] = t1->plalign[0] = '-';
812 t1->plrole[1] = t1->plrace[1] = t1->plgend[1] = t1->plalign[1] = 0;
813 t1->birthdate = t1->deathdate = yyyymmdd((time_t) 0L);
814 Strcpy(t1->name, "@");
815 Strcpy(t1->death, "<eod>\n");
816 writeentry(rfile, t1);
817 (void) fflush(rfile);
818 #endif /* TRUNCATE_FILE */
820 #endif /* UPDATE_RECORD_IN_PLACE */
821 (void) fclose(rfile);
823 free_ttlist(tt_head);
826 if (iflags.toptenwin && !done_stopprint)
827 display_nhwindow(toptenwin, 1);
831 if (iflags.toptenwin) {
832 destroy_nhwindow(toptenwin);
843 Strcpy(linebuf, " No Points Name");
845 while (bp < linebuf + COLNO - 9)
847 Strcpy(bp, "Hp [max]");
848 topten_print(linebuf);
851 /* so>0: standout line; so=0: ordinary line */
853 outentry(rank, t1, so)
854 struct toptenentry *t1;
858 boolean second_line = TRUE;
861 char *bp, hpbuf[24], linebuf3[BUFSZ];
882 Sprintf(eos(linebuf), "%3d", rank);
884 Strcat(linebuf, " ");
886 Sprintf(eos(linebuf), " %10ld %.10s", t1->points ? t1->points : u.urexp,
888 Sprintf(eos(linebuf), "-%s", t1->plrole);
889 if (t1->plrace[0] != '?')
890 Sprintf(eos(linebuf), "-%s", t1->plrace);
891 /* Printing of gender and alignment is intentional. It has been
892 * part of the NetHack Geek Code, and illustrates a proper way to
893 * specify a character from the command line.
895 Sprintf(eos(linebuf), "-%s", t1->plgend);
896 if (t1->plalign[0] != '?')
898 Sprintf(eos(linebuf), "-%s ", t1->plalign);
900 Sprintf(eos(linebuf), "-%s", t1->plalign);
903 Strcat(linebuf, " ");
907 Strcat(linebuf, "
\82Í");
908 /*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 */
910 if (!strncmp(jdeath, "
\96\82\8f\9c\82¯
\82ð
\8eè
\82É", 12))
912 else if (!strncmp(jdeath, "
\93V
\8fã
\82Å
\92p
\90J
\82ð
\8eó
\82¯", 16))
914 else if (!strncmp(jdeath, "
\8bU
\95¨
\82Ì
\96\82\8f\9c\82¯
\82ð
\92Í
\82Ü
\82³
\82ê", 24))
918 if (!strncmp("escaped", t1->death, 7)) {
920 if (!strncmp("
\92E
\8fo
\82µ
\82½", jdeath, 8)
921 || !strncmp("escaped", jdeath, 7)) {
924 Sprintf(eos(linebuf), "escaped the dungeon %s[max level %d]",
925 !strncmp(" (", t1->death + 7, 2) ? t1->death + 7 + 2 : "",
927 /* fixup for closing paren in "escaped... with...Amulet)[max..." */
928 if ((bp = index(linebuf, ')')) != 0)
929 *bp = (t1->deathdnum == astral_level.dnum) ? '\0' : ' ';
932 strncpy(jbuf, t1->death, jdeath - t1->death);
933 jbuf[jdeath - t1->death] = '\0';
934 Sprintf(action, "%s
\96À
\8b{
\82©
\82ç
\92E
\8fo
\82µ
\82½[
\8dÅ
\91å
\92n
\89º%d
\8aK]",
939 } else if (!strncmp("ascended", t1->death, 8)) {
941 } else if (!strncmp("
\8f¸
\93V
\82µ
\82½", jdeath, 8)
942 || !strncmp("ascended", jdeath, 8)) {
945 Sprintf(eos(linebuf), "ascended to demigod%s-hood",
946 (t1->plgend[0] == 'F') ? "dess" : "");
948 Sprintf(action, "
\8f¸
\93V
\82µ%s
\90_
\82Æ
\82È
\82Á
\82½",
949 (t1->plgend[0] == 'F') ? "
\8f\97" : "");
954 if (!strncmp(t1->death, "quit", 4)) {
956 if (!strncmp(jdeath, "
\94²
\82¯
\82½", 4)) {
958 Strcat(linebuf, "quit");
960 Strcat(action, t1->death);
966 } else if (!strncmp(t1->death, "died of st", 10)) {
967 Strcat(linebuf, "starved to death");
969 } else if (!strncmp(t1->death, "choked", 6)) {
970 Sprintf(eos(linebuf), "choked on h%s food",
971 (t1->plgend[0] == 'F') ? "er" : "is");
972 } else if (!strncmp(t1->death, "poisoned", 8)) {
973 Strcat(linebuf, "was poisoned");
974 } else if (!strncmp(t1->death, "crushed", 7)) {
975 Strcat(linebuf, "was crushed to death");
976 } else if (!strncmp(t1->death, "petrified by ", 13)) {
977 Strcat(linebuf, "turned to stone");
979 Strcat(linebuf, "died");
982 if (t1->deathdnum == astral_level.dnum) {
984 const char *arg, *fmt = " on the Plane of %s";
989 switch (t1->deathlev) {
992 fmt = " on the %s Plane";
997 arg = "
\93V
\8fã
\8aE";
1003 arg = "
\90\85\82Ì
\90¸
\97ì
\8aE";
1009 arg = "
\89Î
\82Ì
\90¸
\97ì
\8aE";
1015 arg = "
\95\97\82Ì
\90¸
\97ì
\8aE";
1021 arg = "
\92n
\82Ì
\90¸
\97ì
\8aE";
1028 Sprintf(eos(linebuf), fmt, arg);
1030 Sprintf(where, "%s
\82É
\82Ä", arg);
1034 Sprintf(eos(linebuf), " in %s", dungeons[t1->deathdnum].dname);
1036 Sprintf(eos(linebuf), "%s", dungeons[t1->deathdnum].dname);
1037 if (t1->deathdnum != knox_level.dnum)
1039 Sprintf(eos(linebuf), " on level %d", t1->deathlev);
1041 Sprintf(eos(linebuf), "
\82Ì
\92n
\89º%d
\8aK
\82É
\82Ä", t1->deathlev);
1042 if (t1->deathlev != t1->maxlvl)
1044 Sprintf(eos(linebuf), " [max %d]", t1->maxlvl);
1046 Sprintf(eos(where), "[
\8dÅ
\91å
\92n
\89º%d
\8aK]", t1->maxlvl);
1049 #if 0 /*JP*//*
\93ú
\96{
\8cê
\82Å
\82Í
\8d×
\8dH
\95s
\97v */
1050 /* kludge for "quit while already on Charon's boat" */
1051 if (!strncmp(t1->death, "quit ", 5))
1052 Strcat(linebuf, t1->death + 4);
1056 Strcat(linebuf, ".");
1059 /* Quit, starved, ascended, and escaped contain no second line */
1062 Sprintf(eos(linebuf), " %c%s.", highc(*(t1->death)), t1->death + 1);
1064 Sprintf(action, "%s", t1->death);
1067 Sprintf(eos(linebuf), "%s%s%s
\81D", who, where, action);
1069 lngr = (int) strlen(linebuf);
1071 hpbuf[0] = '-', hpbuf[1] = '\0';
1073 Sprintf(hpbuf, "%d", t1->hp);
1074 /* beginning of hp column after padding (not actually padded yet) */
1075 hppos = COLNO - (sizeof(" Hp [max]") - 1); /* sizeof(str) includes \0 */
1077 while (lngr >= hppos) {
1078 /*JP hppos
\82æ
\82è
\91O
\82Ì
\93K
\93\96\82È
\88Ê
\92u
\82Å
\95ª
\8a\84\82·
\82é
\81D*/
1081 split_japanese(linebuf, car, cdr, hppos);
1085 while (bp < car + (COLNO-1)) *bp++ = ' ';
1087 topten_print_bold(car);
1091 Sprintf(linebuf, "%15s %s", "", cdr);
1092 lngr = (int)strlen(linebuf);
1094 /*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*/
1096 while (lngr >= hppos) {
1097 for (bp = eos(linebuf); !(*bp == ' ' && (bp - linebuf < hppos)); bp--)
1099 /* special case: word is too long, wrap in the middle */
1100 if (linebuf + 15 >= bp)
1101 bp = linebuf + hppos - 1;
1102 /* special case: if about to wrap in the middle of maximum
1103 dungeon depth reached, wrap in front of it instead */
1104 if (bp > linebuf + 5 && !strncmp(bp - 5, " [max", 5))
1107 Strcpy(linebuf3, bp);
1109 Strcpy(linebuf3, bp + 1);
1112 while (bp < linebuf + (COLNO - 1))
1115 topten_print_bold(linebuf);
1117 topten_print(linebuf);
1118 Sprintf(linebuf, "%15s %s", "", linebuf3);
1119 lngr = strlen(linebuf);
1122 /* beginning of hp column not including padding */
1123 hppos = COLNO - 7 - (int) strlen(hpbuf);
1126 if (bp <= linebuf + hppos) {
1127 /* pad any necessary blanks to the hit point entry */
1128 while (bp < linebuf + hppos)
1131 Sprintf(eos(bp), " %s[%d]",
1132 (t1->maxhp < 10) ? " " : (t1->maxhp < 100) ? " " : "",
1140 while (bp < linebuf + so)
1143 topten_print_bold(linebuf);
1145 topten_print(linebuf);
1149 score_wanted(current_ver, rank, t1, playerct, players, uid)
1150 boolean current_ver;
1152 struct toptenentry *t1;
1154 const char **players;
1160 && (t1->ver_major != VERSION_MAJOR || t1->ver_minor != VERSION_MINOR
1161 || t1->patchlevel != PATCHLEVEL))
1164 if (sysopt.pers_is_uid && !playerct && t1->uid == uid)
1167 for (i = 0; i < playerct; i++) {
1168 if (players[i][0] == '-' && index("pr", players[i][1])
1169 && players[i][2] == 0 && i + 1 < playerct) {
1170 const char *arg = players[i + 1];
1171 if ((players[i][1] == 'p'
1172 && str2role(arg) == str2role(t1->plrole))
1173 || (players[i][1] == 'r'
1174 && str2race(arg) == str2race(t1->plrace)))
1177 } else if (strcmp(players[i], "all") == 0
1178 || strncmp(t1->name, players[i], NAMSZ) == 0
1179 || (players[i][0] == '-' && players[i][1] == t1->plrole[0]
1180 && players[i][2] == 0)
1181 || (digit(players[i][0]) && rank <= atoi(players[i])))
1188 * print selected parts of score list.
1189 * argc >= 2, with argv[0] untrustworthy (directory names, et al.),
1190 * and argv[1] starting with "-s".
1197 const char **players;
1199 boolean current_ver = TRUE, init_done = FALSE;
1200 register struct toptenentry *t1;
1202 boolean match_found = FALSE;
1206 const char *player0;
1208 if (argc < 2 || strncmp(argv[1], "-s", 2)) {
1209 raw_printf("prscore: bad arguments (%d)", argc);
1213 rfile = fopen_datafile(RECORD, "r", SCOREPREFIX);
1215 raw_print("Cannot open record file!");
1221 extern winid amii_rawprwin;
1223 init_nhwindows(&argc, argv);
1224 amii_rawprwin = create_nhwindow(NHW_TEXT);
1228 /* If the score list isn't after a game, we never went through
1229 * initialization. */
1230 if (wiz1_level.dlevel == 0) {
1236 if (!argv[1][2]) { /* plain "-s" */
1242 if (argc > 1 && !strcmp(argv[1], "-v")) {
1243 current_ver = FALSE;
1249 if (sysopt.pers_is_uid) {
1252 players = (const char **) 0;
1257 player0 = "all"; /* single user system */
1259 player0 = "hackplayer";
1267 players = (const char **) ++argv;
1272 players = (const char **)alloc(sizeof(char *) * argc + 1);
1273 for (i = 0; i < argc; i++) {
1274 char *p = (char *)str2ic(argv[i]);
1275 players[i] = (char *)alloc(strlen(p) + 1);
1276 strcpy((void *)players[i], p);
1283 t1 = tt_head = newttentry();
1284 for (rank = 1;; rank++) {
1285 readentry(rfile, t1);
1286 if (t1->points == 0)
1289 && score_wanted(current_ver, rank, t1, playerct, players, uid))
1291 t1->tt_next = newttentry();
1295 (void) fclose(rfile);
1304 for (rank = 1; t1->points != 0; rank++, t1 = t1->tt_next) {
1305 if (score_wanted(current_ver, rank, t1, playerct, players, uid))
1306 (void) outentry(rank, t1, FALSE);
1309 Sprintf(pbuf, "Cannot find any %sentries for ",
1310 current_ver ? "current " : "");
1312 Strcat(pbuf, "you.");
1315 Strcat(pbuf, "any of ");
1316 for (i = 0; i < playerct; i++) {
1317 /* stop printing players if there are too many to fit */
1318 if (strlen(pbuf) + strlen(players[i]) + 2 >= BUFSZ) {
1319 if (strlen(pbuf) < BUFSZ - 4)
1320 Strcat(pbuf, "...");
1322 Strcpy(pbuf + strlen(pbuf) - 4, "...");
1325 Strcat(pbuf, players[i]);
1326 if (i < playerct - 1) {
1327 if (players[i][0] == '-' && index("pr", players[i][1])
1328 && players[i][2] == 0)
1336 raw_printf("Usage: %s -s [-v] <playertypes> [maxrank] [playernames]",
1339 raw_printf("Player types are: [-p role] [-r race]");
1341 free_ttlist(tt_head);
1344 extern winid amii_rawprwin;
1346 display_nhwindow(amii_rawprwin, 1);
1347 destroy_nhwindow(amii_rawprwin);
1348 amii_rawprwin = WIN_ERR;
1360 /* Look for this role in the role table */
1361 for (i = 0; roles[i].name.m; i++)
1362 if (!strncmp(plch, roles[i].filecode, ROLESZ)) {
1363 if (fem && roles[i].femalenum != NON_PM)
1364 return roles[i].femalenum;
1365 else if (roles[i].malenum != NON_PM)
1366 return roles[i].malenum;
1370 /* this might be from a 3.2.x score for former Elf class */
1371 if (!strcmp(plch, "E"))
1374 impossible("What weird role is this? (%s)", plch);
1375 return PM_HUMAN_MUMMY;
1379 * Get a random player name and class from the high score list,
1381 struct toptenentry *
1382 get_rnd_toptenentry()
1386 register struct toptenentry *tt;
1387 static struct toptenentry tt_buf;
1389 rfile = fopen_datafile(RECORD, "r", SCOREPREFIX);
1391 impossible("Cannot open record file!");
1396 rank = rnd(sysopt.tt_oname_maxrank);
1398 for (i = rank; i; i--) {
1399 readentry(rfile, tt);
1400 if (tt->points == 0)
1404 if (tt->points == 0) {
1413 (void) fclose(rfile);
1419 * Attach random player name and class from high score list
1420 * to an object (for statues or morgue corpses).
1426 struct toptenentry *tt;
1428 return (struct obj *) 0;
1430 tt = get_rnd_toptenentry();
1433 return (struct obj *) 0;
1435 set_corpsenm(otmp, classmon(tt->plrole, (tt->plgend[0] == 'F')));
1436 otmp = oname(otmp, tt->name);
1441 #ifdef NO_SCAN_BRACK
1442 /* Lattice scanf isn't up to reading the scorefile. What */
1443 /* follows deals with that; I admit it's ugly. (KL) */
1444 /* Now generally available (KL) */
1449 while ((p = index(p, ' ')) != 0)
1457 while ((p = index(p, '|')) != 0)
1460 #endif /* NO_SCAN_BRACK */