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 */
114 char *kname = killer.name;
116 buf[0] = '\0'; /* so strncat() can find the end */
117 #if 1 /*JP*//*
\90æ
\82É
\91Î
\8fÛ
\82ð
\83R
\83s
\81[*/
118 strncat(buf, kname, siz - 1);
121 switch (killer.format) {
123 impossible("bad killer format? (%d)", killer.format);
125 case NO_KILLER_PREFIX:
128 #if 0 /*JP*//*
\93ú
\96{
\8cê
\82Å
\82Í
\95s
\97v*/
134 (void) strncat(buf, killed_by_prefix[how], siz - 1);
137 #else /*JP:
\8aù
\82É
\91Î
\8fÛ
\82ð
\83R
\83s
\81[
\82µ
\82Ä
\82¢
\82é
\82Ì
\82Å
\92P
\82É
\92Ç
\89Á*/
138 (void) strncat(buf, killed_by_prefix[how], siz - 1);
143 (void) strncat(buf, "
\82É
\8eE
\82³
\82ê
\82½", siz - 1);
146 #if 0 /*JP*//*
\8aù
\82É
\83R
\83s
\81[
\8dÏ
\82Ý*/
147 /* we're writing into buf[0] (after possibly advancing buf) rather than
148 appending, but strncat() appends a terminator and strncpy() doesn't */
149 (void) strncat(buf, kname, siz - 1);
157 if (toptenwin == WIN_ERR)
160 putstr(toptenwin, ATR_NONE, x);
167 if (toptenwin == WIN_ERR)
170 putstr(toptenwin, ATR_BOLD, x);
174 observable_depth(lev)
178 /* if we ever randomize the order of the elemental planes, we
179 must use a constant external representation in the record file */
180 if (In_endgame(lev)) {
181 if (Is_astralevel(lev))
183 else if (Is_waterlevel(lev))
185 else if (Is_firelevel(lev))
187 else if (Is_airlevel(lev))
189 else if (Is_earthlevel(lev))
198 /* throw away characters until current record has been entirely consumed */
207 } while (c != '\n' && c != EOF);
213 struct toptenentry *tt;
215 char inbuf[SCANBUFSZ], s1[SCANBUFSZ], s2[SCANBUFSZ], s3[SCANBUFSZ],
216 s4[SCANBUFSZ], s5[SCANBUFSZ], s6[SCANBUFSZ];
218 #ifdef NO_SCAN_BRACK /* Version_ Pts DgnLevs_ Hp___ Died__Born id */
219 static const char fmt[] = "%d %d %d %ld %d %d %d %d %d %d %ld %ld %d%*c";
220 static const char fmt32[] = "%c%c %s %s%*c";
221 static const char fmt33[] = "%s %s %s %s %s %s%*c";
223 static const char fmt[] = "%d.%d.%d %ld %d %d %d %d %d %d %ld %ld %d ";
224 static const char fmt32[] = "%c%c %[^,],%[^\n]%*c";
225 static const char fmt33[] = "%s %s %s %s %[^,],%[^\n]%*c";
228 #ifdef UPDATE_RECORD_IN_PLACE
229 /* note: input below must read the record's terminating newline */
230 final_fpos = tt->fpos = ftell(rfile);
233 if (fscanf(rfile, fmt, &tt->ver_major, &tt->ver_minor, &tt->patchlevel,
234 &tt->points, &tt->deathdnum, &tt->deathlev, &tt->maxlvl,
235 &tt->hp, &tt->maxhp, &tt->deaths, &tt->deathdate,
236 &tt->birthdate, &tt->uid) != TTFIELDS) {
239 discardexcess(rfile);
241 /* load remainder of record into a local buffer;
242 this imposes an implicit length limit of SCANBUFSZ
243 on every string field extracted from the buffer */
244 if (!fgets(inbuf, sizeof inbuf, rfile)) {
245 /* sscanf will fail and tt->points will be set to 0 */
247 } else if (!index(inbuf, '\n')) {
248 Strcpy(&inbuf[sizeof inbuf - 2], "\n");
249 discardexcess(rfile);
251 /* Check for backwards compatibility */
252 if (tt->ver_major < 3 || (tt->ver_major == 3 && tt->ver_minor < 3)) {
255 if (sscanf(inbuf, fmt32, tt->plrole, tt->plgend, s1, s2) == 4) {
256 tt->plrole[1] = tt->plgend[1] = '\0'; /* read via %c */
257 copynchars(tt->name, s1, (int) (sizeof tt->name) - 1);
258 copynchars(tt->death, s2, (int) (sizeof tt->death) - 1);
261 tt->plrole[1] = '\0';
262 if ((i = str2role(tt->plrole)) >= 0)
263 Strcpy(tt->plrole, roles[i].filecode);
264 Strcpy(tt->plrace, "?");
265 Strcpy(tt->plgend, (tt->plgend[0] == 'M') ? "Mal" : "Fem");
266 Strcpy(tt->plalign, "?");
267 } else if (sscanf(inbuf, fmt33, s1, s2, s3, s4, s5, s6) == 6) {
268 copynchars(tt->plrole, s1, (int) (sizeof tt->plrole) - 1);
269 copynchars(tt->plrace, s2, (int) (sizeof tt->plrace) - 1);
270 copynchars(tt->plgend, s3, (int) (sizeof tt->plgend) - 1);
271 copynchars(tt->plalign, s4, (int) (sizeof tt->plalign) - 1);
272 copynchars(tt->name, s5, (int) (sizeof tt->name) - 1);
273 copynchars(tt->death, s6, (int) (sizeof tt->death) - 1);
277 if (tt->points > 0) {
278 nsb_unmung_line(tt->name);
279 nsb_unmung_line(tt->death);
284 /* check old score entries for Y2K problem and fix whenever found */
285 if (tt->points > 0) {
286 if (tt->birthdate < 19000000L)
287 tt->birthdate += 19000000L;
288 if (tt->deathdate < 19000000L)
289 tt->deathdate += 19000000L;
294 writeentry(rfile, tt)
296 struct toptenentry *tt;
298 static const char fmt32[] = "%c%c "; /* role,gender */
299 static const char fmt33[] = "%s %s %s %s "; /* role,race,gndr,algn */
300 #ifndef NO_SCAN_BRACK
301 static const char fmt0[] = "%d.%d.%d %ld %d %d %d %d %d %d %ld %ld %d ";
302 static const char fmtX[] = "%s,%s%s%s\n";
303 #else /* NO_SCAN_BRACK */
304 static const char fmt0[] = "%d %d %d %ld %d %d %d %d %d %d %ld %ld %d ";
305 static const char fmtX[] = "%s %s%s%s\n";
307 nsb_mung_line(tt->name);
308 nsb_mung_line(tt->death);
311 (void) fprintf(rfile, fmt0, tt->ver_major, tt->ver_minor, tt->patchlevel,
312 tt->points, tt->deathdnum, tt->deathlev, tt->maxlvl,
313 tt->hp, tt->maxhp, tt->deaths, tt->deathdate,
314 tt->birthdate, tt->uid);
315 if (tt->ver_major < 3 || (tt->ver_major == 3 && tt->ver_minor < 3))
316 (void) fprintf(rfile, fmt32, tt->plrole[0], tt->plgend[0]);
318 (void) fprintf(rfile, fmt33, tt->plrole, tt->plrace, tt->plgend,
320 (void) fprintf(rfile, fmtX, onlyspace(tt->name) ? "_" : tt->name,
322 (multi ? ", while " : ""),
323 (multi ? (multi_reason ? multi_reason : "helpless") : ""));
326 nsb_unmung_line(tt->name);
327 nsb_unmung_line(tt->death);
331 /* as tab is never used in eg. plname or death, no need to mangle those. */
333 writexlentry(rfile, tt)
335 struct toptenentry *tt;
337 #define Fprintf (void) fprintf
338 #define XLOG_SEP '\t' /* xlogfile field separator. */
341 Sprintf(buf, "version=%d.%d.%d", tt->ver_major, tt->ver_minor,
343 Sprintf(eos(buf), "%cpoints=%ld%cdeathdnum=%d%cdeathlev=%d", XLOG_SEP,
344 tt->points, XLOG_SEP, tt->deathdnum, XLOG_SEP, tt->deathlev);
345 Sprintf(eos(buf), "%cmaxlvl=%d%chp=%d%cmaxhp=%d", XLOG_SEP, tt->maxlvl,
346 XLOG_SEP, tt->hp, XLOG_SEP, tt->maxhp);
347 Sprintf(eos(buf), "%cdeaths=%d%cdeathdate=%ld%cbirthdate=%ld%cuid=%d",
348 XLOG_SEP, tt->deaths, XLOG_SEP, tt->deathdate, XLOG_SEP,
349 tt->birthdate, XLOG_SEP, tt->uid);
350 Fprintf(rfile, "%s", buf);
351 Sprintf(buf, "%crole=%s%crace=%s%cgender=%s%calign=%s", XLOG_SEP,
352 tt->plrole, XLOG_SEP, tt->plrace, XLOG_SEP, tt->plgend, XLOG_SEP,
354 Fprintf(rfile, "%s%cname=%s%cdeath=%s",
355 buf, /* (already includes separator) */
356 XLOG_SEP, plname, XLOG_SEP, tt->death);
358 Fprintf(rfile, "%cwhile=%s", XLOG_SEP,
359 multi_reason ? multi_reason : "helpless");
360 Fprintf(rfile, "%cconduct=0x%lx%cturns=%ld%cachieve=0x%lx", XLOG_SEP,
361 encodeconduct(), XLOG_SEP, moves, XLOG_SEP, encodeachieve());
362 Fprintf(rfile, "%crealtime=%ld%cstarttime=%ld%cendtime=%ld", XLOG_SEP,
363 (long) urealtime.realtime, XLOG_SEP,
364 (long) ubirthday, XLOG_SEP, (long) urealtime.finish_time);
365 Fprintf(rfile, "%cgender0=%s%calign0=%s", XLOG_SEP,
366 genders[flags.initgend].filecode, XLOG_SEP,
367 aligns[1 - u.ualignbase[A_ORIGINAL]].filecode);
368 Fprintf(rfile, "%cflags=0x%lx", XLOG_SEP, encodexlogflags());
369 Fprintf(rfile, "\n");
382 if (!u.uroleplay.numbones)
393 if (!u.uconduct.food)
395 if (!u.uconduct.unvegan)
397 if (!u.uconduct.unvegetarian)
399 if (!u.uconduct.gnostic)
401 if (!u.uconduct.weaphit)
403 if (!u.uconduct.killer)
405 if (!u.uconduct.literate)
407 if (!u.uconduct.polypiles)
409 if (!u.uconduct.polyselfs)
411 if (!u.uconduct.wishes)
413 if (!u.uconduct.wisharti)
415 if (!num_genocides())
428 if (u.uachieve.enter_gehennom)
430 if (u.uachieve.menorah)
434 if (u.uevent.invoked)
436 if (u.uachieve.amulet)
438 if (In_endgame(&u.uz))
440 if (Is_astralevel(&u.uz))
442 if (u.uachieve.ascended)
444 if (u.uachieve.mines_luckstone)
446 if (u.uachieve.finish_sokoban)
448 if (u.uachieve.killed_medusa)
450 if (u.uroleplay.blind)
452 if (u.uroleplay.nudist)
460 struct toptenentry *tt;
462 struct toptenentry *ttnext;
464 while (tt->points > 0) {
465 ttnext = tt->tt_next;
478 int rank, rank0 = -1, rank1 = 0;
479 int occ_cnt = sysopt.persmax;
480 register struct toptenentry *t0, *tprev;
481 struct toptenentry *t1;
483 register int flg = 0;
490 #endif /* XLOGFILE */
493 /* Under DICE 3.0, this crashes the system consistently, apparently due to
494 * corruption of *rfile somewhere. Until I figure this out, just cut out
495 * topten support entirely - at least then the game exits cleanly. --AC
500 /* If we are in the midst of a panic, cut out topten entirely.
501 * topten uses alloc() several times, which will lead to
502 * problems if the panic was the result of an alloc() failure.
504 if (program_state.panicking)
507 if (iflags.toptenwin) {
508 toptenwin = create_nhwindow(NHW_TEXT);
511 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
512 #define HUP if (!program_state.done_hup)
518 restore_colors(); /* make sure the screen is black on white */
520 /* create a new 'topten' entry */
523 t0->ver_major = VERSION_MAJOR;
524 t0->ver_minor = VERSION_MINOR;
525 t0->patchlevel = PATCHLEVEL;
526 t0->points = u.urexp;
527 t0->deathdnum = u.uz.dnum;
528 /* deepest_lev_reached() is in terms of depth(), and reporting the
529 * deepest level reached in the dungeon death occurred in doesn't
530 * seem right, so we have to report the death level in depth() terms
531 * as well (which also seems reasonable since that's all the player
532 * sees on the screen anyway)
534 t0->deathlev = observable_depth(&u.uz);
535 t0->maxlvl = deepest_lev_reached(TRUE);
537 t0->maxhp = u.uhpmax;
538 t0->deaths = u.umortality;
540 copynchars(t0->plrole, urole.filecode, ROLESZ);
541 copynchars(t0->plrace, urace.filecode, ROLESZ);
542 copynchars(t0->plgend, genders[flags.female].filecode, ROLESZ);
543 copynchars(t0->plalign, aligns[1 - u.ualign.type].filecode, ROLESZ);
544 copynchars(t0->name, plname, NAMSZ);
545 formatkiller(t0->death, sizeof t0->death, how);
546 t0->birthdate = yyyymmdd(ubirthday);
547 t0->deathdate = yyyymmdd(when);
549 #ifdef UPDATE_RECORD_IN_PLACE
553 #ifdef LOGFILE /* used for debugging (who dies of what, where) */
554 if (lock_file(LOGFILE, SCOREPREFIX, 10)) {
555 if (!(lfile = fopen_datafile(LOGFILE, "a", SCOREPREFIX))) {
556 HUP raw_print("Cannot open log file!");
558 writeentry(lfile, t0);
559 (void) fclose(lfile);
561 unlock_file(LOGFILE);
565 if (lock_file(XLOGFILE, SCOREPREFIX, 10)) {
566 if (!(xlfile = fopen_datafile(XLOGFILE, "a", SCOREPREFIX))) {
567 HUP raw_print("Cannot open extended log file!");
569 writexlentry(xlfile, t0);
570 (void) fclose(xlfile);
572 unlock_file(XLOGFILE);
574 #endif /* XLOGFILE */
576 if (wizard || discover) {
583 "Since you were in %s mode, the score list will not be checked.",
584 wizard ? "wizard" : "discover");
586 "%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",
587 wizard ? "
\83E
\83B
\83U
\81[
\83h" : "
\94
\8c©");
594 if (!lock_file(RECORD, SCOREPREFIX, 60))
597 #ifdef UPDATE_RECORD_IN_PLACE
598 rfile = fopen_datafile(RECORD, "r+", SCOREPREFIX);
600 rfile = fopen_datafile(RECORD, "r", SCOREPREFIX);
604 HUP raw_print("Cannot open record file!");
609 HUP topten_print("");
611 /* assure minimum number of points */
612 if (t0->points < sysopt.pointsmin)
615 t1 = tt_head = newttentry();
617 /* rank0: -1 undefined, 0 not_on_list, n n_th on list */
619 readentry(rfile, t1);
620 if (t1->points < sysopt.pointsmin)
622 if (rank0 < 0 && t1->points < t0->points) {
629 #ifdef UPDATE_RECORD_IN_PLACE
630 t0->fpos = t1->fpos; /* insert here */
634 flg++; /* ask for a rewrite */
640 if ((sysopt.pers_is_uid ? t1->uid == t0->uid
641 : strncmp(t1->name, t0->name, NAMSZ) == 0)
642 && !strncmp(t1->plrole, t0->plrole, ROLESZ) && --occ_cnt <= 0) {
651 "You didn't beat your previous score of %ld points.",
653 "
\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",
664 if (rank <= sysopt.entrymax) {
665 t1->tt_next = newttentry();
669 if (rank > sysopt.entrymax) {
674 if (flg) { /* rewrite record file */
675 #ifdef UPDATE_RECORD_IN_PLACE
676 (void) fseek(rfile, (t0->fpos >= 0 ? t0->fpos : final_fpos),
679 (void) fclose(rfile);
680 if (!(rfile = fopen_datafile(RECORD, "w", SCOREPREFIX))) {
681 HUP raw_print("Cannot write record file");
683 free_ttlist(tt_head);
686 #endif /* UPDATE_RECORD_IN_PLACE */
691 topten_print("You made the top ten list!");
693 topten_print("
\82 \82È
\82½
\82Í
\83g
\83b
\83v10
\83\8a\83X
\83g
\82É
\8dÚ
\82Á
\82½
\81I");
699 "You reached the %d%s place on the top %d list.",
700 rank0, ordin(rank0), sysopt.entrymax);
703 "
\82 \82È
\82½
\82Í
\81C
\83g
\83b
\83v%d
\83\8a\83X
\83g
\82Ì%d
\88Ê
\82É
\8dÚ
\82Á
\82½
\81D",
704 sysopt.entrymax, rank0);
718 for (rank = 1; t1->points != 0; rank++, t1 = t1->tt_next) {
720 #ifdef UPDATE_RECORD_IN_PLACE
724 writeentry(rfile, t1);
727 if (rank > flags.end_top && (rank < rank0 - flags.end_around
728 || rank > rank0 + flags.end_around)
730 || (sysopt.pers_is_uid
732 : strncmp(t1->name, t0->name, NAMSZ) == 0)))
734 if (rank == rank0 - flags.end_around
735 && rank0 > flags.end_top + flags.end_around + 1 && !flags.end_own)
738 outentry(rank, t1, FALSE);
740 outentry(rank, t1, TRUE);
742 outentry(rank, t1, TRUE);
743 outentry(0, t0, TRUE);
748 outentry(0, t0, TRUE);
749 #ifdef UPDATE_RECORD_IN_PLACE
752 /* if a reasonable way to truncate a file exists, use it */
753 truncate_file(rfile);
755 /* use sentinel record rather than relying on truncation */
756 t1->points = 0L; /* terminates file when read back in */
757 t1->ver_major = t1->ver_minor = t1->patchlevel = 0;
758 t1->uid = t1->deathdnum = t1->deathlev = 0;
759 t1->maxlvl = t1->hp = t1->maxhp = t1->deaths = 0;
760 t1->plrole[0] = t1->plrace[0] = t1->plgend[0] = t1->plalign[0] = '-';
761 t1->plrole[1] = t1->plrace[1] = t1->plgend[1] = t1->plalign[1] = 0;
762 t1->birthdate = t1->deathdate = yyyymmdd((time_t) 0L);
763 Strcpy(t1->name, "@");
764 Strcpy(t1->death, "<eod>\n");
765 writeentry(rfile, t1);
766 (void) fflush(rfile);
767 #endif /* TRUNCATE_FILE */
769 #endif /* UPDATE_RECORD_IN_PLACE */
770 (void) fclose(rfile);
772 free_ttlist(tt_head);
775 if (iflags.toptenwin && !done_stopprint)
776 display_nhwindow(toptenwin, 1);
780 if (iflags.toptenwin) {
781 destroy_nhwindow(toptenwin);
792 Strcpy(linebuf, " No Points Name");
794 while (bp < linebuf + COLNO - 9)
796 Strcpy(bp, "Hp [max]");
797 topten_print(linebuf);
800 /* so>0: standout line; so=0: ordinary line */
802 outentry(rank, t1, so)
803 struct toptenentry *t1;
807 boolean second_line = TRUE;
809 char *bp, hpbuf[24], linebuf3[BUFSZ];
827 Sprintf(eos(linebuf), "%3d", rank);
829 Strcat(linebuf, " ");
831 Sprintf(eos(linebuf), " %10ld %.10s", t1->points ? t1->points : u.urexp,
833 Sprintf(eos(linebuf), "-%s", t1->plrole);
834 if (t1->plrace[0] != '?')
835 Sprintf(eos(linebuf), "-%s", t1->plrace);
836 /* Printing of gender and alignment is intentional. It has been
837 * part of the NetHack Geek Code, and illustrates a proper way to
838 * specify a character from the command line.
840 Sprintf(eos(linebuf), "-%s", t1->plgend);
841 if (t1->plalign[0] != '?')
843 Sprintf(eos(linebuf), "-%s ", t1->plalign);
845 Sprintf(eos(linebuf), "-%s", t1->plalign);
848 Strcat(linebuf, " ");
852 Strcat(linebuf, "
\82Í");
853 /*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 */
855 if (!strncmp(jdeath, "
\96\82\8f\9c\82¯
\82ð
\8eè
\82É", 12))
857 else if (!strncmp(jdeath, "
\93V
\8fã
\82Å
\92p
\90J
\82ð
\8eó
\82¯", 16))
859 else if (!strncmp(jdeath, "
\8bU
\95¨
\82Ì
\96\82\8f\9c\82¯
\82ð
\92Í
\82Ü
\82³
\82ê", 24))
863 if (!strncmp("escaped", t1->death, 7)) {
865 if (!strncmp("
\92E
\8fo
\82µ
\82½", jdeath, 8)
866 || !strncmp("escaped", jdeath, 7)) {
869 Sprintf(eos(linebuf), "escaped the dungeon %s[max level %d]",
870 !strncmp(" (", t1->death + 7, 2) ? t1->death + 7 + 2 : "",
872 /* fixup for closing paren in "escaped... with...Amulet)[max..." */
873 if ((bp = index(linebuf, ')')) != 0)
874 *bp = (t1->deathdnum == astral_level.dnum) ? '\0' : ' ';
877 strncpy(jbuf, t1->death, jdeath - t1->death);
878 jbuf[jdeath - t1->death] = '\0';
879 Sprintf(action, "%s
\96À
\8b{
\82©
\82ç
\92E
\8fo
\82µ
\82½[
\8dÅ
\91å
\92n
\89º%d
\8aK]",
884 } else if (!strncmp("ascended", t1->death, 8)) {
886 } else if (!strncmp("
\8f¸
\93V
\82µ
\82½", jdeath, 8)
887 || !strncmp("ascended", jdeath, 8)) {
890 Sprintf(eos(linebuf), "ascended to demigod%s-hood",
891 (t1->plgend[0] == 'F') ? "dess" : "");
893 Sprintf(action, "
\8f¸
\93V
\82µ%s
\90_
\82Æ
\82È
\82Á
\82½",
894 (t1->plgend[0] == 'F') ? "
\8f\97" : "");
899 if (!strncmp(t1->death, "quit", 4)) {
901 if (!strncmp(jdeath, "
\94²
\82¯
\82½", 4)) {
903 Strcat(linebuf, "quit");
905 Strcat(action, t1->death);
911 } else if (!strncmp(t1->death, "died of st", 10)) {
912 Strcat(linebuf, "starved to death");
914 } else if (!strncmp(t1->death, "choked", 6)) {
915 Sprintf(eos(linebuf), "choked on h%s food",
916 (t1->plgend[0] == 'F') ? "er" : "is");
917 } else if (!strncmp(t1->death, "poisoned", 8)) {
918 Strcat(linebuf, "was poisoned");
919 } else if (!strncmp(t1->death, "crushed", 7)) {
920 Strcat(linebuf, "was crushed to death");
921 } else if (!strncmp(t1->death, "petrified by ", 13)) {
922 Strcat(linebuf, "turned to stone");
924 Strcat(linebuf, "died");
927 if (t1->deathdnum == astral_level.dnum) {
928 const char *arg, *fmt = " on the Plane of %s";
930 switch (t1->deathlev) {
933 fmt = " on the %s Plane";
938 arg = "
\93V
\8fã
\8aE";
944 arg = "
\90\85\82Ì
\90¸
\97ì
\8aE";
950 arg = "
\89Î
\82Ì
\90¸
\97ì
\8aE";
956 arg = "
\95\97\82Ì
\90¸
\97ì
\8aE";
962 arg = "
\92n
\82Ì
\90¸
\97ì
\8aE";
969 Sprintf(eos(linebuf), fmt, arg);
971 Sprintf(where, "%s
\82É
\82Ä", arg);
975 Sprintf(eos(linebuf), " in %s", dungeons[t1->deathdnum].dname);
977 Sprintf(eos(linebuf), "%s", dungeons[t1->deathdnum].dname);
978 if (t1->deathdnum != knox_level.dnum)
980 Sprintf(eos(linebuf), " on level %d", t1->deathlev);
982 Sprintf(eos(linebuf), "
\82Ì
\92n
\89º%d
\8aK
\82É
\82Ä", t1->deathlev);
983 if (t1->deathlev != t1->maxlvl)
985 Sprintf(eos(linebuf), " [max %d]", t1->maxlvl);
987 Sprintf(eos(where), "[
\8dÅ
\91å
\92n
\89º%d
\8aK]", t1->maxlvl);
990 /* kludge for "quit while already on Charon's boat" */
991 if (!strncmp(t1->death, "quit ", 5))
992 Strcat(linebuf, t1->death + 4);
995 Strcat(linebuf, ".");
998 /* Quit, starved, ascended, and escaped contain no second line */
1001 Sprintf(eos(linebuf), " %c%s.", highc(*(t1->death)), t1->death + 1);
1003 Sprintf(action, "%s", t1->death);
1006 Sprintf(eos(linebuf), "%s%s%s
\81D", who, where, action);
1008 lngr = (int) strlen(linebuf);
1010 hpbuf[0] = '-', hpbuf[1] = '\0';
1012 Sprintf(hpbuf, "%d", t1->hp);
1013 /* beginning of hp column after padding (not actually padded yet) */
1014 hppos = COLNO - (sizeof(" Hp [max]") - 1); /* sizeof(str) includes \0 */
1016 while(lngr >= hppos ){
1017 /*JP hppos
\82æ
\82è
\91O
\82Ì
\93K
\93\96\82È
\88Ê
\92u
\82Å
\95ª
\8a\84\82·
\82é
\81D*/
1020 split_japanese(linebuf, car, cdr, hppos);
1024 while (bp < car + (COLNO-1)) *bp++ = ' ';
1026 topten_print_bold(car);
1030 Sprintf(linebuf, "%15s %s", "", cdr);
1031 lngr = (int)strlen(linebuf);
1033 /*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*/
1035 while (lngr >= hppos) {
1036 for (bp = eos(linebuf); !(*bp == ' ' && (bp - linebuf < hppos)); bp--)
1038 /* special case: word is too long, wrap in the middle */
1039 if (linebuf + 15 >= bp)
1040 bp = linebuf + hppos - 1;
1041 /* special case: if about to wrap in the middle of maximum
1042 dungeon depth reached, wrap in front of it instead */
1043 if (bp > linebuf + 5 && !strncmp(bp - 5, " [max", 5))
1046 Strcpy(linebuf3, bp);
1048 Strcpy(linebuf3, bp + 1);
1051 while (bp < linebuf + (COLNO - 1))
1054 topten_print_bold(linebuf);
1056 topten_print(linebuf);
1057 Sprintf(linebuf, "%15s %s", "", linebuf3);
1058 lngr = strlen(linebuf);
1061 /* beginning of hp column not including padding */
1062 hppos = COLNO - 7 - (int) strlen(hpbuf);
1065 if (bp <= linebuf + hppos) {
1066 /* pad any necessary blanks to the hit point entry */
1067 while (bp < linebuf + hppos)
1070 Sprintf(eos(bp), " %s[%d]",
1071 (t1->maxhp < 10) ? " " : (t1->maxhp < 100) ? " " : "",
1079 while (bp < linebuf + so)
1082 topten_print_bold(linebuf);
1084 topten_print(linebuf);
1088 score_wanted(current_ver, rank, t1, playerct, players, uid)
1089 boolean current_ver;
1091 struct toptenentry *t1;
1093 const char **players;
1099 && (t1->ver_major != VERSION_MAJOR || t1->ver_minor != VERSION_MINOR
1100 || t1->patchlevel != PATCHLEVEL))
1103 if (sysopt.pers_is_uid && !playerct && t1->uid == uid)
1106 for (i = 0; i < playerct; i++) {
1107 if (players[i][0] == '-' && index("pr", players[i][1])
1108 && players[i][2] == 0 && i + 1 < playerct) {
1109 const char *arg = players[i + 1];
1110 if ((players[i][1] == 'p'
1111 && str2role(arg) == str2role(t1->plrole))
1112 || (players[i][1] == 'r'
1113 && str2race(arg) == str2race(t1->plrace)))
1116 } else if (strcmp(players[i], "all") == 0
1117 || strncmp(t1->name, players[i], NAMSZ) == 0
1118 || (players[i][0] == '-' && players[i][1] == t1->plrole[0]
1119 && players[i][2] == 0)
1120 || (digit(players[i][0]) && rank <= atoi(players[i])))
1127 * print selected parts of score list.
1128 * argc >= 2, with argv[0] untrustworthy (directory names, et al.),
1129 * and argv[1] starting with "-s".
1136 const char **players;
1138 boolean current_ver = TRUE, init_done = FALSE;
1139 register struct toptenentry *t1;
1141 boolean match_found = FALSE;
1145 const char *player0;
1147 if (argc < 2 || strncmp(argv[1], "-s", 2)) {
1148 raw_printf("prscore: bad arguments (%d)", argc);
1152 rfile = fopen_datafile(RECORD, "r", SCOREPREFIX);
1154 raw_print("Cannot open record file!");
1160 extern winid amii_rawprwin;
1162 init_nhwindows(&argc, argv);
1163 amii_rawprwin = create_nhwindow(NHW_TEXT);
1167 /* If the score list isn't after a game, we never went through
1168 * initialization. */
1169 if (wiz1_level.dlevel == 0) {
1175 if (!argv[1][2]) { /* plain "-s" */
1181 if (argc > 1 && !strcmp(argv[1], "-v")) {
1182 current_ver = FALSE;
1188 if (sysopt.pers_is_uid) {
1191 players = (const char **) 0;
1196 player0 = "all"; /* single user system */
1198 player0 = "hackplayer";
1205 players = (const char **) ++argv;
1209 t1 = tt_head = newttentry();
1210 for (rank = 1;; rank++) {
1211 readentry(rfile, t1);
1212 if (t1->points == 0)
1215 && score_wanted(current_ver, rank, t1, playerct, players, uid))
1217 t1->tt_next = newttentry();
1221 (void) fclose(rfile);
1230 for (rank = 1; t1->points != 0; rank++, t1 = t1->tt_next) {
1231 if (score_wanted(current_ver, rank, t1, playerct, players, uid))
1232 (void) outentry(rank, t1, FALSE);
1235 Sprintf(pbuf, "Cannot find any %sentries for ",
1236 current_ver ? "current " : "");
1238 Strcat(pbuf, "you.");
1241 Strcat(pbuf, "any of ");
1242 for (i = 0; i < playerct; i++) {
1243 /* stop printing players if there are too many to fit */
1244 if (strlen(pbuf) + strlen(players[i]) + 2 >= BUFSZ) {
1245 if (strlen(pbuf) < BUFSZ - 4)
1246 Strcat(pbuf, "...");
1248 Strcpy(pbuf + strlen(pbuf) - 4, "...");
1251 Strcat(pbuf, players[i]);
1252 if (i < playerct - 1) {
1253 if (players[i][0] == '-' && index("pr", players[i][1])
1254 && players[i][2] == 0)
1262 raw_printf("Usage: %s -s [-v] <playertypes> [maxrank] [playernames]",
1265 raw_printf("Player types are: [-p role] [-r race]");
1267 free_ttlist(tt_head);
1270 extern winid amii_rawprwin;
1272 display_nhwindow(amii_rawprwin, 1);
1273 destroy_nhwindow(amii_rawprwin);
1274 amii_rawprwin = WIN_ERR;
1286 /* Look for this role in the role table */
1287 for (i = 0; roles[i].name.m; i++)
1288 if (!strncmp(plch, roles[i].filecode, ROLESZ)) {
1289 if (fem && roles[i].femalenum != NON_PM)
1290 return roles[i].femalenum;
1291 else if (roles[i].malenum != NON_PM)
1292 return roles[i].malenum;
1296 /* this might be from a 3.2.x score for former Elf class */
1297 if (!strcmp(plch, "E"))
1300 impossible("What weird role is this? (%s)", plch);
1301 return PM_HUMAN_MUMMY;
1305 * Get a random player name and class from the high score list,
1306 * and attach them to an object (for statues or morgue corpses).
1314 register struct toptenentry *tt;
1316 struct toptenentry tt_buf;
1319 return (struct obj *) 0;
1321 rfile = fopen_datafile(RECORD, "r", SCOREPREFIX);
1323 impossible("Cannot open record file!");
1324 return (struct obj *) 0;
1328 rank = rnd(sysopt.tt_oname_maxrank);
1330 for (i = rank; i; i--) {
1331 readentry(rfile, tt);
1332 if (tt->points == 0)
1336 if (tt->points == 0) {
1342 otmp = (struct obj *) 0;
1344 set_corpsenm(otmp, classmon(tt->plrole, (tt->plgend[0] == 'F')));
1345 otmp = oname(otmp, tt->name);
1348 (void) fclose(rfile);
1352 #ifdef NO_SCAN_BRACK
1353 /* Lattice scanf isn't up to reading the scorefile. What */
1354 /* follows deals with that; I admit it's ugly. (KL) */
1355 /* Now generally available (KL) */
1360 while ((p = index(p, ' ')) != 0)
1368 while ((p = index(p, '|')) != 0)
1371 #endif /* NO_SCAN_BRACK */