OSDN Git Service

6cfdd5aea2141172525087ae968c4bf634df98c1
[jnethack/source.git] / src / end.c
1 /* NetHack 3.6  end.c   $NHDT-Date: 1575245059 2019/12/02 00:04:19 $  $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.181 $ */
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. */
5
6 #define NEED_VARARGS /* comment line for pre-compiled headers */
7
8 #include "hack.h"
9 #include "lev.h"
10 #ifndef NO_SIGNAL
11 #include <signal.h>
12 #endif
13 #include <ctype.h>
14 #ifndef LONG_MAX
15 #include <limits.h>
16 #endif
17 #include "dlb.h"
18
19 /* add b to long a, convert wraparound to max value */
20 #define nowrap_add(a, b) (a = ((a + b) < 0 ? LONG_MAX : (a + b)))
21
22 /* these probably ought to be generated by makedefs, like LAST_GEM */
23 #define FIRST_GEM DILITHIUM_CRYSTAL
24 #define FIRST_AMULET AMULET_OF_ESP
25 #define LAST_AMULET AMULET_OF_YENDOR
26
27 struct valuable_data {
28     long count;
29     int typ;
30 };
31
32 static struct valuable_data
33     gems[LAST_GEM + 1 - FIRST_GEM + 1], /* 1 extra for glass */
34     amulets[LAST_AMULET + 1 - FIRST_AMULET];
35
36 static struct val_list {
37     struct valuable_data *list;
38     int size;
39 } valuables[] = { { gems, sizeof gems / sizeof *gems },
40                   { amulets, sizeof amulets / sizeof *amulets },
41                   { 0, 0 } };
42
43 #ifndef NO_SIGNAL
44 STATIC_PTR void FDECL(done_intr, (int));
45 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
46 static void FDECL(done_hangup, (int));
47 #endif
48 #endif
49 STATIC_DCL void FDECL(disclose, (int, BOOLEAN_P));
50 STATIC_DCL void FDECL(get_valuables, (struct obj *));
51 STATIC_DCL void FDECL(sort_valuables, (struct valuable_data *, int));
52 STATIC_DCL void NDECL(done_object_cleanup);
53 STATIC_DCL void FDECL(artifact_score, (struct obj *, BOOLEAN_P, winid));
54 STATIC_DCL void FDECL(really_done, (int)) NORETURN;
55 STATIC_DCL void FDECL(savelife, (int));
56 STATIC_PTR int FDECL(CFDECLSPEC vanqsort_cmp, (const genericptr,
57                                                const genericptr));
58 STATIC_DCL int NDECL(set_vanq_order);
59 STATIC_DCL void FDECL(list_vanquished, (CHAR_P, BOOLEAN_P));
60 STATIC_DCL void FDECL(list_genocided, (CHAR_P, BOOLEAN_P));
61 STATIC_DCL boolean FDECL(should_query_disclose_option, (int, char *));
62 #ifdef DUMPLOG
63 STATIC_DCL void NDECL(dump_plines);
64 #endif
65 STATIC_DCL void FDECL(dump_everything, (int, time_t));
66 STATIC_DCL int NDECL(num_extinct);
67
68 #if defined(__BEOS__) || defined(MICRO) || defined(OS2)
69 extern void FDECL(nethack_exit, (int));
70 #else
71 #define nethack_exit exit
72 #endif
73
74 #define done_stopprint program_state.stopprint
75
76 #ifndef PANICTRACE
77 #define NH_abort NH_abort_
78 #endif
79
80 #ifdef AMIGA
81 #define NH_abort_() Abort(0)
82 #else
83 #ifdef SYSV
84 #define NH_abort_() (void) abort()
85 #else
86 #ifdef WIN32
87 #define NH_abort_() win32_abort()
88 #else
89 #define NH_abort_() abort()
90 #endif
91 #endif /* !SYSV */
92 #endif /* !AMIGA */
93
94 #ifdef PANICTRACE
95 #include <errno.h>
96 #ifdef PANICTRACE_LIBC
97 #include <execinfo.h>
98 #endif
99
100 /* What do we try and in what order?  Tradeoffs:
101  * libc: +no external programs required
102  *        -requires newish libc/glibc
103  *        -requires -rdynamic
104  * gdb:   +gives more detailed information
105  *        +works on more OS versions
106  *        -requires -g, which may preclude -O on some compilers
107  */
108 #ifdef SYSCF
109 #define SYSOPT_PANICTRACE_GDB sysopt.panictrace_gdb
110 #ifdef PANICTRACE_LIBC
111 #define SYSOPT_PANICTRACE_LIBC sysopt.panictrace_libc
112 #else
113 #define SYSOPT_PANICTRACE_LIBC 0
114 #endif
115 #else
116 #define SYSOPT_PANICTRACE_GDB (nh_getenv("NETHACK_USE_GDB") == 0 ? 0 : 2)
117 #ifdef PANICTRACE_LIBC
118 #define SYSOPT_PANICTRACE_LIBC 1
119 #else
120 #define SYSOPT_PANICTRACE_LIBC 0
121 #endif
122 #endif
123
124 static void NDECL(NH_abort);
125 #ifndef NO_SIGNAL
126 static void FDECL(panictrace_handler, (int));
127 #endif
128 static boolean NDECL(NH_panictrace_libc);
129 static boolean NDECL(NH_panictrace_gdb);
130
131 #ifndef NO_SIGNAL
132 /* called as signal() handler, so sent at least one arg */
133 /*ARGUSED*/
134 void
135 panictrace_handler(sig_unused)
136 int sig_unused UNUSED;
137 {
138 #define SIG_MSG "\nSignal received.\n"
139     int f2;
140     
141     f2 = (int) write(2, SIG_MSG, sizeof SIG_MSG - 1);
142     nhUse(f2);  /* what could we do if write to fd#2 (stderr) fails  */
143     NH_abort(); /* ... and we're already in the process of quitting? */
144 }
145
146 void
147 panictrace_setsignals(set)
148 boolean set;
149 {
150 #define SETSIGNAL(sig) \
151     (void) signal(sig, set ? (SIG_RET_TYPE) panictrace_handler : SIG_DFL);
152 #ifdef SIGILL
153     SETSIGNAL(SIGILL);
154 #endif
155 #ifdef SIGTRAP
156     SETSIGNAL(SIGTRAP);
157 #endif
158 #ifdef SIGIOT
159     SETSIGNAL(SIGIOT);
160 #endif
161 #ifdef SIGBUS
162     SETSIGNAL(SIGBUS);
163 #endif
164 #ifdef SIGFPE
165     SETSIGNAL(SIGFPE);
166 #endif
167 #ifdef SIGSEGV
168     SETSIGNAL(SIGSEGV);
169 #endif
170 #ifdef SIGSTKFLT
171     SETSIGNAL(SIGSTKFLT);
172 #endif
173 #ifdef SIGSYS
174     SETSIGNAL(SIGSYS);
175 #endif
176 #ifdef SIGEMT
177     SETSIGNAL(SIGEMT);
178 #endif
179 #undef SETSIGNAL
180 }
181 #endif /* NO_SIGNAL */
182
183 static void
184 NH_abort()
185 {
186     int gdb_prio = SYSOPT_PANICTRACE_GDB;
187     int libc_prio = SYSOPT_PANICTRACE_LIBC;
188     static boolean aborting = FALSE;
189
190     if (aborting)
191         return;
192     aborting = TRUE;
193
194 #ifndef VMS
195     if (gdb_prio == libc_prio && gdb_prio > 0)
196         gdb_prio++;
197
198     if (gdb_prio > libc_prio) {
199         (void) (NH_panictrace_gdb() || (libc_prio && NH_panictrace_libc()));
200     } else {
201         (void) (NH_panictrace_libc() || (gdb_prio && NH_panictrace_gdb()));
202     }
203
204 #else /* VMS */
205     /* overload otherwise unused priority for debug mode: 1 = show
206        traceback and exit; 2 = show traceback and stay in debugger */
207     /* if (wizard && gdb_prio == 1) gdb_prio = 2; */
208     vms_traceback(gdb_prio);
209     nhUse(libc_prio);
210
211 #endif /* ?VMS */
212
213 #ifndef NO_SIGNAL
214     panictrace_setsignals(FALSE);
215 #endif
216     NH_abort_();
217 }
218
219 static boolean
220 NH_panictrace_libc()
221 {
222 #ifdef PANICTRACE_LIBC
223     void *bt[20];
224     size_t count, x;
225     char **info;
226
227     raw_print("Generating more information you may report:\n");
228     count = backtrace(bt, SIZE(bt));
229     info = backtrace_symbols(bt, count);
230     for (x = 0; x < count; x++) {
231         raw_printf("[%lu] %s", (unsigned long) x, info[x]);
232     }
233     /* free(info);   -- Don't risk it. */
234     return TRUE;
235 #else
236     return FALSE;
237 #endif /* !PANICTRACE_LIBC */
238 }
239
240 /*
241  *   fooPATH  file system path for foo
242  *   fooVAR   (possibly const) variable containing fooPATH
243  */
244 #ifdef PANICTRACE_GDB
245 #ifdef SYSCF
246 #define GDBVAR sysopt.gdbpath
247 #define GREPVAR sysopt.greppath
248 #else /* SYSCF */
249 #define GDBVAR GDBPATH
250 #define GREPVAR GREPPATH
251 #endif /* SYSCF */
252 #endif /* PANICTRACE_GDB */
253
254 static boolean
255 NH_panictrace_gdb()
256 {
257 #ifdef PANICTRACE_GDB
258     /* A (more) generic method to get a stack trace - invoke
259      * gdb on ourself. */
260     const char *gdbpath = GDBVAR;
261     const char *greppath = GREPVAR;
262     char buf[BUFSZ];
263     FILE *gdb;
264
265     if (gdbpath == NULL || gdbpath[0] == 0)
266         return FALSE;
267     if (greppath == NULL || greppath[0] == 0)
268         return FALSE;
269
270     sprintf(buf, "%s -n -q %s %d 2>&1 | %s '^#'",
271             gdbpath, ARGV0, getpid(), greppath);
272     gdb = popen(buf, "w");
273     if (gdb) {
274         raw_print("Generating more information you may report:\n");
275         fprintf(gdb, "bt\nquit\ny");
276         fflush(gdb);
277         sleep(4); /* ugly */
278         pclose(gdb);
279         return TRUE;
280     } else {
281         return FALSE;
282     }
283 #else
284     return FALSE;
285 #endif /* !PANICTRACE_GDB */
286 }
287 #endif /* PANICTRACE */
288
289 #if 0 /*JP*//*\93ú\96{\8cê\82Å\82Í\8eg\82í\82È\82¢*/
290 /*
291  * The order of these needs to match the macros in hack.h.
292  */
293 static NEARDATA const char *deaths[] = {
294     /* the array of death */
295     "died", "choked", "poisoned", "starvation", "drowning", "burning",
296     "dissolving under the heat and pressure", "crushed", "turned to stone",
297     "turned into slime", "genocided", "panic", "trickery", "quit",
298     "escaped", "ascended"
299 };
300 #endif /*JP*/
301
302 static NEARDATA const char *ends[] = {
303     /* "when you %s" */
304 #if 0 /*JP*/
305     "died", "choked", "were poisoned",
306     "starved", "drowned", "burned",
307     "dissolved in the lava",
308     "were crushed", "turned to stone",
309     "turned into slime", "were genocided",
310     "panicked", "were tricked", "quit",
311     "escaped", "ascended"
312 #else /*JP: \8dÅ\8cã\82É\81u\8eE\82³\82ê\82½\81v\92Ç\89Á */
313     "\8e\80\82ñ\82¾", "\92\82\91§\82µ\82½", "\93Å\82É\82¨\82©\82³\82ê\82½",
314     "\89ì\8e\80\82µ\82½", "\93M\8e\80\82µ\82½", "\8fÄ\8e\80\82µ\82½",
315     "\97n\8aâ\82É\97n\82¯\82½",
316     "\89\9f\82µ\92×\82³\82ê\82½", "\90Î\82É\82È\82Á\82½",
317     "\82Ç\82ë\82Ç\82ë\82É\97n\82¯\82½", "\8bs\8eE\82³\82ê\82½",
318     "\83p\83j\83b\83N\82É\82¨\82¿\82¢\82Á\82½", "\8aï\96­\82È\8fo\97\88\8e\96\82É\89ï\82Á\82½", "\94²\82¯\82½",
319     "\92E\8fo\82µ\82½", "\8f¸\93V\82µ\82½", "\8eE\82³\82ê\82½"
320 #endif
321 };
322
323 static boolean Schroedingers_cat = FALSE;
324
325 /*ARGSUSED*/
326 void
327 done1(sig_unused) /* called as signal() handler, so sent at least one arg */
328 int sig_unused UNUSED;
329 {
330 #ifndef NO_SIGNAL
331     (void) signal(SIGINT, SIG_IGN);
332 #endif
333     if (flags.ignintr) {
334 #ifndef NO_SIGNAL
335         (void) signal(SIGINT, (SIG_RET_TYPE) done1);
336 #endif
337         clear_nhwindow(WIN_MESSAGE);
338         curs_on_u();
339         wait_synch();
340         if (multi > 0)
341             nomul(0);
342     } else {
343         (void) done2();
344     }
345 }
346
347 /* "#quit" command or keyboard interrupt */
348 int
349 done2()
350 {
351     if (iflags.debug_fuzzer)
352         return 0;
353 /*JP
354     if (!paranoid_query(ParanoidQuit, "Really quit?")) {
355 */
356     if (!paranoid_query(ParanoidQuit, "\96{\93\96\82É\82â\82ß\82é\81H")) {
357 #ifndef NO_SIGNAL
358         (void) signal(SIGINT, (SIG_RET_TYPE) done1);
359 #endif
360         clear_nhwindow(WIN_MESSAGE);
361         curs_on_u();
362         wait_synch();
363         if (multi > 0)
364             nomul(0);
365         if (multi == 0) {
366             u.uinvulnerable = FALSE; /* avoid ctrl-C bug -dlc */
367             u.usleep = 0;
368         }
369         return 0;
370     }
371 #if (defined(UNIX) || defined(VMS) || defined(LATTICE))
372     if (wizard) {
373         int c;
374 #ifdef VMS
375         extern int debuggable; /* sys/vms/vmsmisc.c, vmsunix.c */
376
377         c = !debuggable ? 'n' : ynq("Enter debugger?");
378 #else
379 #ifdef LATTICE
380         c = ynq("Create SnapShot?");
381 #else
382         c = ynq("Dump core?");
383 #endif
384 #endif
385         if (c == 'y') {
386 #ifndef NO_SIGNAL
387             (void) signal(SIGINT, (SIG_RET_TYPE) done1);
388 #endif
389             exit_nhwindows((char *) 0);
390             NH_abort();
391         } else if (c == 'q')
392             done_stopprint++;
393     }
394 #endif
395 #ifndef LINT
396     done(QUIT);
397 #endif
398     return 0;
399 }
400
401 #ifndef NO_SIGNAL
402 /*ARGSUSED*/
403 STATIC_PTR void
404 done_intr(sig_unused) /* called as signal() handler, so sent at least 1 arg */
405 int sig_unused UNUSED;
406 {
407     done_stopprint++;
408     (void) signal(SIGINT, SIG_IGN);
409 #if defined(UNIX) || defined(VMS)
410     (void) signal(SIGQUIT, SIG_IGN);
411 #endif
412     return;
413 }
414
415 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
416 /* signal() handler */
417 static void
418 done_hangup(sig)
419 int sig;
420 {
421     program_state.done_hup++;
422     sethanguphandler((void FDECL((*), (int) )) SIG_IGN);
423     done_intr(sig);
424     return;
425 }
426 #endif
427 #endif /* NO_SIGNAL */
428
429 void
430 done_in_by(mtmp, how)
431 struct monst *mtmp;
432 int how;
433 {
434     char buf[BUFSZ];
435     struct permonst *mptr = mtmp->data,
436                     *champtr = ((mtmp->cham >= LOW_PM)
437                                    ? &mons[mtmp->cham]
438                                    : mptr);
439     boolean distorted = (boolean) (Hallucination && canspotmon(mtmp)),
440             mimicker = (M_AP_TYPE(mtmp) == M_AP_MONSTER),
441             imitator = (mptr != champtr || mimicker);
442
443 /*JP
444     You((how == STONING) ? "turn to stone..." : "die...");
445 */
446     You((how == STONING) ? "\90Î\82É\82È\82Á\82½\81D\81D\81D" : "\8e\80\82É\82Ü\82µ\82½\81D\81D\81D");
447     mark_synch(); /* flush buffered screen output */
448     buf[0] = '\0';
449     killer.format = KILLED_BY_AN;
450 #if 0 /*JP*//* \93ú\96{\8cê\82É\82Í\8aÖ\8cW\82È\82¢\82Ì\82Å\82Ü\82Æ\82ß\82Ä\83R\83\81\83\93\83g\83A\83E\83g */
451     /* "killed by the high priest of Crom" is okay,
452        "killed by the high priest" alone isn't */
453     if ((mptr->geno & G_UNIQ) != 0 && !(imitator && !mimicker)
454         && !(mptr == &mons[PM_HIGH_PRIEST] && !mtmp->ispriest)) {
455         if (!type_is_pname(mptr))
456             Strcat(buf, "the ");
457         killer.format = KILLED_BY;
458     }
459     /* _the_ <invisible> <distorted> ghost of Dudley */
460     if (mptr == &mons[PM_GHOST] && has_mname(mtmp)) {
461         Strcat(buf, "the ");
462         killer.format = KILLED_BY;
463     }
464 #endif
465     if (mtmp->minvis)
466 /*JP
467         Strcat(buf, "invisible ");
468 */
469         Strcat(buf, "\93§\96¾\82È");
470     if (distorted)
471 /*JP
472         Strcat(buf, "hallucinogen-distorted ");
473 */
474         Strcat(buf, "\8c\8ao\82Å\98c\82ñ\82¾");
475
476     if (imitator) {
477         char shape[BUFSZ];
478         const char *realnm = champtr->mname, *fakenm = mptr->mname;
479         boolean alt = is_vampshifter(mtmp);
480
481         if (mimicker) {
482             /* realnm is already correct because champtr==mptr;
483                set up fake mptr for type_is_pname/the_unique_pm */
484             mptr = &mons[mtmp->mappearance];
485             fakenm = mptr->mname;
486 #if 0 /*JP:T*/
487         } else if (alt && strstri(realnm, "vampire")
488                    && !strcmp(fakenm, "vampire bat")) {
489             /* special case: use "vampire in bat form" in preference
490                to redundant looking "vampire in vampire bat form" */
491             fakenm = "bat";
492 #else
493         } else if (alt && strstri(realnm, "\8bz\8c\8c\8bS")
494                    && !strcmp(fakenm, "\8bz\8c\8c\82±\82¤\82à\82è")) {
495             /* \81u\8bz\8c\8c\82±\82¤\82à\82è\82Ì\8ep\82Ì\8bz\8c\8c\8bS\81v\82Í\8fç\92·\82È\82Ì\82Å
496                \81u\82±\82¤\82à\82è\82Ì\8ep\82Ì\8bz\8c\8c\8bS\81v\82Ì\8c`\82É\82·\82é */
497             fakenm = "\82±\82¤\82à\82è";
498 #endif
499         }
500 #if 0 /*JP*/
501         /* for the alternate format, always suppress any article;
502            pname and the_unique should also have s_suffix() applied,
503            but vampires don't take on any shapes which warrant that */
504         if (alt || type_is_pname(mptr)) /* no article */
505             Strcpy(shape, fakenm);
506         else if (the_unique_pm(mptr)) /* "the"; don't use the() here */
507             Sprintf(shape, "the %s", fakenm);
508         else /* "a"/"an" */
509             Strcpy(shape, an(fakenm));
510 #else /*JP:\93ú\96{\8cê\82Å\82Í\83V\83\93\83v\83\8b*/
511         Strcpy(shape, fakenm);
512 #endif
513         /* omit "called" to avoid excessive verbosity */
514 #if 0 /*JP:T*/
515         Sprintf(eos(buf),
516                 alt ? "%s in %s form"
517                     : mimicker ? "%s disguised as %s"
518                                : "%s imitating %s",
519                 realnm, shape);
520 #else
521         Sprintf(eos(buf),
522                 alt ? "%s\82Ì\8ep\82Ì%s"
523                     : mimicker ? "%s\82Ì\82Ó\82è\82ð\82µ\82Ä\82¢\82é%s"
524                                : "%s\82Ì\82Ü\82Ë\82ð\82µ\82Ä\82¢\82é%s",
525                 shape, realnm);
526 #endif
527         mptr = mtmp->data; /* reset for mimicker case */
528     } else if (mptr == &mons[PM_GHOST]) {
529 #if 0 /*JP*/
530         Strcat(buf, "ghost");
531         if (has_mname(mtmp))
532             Sprintf(eos(buf), " of %s", MNAME(mtmp));
533 #else
534         if (has_mname(mtmp))
535             Sprintf(eos(buf), "%s\82Ì\97H\97ì", MNAME(mtmp));
536         else
537             Strcat(buf, "\97H\97ì");
538 #endif
539     } else if (mtmp->isshk) {
540 #if 0 /*JP*/
541         const char *shknm = shkname(mtmp),
542                    *honorific = shkname_is_pname(mtmp) ? ""
543                                    : mtmp->female ? "Ms. " : "Mr. ";
544
545         Sprintf(eos(buf), "%s%s, the shopkeeper", honorific, shknm);
546 #else
547         Sprintf(eos(buf), "%s\82Æ\82¢\82¤\96¼\82Ì\93X\8eå", shkname(mtmp));
548 #endif
549         killer.format = KILLED_BY;
550     } else if (mtmp->ispriest || mtmp->isminion) {
551         /* m_monnam() suppresses "the" prefix plus "invisible", and
552            it overrides the effect of Hallucination on priestname() */
553         Strcat(buf, m_monnam(mtmp));
554     } else {
555 #if 0 /*JP*/
556         Strcat(buf, mptr->mname);
557         if (has_mname(mtmp))
558             Sprintf(eos(buf), " called %s", MNAME(mtmp));
559 #else
560         Strcat(buf, mptr->mname);
561 #endif
562     }
563
564     Strcpy(killer.name, buf);
565     /*
566      * Chicken and egg issue:
567      *  Ordinarily Unchanging ought to override something like this,
568      *  but the transformation occurs at death.  With the current code,
569      *  the effectiveness of Unchanging stops first, but a case could
570      *  be made that it should last long enough to prevent undead
571      *  transformation.  (Turning to slime isn't an issue here because
572      *  Unchanging prevents that from happening.)
573      */
574     if (mptr->mlet == S_WRAITH)
575         u.ugrave_arise = PM_WRAITH;
576     else if (mptr->mlet == S_MUMMY && urace.mummynum != NON_PM)
577         u.ugrave_arise = urace.mummynum;
578     else if (mptr->mlet == S_VAMPIRE && Race_if(PM_HUMAN))
579         u.ugrave_arise = PM_VAMPIRE;
580     else if (mptr == &mons[PM_GHOUL])
581         u.ugrave_arise = PM_GHOUL;
582     /* this could happen if a high-end vampire kills the hero
583        when ordinary vampires are genocided; ditto for wraiths */
584     if (u.ugrave_arise >= LOW_PM
585         && (mvitals[u.ugrave_arise].mvflags & G_GENOD))
586         u.ugrave_arise = NON_PM;
587
588 #if 1 /*JP*/
589     if (how == STONING){
590         /*JP
591               topten.c \82Ì killed_by_prefix \82ð\8eQ\8fÆ\82Ì\82±\82Æ\81B
592               STONING \82Ì\8fê\8d\87\82Í "\90Î\89»\82µ\82½" \82ª\95â\82í\82ê\82é\81B
593          */
594         Strcat(buf, "\82Ì\8dU\8c\82\82Å");
595     }
596     if (how == DIED){
597         /*JP
598               DIED \82Ì\8fê\8d\87\82Í\92Ê\8fí "\8e\80\82ñ\82¾" \82ª\95â\82í\82ê\82é\82ª\81A
599               \89ö\95¨\82É\82æ\82é\8fê\8d\87\82Í "\82É\8eE\82³\82ê\82½" \82ð\95â\82¤\81B
600          */
601         killer.format = KILLED_SUFFIX;
602     }
603 #endif
604     done(how);
605     return;
606 }
607
608 /* some special cases for overriding while-helpless reason */
609 #if 0 /*JP*//*\93ú\96{\8cê\82Å\82Í\8eg\82í\82È\82¢*/
610 static const struct {
611     int why, unmulti;
612     const char *exclude, *include;
613 } death_fixups[] = {
614     /* "petrified by <foo>, while getting stoned" -- "while getting stoned"
615        prevented any last-second recovery, but it was not the cause of
616        "petrified by <foo>" */
617     { STONING, 1, "getting stoned", (char *) 0 },
618     /* "died of starvation, while fainted from lack of food" is accurate
619        but sounds a fairly silly (and doesn't actually appear unless you
620        splice together death and while-helpless from xlogfile) */
621     { STARVING, 0, "fainted from lack of food", "fainted" },
622 };
623
624 /* clear away while-helpless when the cause of death caused that
625    helplessness (ie, "petrified by <foo> while getting stoned") */
626 STATIC_DCL void
627 fixup_death(how)
628 int how;
629 {
630     int i;
631
632     if (multi_reason) {
633         for (i = 0; i < SIZE(death_fixups); ++i)
634             if (death_fixups[i].why == how
635                 && !strcmp(death_fixups[i].exclude, multi_reason)) {
636                 if (death_fixups[i].include) /* substitute alternate reason */
637                     multi_reason = death_fixups[i].include;
638                 else /* remove the helplessness reason */
639                     multi_reason = (char *) 0;
640                 if (death_fixups[i].unmulti) /* possibly hide helplessness */
641                     multi = 0L;
642                 break;
643             }
644     }
645 }
646 #endif
647
648 #if defined(WIN32) && !defined(SYSCF)
649 #define NOTIFY_NETHACK_BUGS
650 #endif
651
652 /*VARARGS1*/
653 void panic
654 VA_DECL(const char *, str)
655 {
656     VA_START(str);
657     VA_INIT(str, char *);
658
659     if (program_state.panicking++)
660         NH_abort(); /* avoid loops - this should never happen*/
661
662     if (iflags.window_inited) {
663         raw_print("\r\nOops...");
664         wait_synch(); /* make sure all pending output gets flushed */
665         exit_nhwindows((char *) 0);
666         iflags.window_inited = 0; /* they're gone; force raw_print()ing */
667     }
668
669     raw_print(program_state.gameover
670 /*JP
671                   ? "Postgame wrapup disrupted."
672 */
673                   ? "\83Q\81[\83\80\8fI\97¹\8e\9e\82Ì\8f\88\97\9d\82ª\95ö\89ó\82µ\82½\81D"
674                   : !program_state.something_worth_saving
675 /*JP
676                         ? "Program initialization has failed."
677 */
678                         ? "\83v\83\8d\83O\83\89\83\80\82Ì\8f\89\8aú\89»\82É\8e¸\94s\82µ\82½\81D"
679 /*JP
680                         : "Suddenly, the dungeon collapses.");
681 */
682                         : "\93Ë\91R\96À\8b{\82ª\95ö\82ê\82½\81D");
683 #ifndef MICRO
684 #ifdef NOTIFY_NETHACK_BUGS
685     if (!wizard)
686         raw_printf("Report the following error to \"%s\" or at \"%s\".",
687                    DEVTEAM_EMAIL, DEVTEAM_URL);
688     else if (program_state.something_worth_saving)
689         raw_print("\nError save file being written.\n");
690 #else /* !NOTIFY_NETHACK_BUGS */
691     if (!wizard) {
692 #if 0 /*JP:T*/
693         const char *maybe_rebuild = !program_state.something_worth_saving
694                                      ? "."
695                                      : "\nand it may be possible to rebuild.";
696 #else
697         const char *maybe_rebuild = !program_state.something_worth_saving
698                                      ? "\81D"
699                                      : "\n\95\9c\8b\8c\82Å\82«\82é\89Â\94\\90«\82ª\82 \82è\82Ü\82·\81D";
700 #endif
701
702         if (sysopt.support)
703             raw_printf("To report this error, %s%s", sysopt.support,
704                        maybe_rebuild);
705         else if (sysopt.fmtd_wizard_list) /* formatted SYSCF WIZARDS */
706             raw_printf("To report this error, contact %s%s",
707                        sysopt.fmtd_wizard_list, maybe_rebuild);
708         else
709 #if 0 /*JP:T*/
710             raw_printf("Report error to \"%s\"%s", WIZARD_NAME,
711                        maybe_rebuild);
712 #else
713             raw_printf("\"%s\"\82É\83G\83\89\81[\82ð\95ñ\8d\90\82µ\82Ä\82­\82¾\82³\82¢\81D%s", WIZARD_NAME,
714                        maybe_rebuild);
715 #endif
716     }
717 #endif /* ?NOTIFY_NETHACK_BUGS */
718     /* XXX can we move this above the prints?  Then we'd be able to
719      * suppress "it may be possible to rebuild" based on dosave0()
720      * or say it's NOT possible to rebuild. */
721     if (program_state.something_worth_saving && !iflags.debug_fuzzer) {
722         set_error_savefile();
723         if (dosave0()) {
724             /* os/win port specific recover instructions */
725             if (sysopt.recover)
726                 raw_printf("%s", sysopt.recover);
727         }
728     }
729 #endif /* !MICRO */
730     {
731         char buf[BUFSZ];
732
733 #if !defined(NO_VSNPRINTF)
734         (void) vsnprintf(buf, sizeof buf, str, VA_ARGS);
735 #else
736         Vsprintf(buf, str, VA_ARGS);
737 #endif
738         raw_print(buf);
739         paniclog("panic", buf);
740     }
741 #ifdef WIN32
742     interject(INTERJECT_PANIC);
743 #endif
744 #if defined(UNIX) || defined(VMS) || defined(LATTICE) || defined(WIN32)
745     if (wizard)
746         NH_abort(); /* generate core dump */
747 #endif
748     VA_END();
749     really_done(PANICKED);
750 }
751
752 STATIC_OVL boolean
753 should_query_disclose_option(category, defquery)
754 int category;
755 char *defquery;
756 {
757     int idx;
758     char disclose, *dop;
759
760     *defquery = 'n';
761     if ((dop = index(disclosure_options, category)) != 0) {
762         idx = (int) (dop - disclosure_options);
763         if (idx < 0 || idx >= NUM_DISCLOSURE_OPTIONS) {
764             impossible(
765                    "should_query_disclose_option: bad disclosure index %d %c",
766                        idx, category);
767             *defquery = DISCLOSE_PROMPT_DEFAULT_YES;
768             return TRUE;
769         }
770         disclose = flags.end_disclose[idx];
771         if (disclose == DISCLOSE_YES_WITHOUT_PROMPT) {
772             *defquery = 'y';
773             return FALSE;
774         } else if (disclose == DISCLOSE_SPECIAL_WITHOUT_PROMPT) {
775             *defquery = 'a';
776             return FALSE;
777         } else if (disclose == DISCLOSE_NO_WITHOUT_PROMPT) {
778             *defquery = 'n';
779             return FALSE;
780         } else if (disclose == DISCLOSE_PROMPT_DEFAULT_YES) {
781             *defquery = 'y';
782             return TRUE;
783         } else if (disclose == DISCLOSE_PROMPT_DEFAULT_SPECIAL) {
784             *defquery = 'a';
785             return TRUE;
786         } else {
787             *defquery = 'n';
788             return TRUE;
789         }
790     }
791     impossible("should_query_disclose_option: bad category %c", category);
792     return TRUE;
793 }
794
795 #ifdef DUMPLOG
796 STATIC_OVL void
797 dump_plines()
798 {
799     int i, j;
800     char buf[BUFSZ], **strp;
801     extern char *saved_plines[];
802     extern unsigned saved_pline_index;
803
804     Strcpy(buf, " "); /* one space for indentation */
805 /*JP
806     putstr(0, 0, "Latest messages:");
807 */
808     putstr(0, 0, "\8dÅ\8cã\82Ì\83\81\83b\83Z\81[\83W:");
809     for (i = 0, j = (int) saved_pline_index; i < DUMPLOG_MSG_COUNT;
810          ++i, j = (j + 1) % DUMPLOG_MSG_COUNT) {
811         strp = &saved_plines[j];
812         if (*strp) {
813             copynchars(&buf[1], *strp, BUFSZ - 1 - 1);
814             putstr(0, 0, buf);
815 #ifdef FREE_ALL_MEMORY
816             free(*strp), *strp = 0;
817 #endif
818         }
819     }
820 }
821 #endif
822
823 /*ARGSUSED*/
824 STATIC_OVL void
825 dump_everything(how, when)
826 int how;
827 time_t when; /* date+time at end of game */
828 {
829 #ifdef DUMPLOG
830     char pbuf[BUFSZ], datetimebuf[24]; /* [24]: room for 64-bit bogus value */
831
832     dump_redirect(TRUE);
833     if (!iflags.in_dumplog)
834         return;
835
836     init_symbols(); /* revert to default symbol set */
837
838     /* one line version ID, which includes build date+time;
839        it's conceivable that the game started with a different
840        build date+time or even with an older nethack version,
841        but we only have access to the one it finished under */
842     putstr(0, 0, getversionstring(pbuf));
843 #if 1 /*JP*/
844     putstr(0, 0, version_string_j(pbuf));
845 #endif
846     putstr(0, 0, "");
847
848     /* game start and end date+time to disambiguate version date+time */
849     Strcpy(datetimebuf, yyyymmddhhmmss(ubirthday));
850 #if 0 /*JP:T*/
851     Sprintf(pbuf, "Game began %4.4s-%2.2s-%2.2s %2.2s:%2.2s:%2.2s",
852             &datetimebuf[0], &datetimebuf[4], &datetimebuf[6],
853             &datetimebuf[8], &datetimebuf[10], &datetimebuf[12]);
854 #else
855     Sprintf(pbuf, "\83Q\81[\83\80\8aJ\8en %4.4s-%2.2s-%2.2s %2.2s:%2.2s:%2.2s",
856             &datetimebuf[0], &datetimebuf[4], &datetimebuf[6],
857             &datetimebuf[8], &datetimebuf[10], &datetimebuf[12]);
858 #endif
859     Strcpy(datetimebuf, yyyymmddhhmmss(when));
860 #if 0 /*JP*/
861     Sprintf(eos(pbuf), ", ended %4.4s-%2.2s-%2.2s %2.2s:%2.2s:%2.2s.",
862             &datetimebuf[0], &datetimebuf[4], &datetimebuf[6],
863             &datetimebuf[8], &datetimebuf[10], &datetimebuf[12]);
864 #else
865     Sprintf(eos(pbuf), ", \8fI\97¹ %4.4s-%2.2s-%2.2s %2.2s:%2.2s:%2.2s.",
866             &datetimebuf[0], &datetimebuf[4], &datetimebuf[6],
867             &datetimebuf[8], &datetimebuf[10], &datetimebuf[12]);
868 #endif
869     putstr(0, 0, pbuf);
870     putstr(0, 0, "");
871
872     /* character name and basic role info */
873     Sprintf(pbuf, "%s, %s %s %s %s", plname,
874             aligns[1 - u.ualign.type].adj,
875             genders[flags.female].adj,
876             urace.adj,
877             (flags.female && urole.name.f) ? urole.name.f : urole.name.m);
878     putstr(0, 0, pbuf);
879     putstr(0, 0, "");
880
881     dump_map();
882     putstr(0, 0, do_statusline1());
883     putstr(0, 0, do_statusline2());
884     putstr(0, 0, "");
885
886     dump_plines();
887     putstr(0, 0, "");
888 /*JP
889     putstr(0, 0, "Inventory:");
890 */
891     putstr(0, 0, "\8e\9d\82¿\95¨:");
892     (void) display_inventory((char *) 0, TRUE);
893     container_contents(invent, TRUE, TRUE, FALSE);
894     enlightenment((BASICENLIGHTENMENT | MAGICENLIGHTENMENT),
895                   (how >= PANICKED) ? ENL_GAMEOVERALIVE : ENL_GAMEOVERDEAD);
896     putstr(0, 0, "");
897     list_vanquished('d', FALSE); /* 'd' => 'y' */
898     putstr(0, 0, "");
899     list_genocided('d', FALSE); /* 'd' => 'y' */
900     putstr(0, 0, "");
901     show_conduct((how >= PANICKED) ? 1 : 2);
902     putstr(0, 0, "");
903     show_overview((how >= PANICKED) ? 1 : 2, how);
904     putstr(0, 0, "");
905     dump_redirect(FALSE);
906 #else
907     nhUse(how);
908     nhUse(when);
909 #endif
910 }
911
912 STATIC_OVL void
913 disclose(how, taken)
914 int how;
915 boolean taken;
916 {
917     char c = '\0', defquery;
918     char qbuf[QBUFSZ];
919     boolean ask = FALSE;
920
921     if (invent && !done_stopprint) {
922         if (taken)
923 #if 0 /*JP:T*/
924             Sprintf(qbuf, "Do you want to see what you had when you %s?",
925                     (how == QUIT) ? "quit" : "died");
926 #else
927             Sprintf(qbuf,"%s\82Æ\82«\89½\82ð\8e\9d\82Á\82Ä\82¢\82½\82©\8c©\82Ü\82·\82©\81H",
928                     (how == QUIT) ? "\82â\82ß\82½" : "\8e\80\82ñ\82¾");
929 #endif
930         else
931 /*JP
932             Strcpy(qbuf, "Do you want your possessions identified?");
933 */
934             Strcpy(qbuf,"\8e\9d\82¿\95¨\82ð\8e¯\95Ê\82µ\82Ü\82·\82©\81H"); 
935
936         ask = should_query_disclose_option('i', &defquery);
937         c = ask ? yn_function(qbuf, ynqchars, defquery) : defquery;
938         if (c == 'y') {
939             /* caller has already ID'd everything */
940             (void) display_inventory((char *) 0, TRUE);
941             container_contents(invent, TRUE, TRUE, FALSE);
942         }
943         if (c == 'q')
944             done_stopprint++;
945     }
946
947     if (!done_stopprint) {
948         ask = should_query_disclose_option('a', &defquery);
949 #if 0 /*JP:T*/
950         c = ask ? yn_function("Do you want to see your attributes?", ynqchars,
951                               defquery)
952                 : defquery;
953 #else
954         c = ask ? yn_function("\91®\90«\82ð\8c©\82Ü\82·\82©\81H", ynqchars,
955                               defquery)
956                 : defquery;
957 #endif
958         if (c == 'y')
959             enlightenment((BASICENLIGHTENMENT | MAGICENLIGHTENMENT),
960                           (how >= PANICKED) ? ENL_GAMEOVERALIVE
961                                             : ENL_GAMEOVERDEAD);
962         if (c == 'q')
963             done_stopprint++;
964     }
965
966     if (!done_stopprint) {
967         ask = should_query_disclose_option('v', &defquery);
968         list_vanquished(defquery, ask);
969     }
970
971     if (!done_stopprint) {
972         ask = should_query_disclose_option('g', &defquery);
973         list_genocided(defquery, ask);
974     }
975
976     if (!done_stopprint) {
977         ask = should_query_disclose_option('c', &defquery);
978 #if 0 /*JP:T*/
979         c = ask ? yn_function("Do you want to see your conduct?", ynqchars,
980                               defquery)
981                 : defquery;
982 #else
983         c = ask ? yn_function("\82Ç\82¤\82¢\82¤\8ds\93®\82ð\82Æ\82Á\82½\82©\8c©\82Ü\82·\82©\81H", ynqchars,
984                               defquery)
985                 : defquery;
986 #endif
987         if (c == 'y')
988             show_conduct((how >= PANICKED) ? 1 : 2);
989         if (c == 'q')
990             done_stopprint++;
991     }
992
993     if (!done_stopprint) {
994         ask = should_query_disclose_option('o', &defquery);
995 #if 0 /*JP:T*/
996         c = ask ? yn_function("Do you want to see the dungeon overview?",
997                               ynqchars, defquery)
998                 : defquery;
999 #else
1000         c = ask ? yn_function("\96À\8b{\82Ì\8aT\97v\82ð\8c©\82Ü\82·\82©\81H",
1001                               ynqchars, defquery)
1002                 : defquery;
1003 #endif
1004         if (c == 'y')
1005             show_overview((how >= PANICKED) ? 1 : 2, how);
1006         if (c == 'q')
1007             done_stopprint++;
1008     }
1009 }
1010
1011 /* try to get the player back in a viable state after being killed */
1012 STATIC_OVL void
1013 savelife(how)
1014 int how;
1015 {
1016     int uhpmin = max(2 * u.ulevel, 10);
1017
1018     if (u.uhpmax < uhpmin)
1019         u.uhpmax = uhpmin;
1020     u.uhp = u.uhpmax;
1021     if (Upolyd) /* Unchanging, or death which bypasses losing hit points */
1022         u.mh = u.mhmax;
1023     if (u.uhunger < 500 || how == CHOKING) {
1024         init_uhunger();
1025     }
1026     /* cure impending doom of sickness hero won't have time to fix */
1027     if ((Sick & TIMEOUT) == 1L) {
1028         make_sick(0L, (char *) 0, FALSE, SICK_ALL);
1029     }
1030 /*JP
1031     nomovemsg = "You survived that attempt on your life.";
1032 */
1033     nomovemsg = "\82 \82È\82½\82Í\90\82«\82È\82ª\82ç\82¦\82½\81D";
1034     context.move = 0;
1035     if (multi > 0)
1036         multi = 0;
1037     else
1038         multi = -1;
1039     if (u.utrap && u.utraptype == TT_LAVA)
1040         reset_utrap(FALSE);
1041     context.botl = 1;
1042     u.ugrave_arise = NON_PM;
1043     HUnchanging = 0L;
1044     curs_on_u();
1045     if (!context.mon_moving)
1046         endmultishot(FALSE);
1047     if (u.uswallow) {
1048         /* might drop hero onto a trap that kills her all over again */
1049         expels(u.ustuck, u.ustuck->data, TRUE);
1050     } else if (u.ustuck) {
1051         if (Upolyd && sticks(youmonst.data))
1052             You("release %s.", mon_nam(u.ustuck));
1053         else
1054             pline("%s releases you.", Monnam(u.ustuck));
1055         unstuck(u.ustuck);
1056     }
1057 }
1058
1059 /*
1060  * Get valuables from the given list.  Revised code: the list always remains
1061  * intact.
1062  */
1063 STATIC_OVL void
1064 get_valuables(list)
1065 struct obj *list; /* inventory or container contents */
1066 {
1067     register struct obj *obj;
1068     register int i;
1069
1070     /* find amulets and gems, ignoring all artifacts */
1071     for (obj = list; obj; obj = obj->nobj)
1072         if (Has_contents(obj)) {
1073             get_valuables(obj->cobj);
1074         } else if (obj->oartifact) {
1075             continue;
1076         } else if (obj->oclass == AMULET_CLASS) {
1077             i = obj->otyp - FIRST_AMULET;
1078             if (!amulets[i].count) {
1079                 amulets[i].count = obj->quan;
1080                 amulets[i].typ = obj->otyp;
1081             } else
1082                 amulets[i].count += obj->quan; /* always adds one */
1083         } else if (obj->oclass == GEM_CLASS && obj->otyp < LUCKSTONE) {
1084             i = min(obj->otyp, LAST_GEM + 1) - FIRST_GEM;
1085             if (!gems[i].count) {
1086                 gems[i].count = obj->quan;
1087                 gems[i].typ = obj->otyp;
1088             } else
1089                 gems[i].count += obj->quan;
1090         }
1091     return;
1092 }
1093
1094 /*
1095  *  Sort collected valuables, most frequent to least.  We could just
1096  *  as easily use qsort, but we don't care about efficiency here.
1097  */
1098 STATIC_OVL void
1099 sort_valuables(list, size)
1100 struct valuable_data list[];
1101 int size; /* max value is less than 20 */
1102 {
1103     register int i, j;
1104     struct valuable_data ltmp;
1105
1106     /* move greater quantities to the front of the list */
1107     for (i = 1; i < size; i++) {
1108         if (list[i].count == 0)
1109             continue;   /* empty slot */
1110         ltmp = list[i]; /* structure copy */
1111         for (j = i; j > 0; --j)
1112             if (list[j - 1].count >= ltmp.count)
1113                 break;
1114             else {
1115                 list[j] = list[j - 1];
1116             }
1117         list[j] = ltmp;
1118     }
1119     return;
1120 }
1121
1122 #if 0
1123 /*
1124  * odds_and_ends() was used for 3.6.0 and 3.6.1.
1125  * Schroedinger's Cat is handled differently as of 3.6.2.
1126  */
1127 STATIC_DCL boolean FDECL(odds_and_ends, (struct obj *, int));
1128
1129 #define CAT_CHECK 2
1130
1131 STATIC_OVL boolean
1132 odds_and_ends(list, what)
1133 struct obj *list;
1134 int what;
1135 {
1136     struct obj *otmp;
1137
1138     for (otmp = list; otmp; otmp = otmp->nobj) {
1139         switch (what) {
1140         case CAT_CHECK: /* Schroedinger's Cat */
1141             /* Ascending is deterministic */
1142             if (SchroedingersBox(otmp))
1143                 return rn2(2);
1144             break;
1145         }
1146         if (Has_contents(otmp))
1147             return odds_and_ends(otmp->cobj, what);
1148     }
1149     return FALSE;
1150 }
1151 #endif
1152
1153 /* deal with some objects which may be in an abnormal state at end of game */
1154 STATIC_OVL void
1155 done_object_cleanup()
1156 {
1157     int ox, oy;
1158
1159     /* might have been killed while using a disposable item, so make sure
1160        it's gone prior to inventory disclosure and creation of bones */
1161     inven_inuse(TRUE);
1162     /*
1163      * Hero can die when throwing an object (by hitting an adjacent
1164      * gas spore, for instance, or being hit by mis-returning Mjollnir),
1165      * or while in transit (from falling down stairs).  If that happens,
1166      * some object(s) might be in limbo rather than on the map or in
1167      * any inventory.  Saving bones with an active light source in limbo
1168      * would trigger an 'object not local' panic.
1169      *
1170      * We used to use dealloc_obj() on thrownobj and kickedobj but
1171      * that keeps them out of bones and could leave uball in a confused
1172      * state (gone but still attached).  Place them on the map but
1173      * bypass flooreffects().  That could lead to minor anomalies in
1174      * bones, like undamaged paper at water or lava locations or piles
1175      * not being knocked down holes, but it seems better to get this
1176      * game over with than risk being tangled up in more and more details.
1177      */
1178     ox = u.ux + u.dx, oy = u.uy + u.dy;
1179     if (!isok(ox, oy) || !accessible(ox, oy))
1180         ox = u.ux, oy = u.uy;
1181     /* put thrown or kicked object on map (for bones); location might
1182        be incorrect (perhaps killed by divine lightning when throwing at
1183        a temple priest?) but this should be better than just vanishing
1184        (fragile stuff should be taken care of before getting here) */
1185     if (thrownobj && thrownobj->where == OBJ_FREE) {
1186         place_object(thrownobj, ox, oy);
1187         stackobj(thrownobj), thrownobj = 0;
1188     }
1189     if (kickedobj && kickedobj->where == OBJ_FREE) {
1190         place_object(kickedobj, ox, oy);
1191         stackobj(kickedobj), kickedobj = 0;
1192     }
1193     /* if Punished hero dies during level change or dies or quits while
1194        swallowed, uball and uchain will be in limbo; put them on floor
1195        so bones will have them and object list cleanup finds them */
1196     if (uchain && uchain->where == OBJ_FREE) {
1197         /* placebc(); */
1198         lift_covet_and_placebc(override_restriction);
1199     }
1200     /* persistent inventory window now obsolete since disclosure uses
1201        a normal popup one; avoids "Bad fruit #n" when saving bones */
1202     if (iflags.perm_invent) {
1203         iflags.perm_invent = FALSE;
1204         update_inventory(); /* make interface notice the change */
1205     }
1206     return;
1207 }
1208
1209 /* called twice; first to calculate total, then to list relevant items */
1210 STATIC_OVL void
1211 artifact_score(list, counting, endwin)
1212 struct obj *list;
1213 boolean counting; /* true => add up points; false => display them */
1214 winid endwin;
1215 {
1216     char pbuf[BUFSZ];
1217     struct obj *otmp;
1218     long value, points;
1219     short dummy; /* object type returned by artifact_name() */
1220
1221     for (otmp = list; otmp; otmp = otmp->nobj) {
1222         if (otmp->oartifact || otmp->otyp == BELL_OF_OPENING
1223             || otmp->otyp == SPE_BOOK_OF_THE_DEAD
1224             || otmp->otyp == CANDELABRUM_OF_INVOCATION) {
1225             value = arti_cost(otmp); /* zorkmid value */
1226             points = value * 5 / 2;  /* score value */
1227             if (counting) {
1228                 nowrap_add(u.urexp, points);
1229             } else {
1230                 discover_object(otmp->otyp, TRUE, FALSE);
1231                 otmp->known = otmp->dknown = otmp->bknown = otmp->rknown = 1;
1232                 /* assumes artifacts don't have quan > 1 */
1233 #if 0 /*JP:T*/
1234                 Sprintf(pbuf, "%s%s (worth %ld %s and %ld points)",
1235                         the_unique_obj(otmp) ? "The " : "",
1236                         otmp->oartifact ? artifact_name(xname(otmp), &dummy)
1237                                         : OBJ_NAME(objects[otmp->otyp]),
1238                         value, currency(value), points);
1239 #else
1240                 Sprintf(pbuf, "%s(%ld%s\81C%ld\83|\83C\83\93\83g\82Ì\89¿\92l)\81C",
1241                         otmp->oartifact ? artifact_name(xname(otmp), &dummy)
1242                                         : OBJ_NAME(objects[otmp->otyp]),
1243                         value, currency(value), points);
1244 #endif
1245                 putstr(endwin, 0, pbuf);
1246             }
1247         }
1248         if (Has_contents(otmp))
1249             artifact_score(otmp->cobj, counting, endwin);
1250     }
1251 }
1252
1253 /* Be careful not to call panic from here! */
1254 void
1255 done(how)
1256 int how;
1257 {
1258     boolean survive = FALSE;
1259
1260     if (how == TRICKED) {
1261         if (killer.name[0]) {
1262             paniclog("trickery", killer.name);
1263             killer.name[0] = '\0';
1264         }
1265         if (wizard) {
1266 /*JP
1267             You("are a very tricky wizard, it seems.");
1268 */
1269             You("\82Æ\82Ä\82à\88µ\82¢\82É\82­\82¢wizard\82Ì\82æ\82¤\82¾\81D");
1270             killer.format = KILLED_BY_AN; /* reset to 0 */
1271             return;
1272         }
1273     }
1274     if (program_state.panicking
1275 #ifdef HANGUPHANDLING
1276         || program_state.done_hup
1277 #endif
1278         ) {
1279         /* skip status update if panicking or disconnected */
1280         context.botl = context.botlx = iflags.time_botl = FALSE;
1281     } else {
1282         /* otherwise force full status update */
1283         context.botlx = TRUE;
1284         bot();
1285     }
1286
1287     if (iflags.debug_fuzzer) {
1288         if (!(program_state.panicking || how == PANICKED)) {
1289             savelife(how);
1290             /* periodically restore characteristics and lost exp levels
1291                or cure lycanthropy */
1292             if (!rn2(10)) {
1293                 struct obj *potion = mksobj((u.ulycn > LOW_PM && !rn2(3))
1294                                             ? POT_WATER : POT_RESTORE_ABILITY,
1295                                             TRUE, FALSE);
1296
1297                 bless(potion);
1298                 (void) peffects(potion); /* always -1 for restore ability */
1299                 /* not useup(); we haven't put this potion into inventory */
1300                 obfree(potion, (struct obj *) 0);
1301             }
1302             killer.name[0] = '\0';
1303             killer.format = 0;
1304             return;
1305         }
1306     } else
1307     if (how == ASCENDED || (!killer.name[0] && how == GENOCIDED))
1308         killer.format = NO_KILLER_PREFIX;
1309     /* Avoid killed by "a" burning or "a" starvation */
1310     if (!killer.name[0] && (how == STARVING || how == BURNING))
1311         killer.format = KILLED_BY;
1312     if (!killer.name[0] || how >= PANICKED)
1313 /*JP
1314         Strcpy(killer.name, deaths[how]);
1315 */
1316         Strcpy(killer.name, ends[how]);
1317
1318     if (how < PANICKED) {
1319         u.umortality++;
1320         /* in case caller hasn't already done this */
1321         if (u.uhp != 0 || (Upolyd && u.mh != 0)) {
1322             /* force HP to zero in case it is still positive (some
1323                deaths aren't triggered by loss of hit points), or
1324                negative (-1 is used as a flag in some circumstances
1325                which don't apply when actually dying due to HP loss) */
1326             u.uhp = u.mh = 0;
1327             context.botl = 1;
1328         }
1329     }
1330     if (Lifesaved && (how <= GENOCIDED)) {
1331 /*JP
1332         pline("But wait...");
1333 */
1334         pline("\82¿\82å\82Á\82Æ\82Ü\82Á\82½\81D\81D\81D");
1335         makeknown(AMULET_OF_LIFE_SAVING);
1336 /*JP
1337         Your("medallion %s!", !Blind ? "begins to glow" : "feels warm");
1338 */
1339         Your("\96\82\8f\9c\82¯\82Í%s\81I", !Blind ? "\8bP\82«\82Í\82\82ß\82½" : "\92g\82©\82­\82È\82è\82Í\82\82ß\82½");
1340         if (how == CHOKING)
1341 /*JP
1342             You("vomit ...");
1343 */
1344             You("\93f\82¢\82½\81D\81D\81D");
1345 /*JP
1346         You_feel("much better!");
1347 */
1348         You("\8bC\95ª\82ª\82æ\82­\82È\82Á\82½\81I");
1349 /*JP
1350         pline_The("medallion crumbles to dust!");
1351 */
1352         pline("\96\82\8f\9c\82¯\82Í\82±\82È\82²\82È\82É\82­\82¾\82¯\82½\81I");
1353         if (uamul)
1354             useup(uamul);
1355
1356         (void) adjattrib(A_CON, -1, TRUE);
1357         savelife(how);
1358         if (how == GENOCIDED) {
1359 /*JP
1360             pline("Unfortunately you are still genocided...");
1361 */
1362             pline("\8ec\94O\82È\82ª\82ç\81C\82 \82È\82½\82Í\8bs\8eE\82³\82ê\82½\82Ü\82Ü\82¾\81D\81D\81D");
1363         } else {
1364             survive = TRUE;
1365         }
1366     }
1367     /* explore and wizard modes offer player the option to keep playing */
1368 #if 0 /*JP:T*/
1369     if (!survive && (wizard || discover) && how <= GENOCIDED
1370         && !paranoid_query(ParanoidDie, "Die?")) {
1371 #else
1372     if (!survive && (wizard || discover) && how <= GENOCIDED
1373         && !paranoid_query(ParanoidDie, "\8e\80\82ñ\82Å\82Ý\82é\81H")) {
1374 #endif
1375 /*JP
1376         pline("OK, so you don't %s.", (how == CHOKING) ? "choke" : "die");
1377 */
1378         You("\8e\80\82È\82È\82©\82Á\82½\81D");
1379         iflags.last_msg = PLNMSG_OK_DONT_DIE;
1380         savelife(how);
1381         survive = TRUE;
1382     }
1383
1384     if (survive) {
1385         killer.name[0] = '\0';
1386         killer.format = KILLED_BY_AN; /* reset to 0 */
1387         return;
1388     }
1389     really_done(how);
1390     /*NOTREACHED*/
1391 }
1392
1393 /* separated from done() in order to specify the __noreturn__ attribute */
1394 STATIC_OVL void
1395 really_done(how)
1396 int how;
1397 {
1398     boolean taken;
1399     char pbuf[BUFSZ];
1400     winid endwin = WIN_ERR;
1401     boolean bones_ok, have_windows = iflags.window_inited;
1402     struct obj *corpse = (struct obj *) 0;
1403     time_t endtime;
1404     long umoney;
1405     long tmp;
1406
1407     /*
1408      *  The game is now over...
1409      */
1410     program_state.gameover = 1;
1411     /* in case of a subsequent panic(), there's no point trying to save */
1412     program_state.something_worth_saving = 0;
1413 #ifdef HANGUPHANDLING
1414     if (program_state.done_hup)
1415         done_stopprint++;
1416 #endif
1417     /* render vision subsystem inoperative */
1418     iflags.vision_inited = 0;
1419
1420     /* maybe use up active invent item(s), place thrown/kicked missile,
1421        deal with ball and chain possibly being temporarily off the map */
1422     if (!program_state.panicking)
1423         done_object_cleanup();
1424     /* in case we're panicking; normally cleared by done_object_cleanup() */
1425     iflags.perm_invent = FALSE;
1426
1427     /* remember time of death here instead of having bones, rip, and
1428        topten figure it out separately and possibly getting different
1429        time or even day if player is slow responding to --More-- */
1430     urealtime.finish_time = endtime = getnow();
1431     urealtime.realtime += (long) (endtime - urealtime.start_timing);
1432     /* collect these for end of game disclosure (not used during play) */
1433     iflags.at_night = night();
1434     iflags.at_midnight = midnight();
1435
1436     dump_open_log(endtime);
1437     /* Sometimes you die on the first move.  Life's not fair.
1438      * On those rare occasions you get hosed immediately, go out
1439      * smiling... :-)  -3.
1440      */
1441     if (moves <= 1 && how < PANICKED && !done_stopprint)
1442 /*JP
1443         pline("Do not pass Go.  Do not collect 200 %s.", currency(200L));
1444 */
1445         pline("\92\8d\88Ó\88ê\95b\81C\89ö\89ä\88ê\90\81C\8e\80\96S\88ê\95à\81D");
1446
1447     if (have_windows)
1448         wait_synch(); /* flush screen output */
1449 #ifndef NO_SIGNAL
1450     (void) signal(SIGINT, (SIG_RET_TYPE) done_intr);
1451 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
1452     (void) signal(SIGQUIT, (SIG_RET_TYPE) done_intr);
1453     sethanguphandler(done_hangup);
1454 #endif
1455 #endif /* NO_SIGNAL */
1456
1457     bones_ok = (how < GENOCIDED) && can_make_bones();
1458
1459     if (bones_ok && launch_in_progress())
1460         force_launch_placement();
1461
1462     /* maintain ugrave_arise even for !bones_ok */
1463     if (how == PANICKED)
1464         u.ugrave_arise = (NON_PM - 3); /* no corpse, no grave */
1465     else if (how == BURNING || how == DISSOLVED) /* corpse burns up too */
1466         u.ugrave_arise = (NON_PM - 2); /* leave no corpse */
1467     else if (how == STONING)
1468         u.ugrave_arise = (NON_PM - 1); /* statue instead of corpse */
1469     else if (how == TURNED_SLIME
1470              /* it's possible to turn into slime even though green slimes
1471                 have been genocided:  genocide could occur after hero is
1472                 already infected or hero could eat a glob of one created
1473                 before genocide; don't try to arise as one if they're gone */
1474              && !(mvitals[PM_GREEN_SLIME].mvflags & G_GENOD))
1475         u.ugrave_arise = PM_GREEN_SLIME;
1476
1477     if (how == QUIT) {
1478         killer.format = NO_KILLER_PREFIX;
1479         if (u.uhp < 1) {
1480             how = DIED;
1481             u.umortality++; /* skipped above when how==QUIT */
1482 /*JP
1483             Strcpy(killer.name, "quit while already on Charon's boat");
1484 */
1485             Strcpy(killer.name, "\8eO\93r\82Ì\90ì\82Ì\93n\82µ\91D\82É\8fæ\82Á\82Ä\82¢\82é\8aÔ\82É\94²\82¯\82½");
1486         }
1487     }
1488     if (how == ESCAPED || how == PANICKED)
1489         killer.format = NO_KILLER_PREFIX;
1490
1491 #if 0 /*JP*//*\93ú\96{\8cê\82Å\82Í\8eg\82í\82È\82¢*/
1492     fixup_death(how); /* actually, fixup multi_reason */
1493 #endif
1494
1495     if (how != PANICKED) {
1496         boolean silently = done_stopprint ? TRUE : FALSE;
1497
1498         /* these affect score and/or bones, but avoid them during panic */
1499         taken = paybill((how == ESCAPED) ? -1 : (how != QUIT), silently);
1500         paygd(silently);
1501         clearpriests();
1502     } else
1503         taken = FALSE; /* lint; assert( !bones_ok ); */
1504
1505     clearlocks();
1506
1507     if (have_windows)
1508         display_nhwindow(WIN_MESSAGE, FALSE);
1509
1510     if (how != PANICKED) {
1511         struct obj *obj;
1512
1513         /*
1514          * This is needed for both inventory disclosure and dumplog.
1515          * Both are optional, so do it once here instead of duplicating
1516          * it in both of those places.
1517          */
1518         for (obj = invent; obj; obj = obj->nobj) {
1519             discover_object(obj->otyp, TRUE, FALSE);
1520             obj->known = obj->bknown = obj->dknown = obj->rknown = 1;
1521             if (Is_container(obj) || obj->otyp == STATUE)
1522                 obj->cknown = obj->lknown = 1;
1523             /* we resolve Schroedinger's cat now in case of both
1524                disclosure and dumplog, where the 50:50 chance for
1525                live cat has to be the same both times */
1526             if (SchroedingersBox(obj)) {
1527                 if (!Schroedingers_cat) {
1528                     /* tell observe_quantum_cat() not to create a cat; if it
1529                        chooses live cat in this situation, it will leave the
1530                        SchroedingersBox flag set (for container_contents()) */
1531                     observe_quantum_cat(obj, FALSE, FALSE);
1532                     if (SchroedingersBox(obj))
1533                         Schroedingers_cat = TRUE;
1534                 } else
1535                     obj->spe = 0; /* ordinary box with cat corpse in it */
1536             }
1537         }
1538
1539         if (strcmp(flags.end_disclose, "none"))
1540             disclose(how, taken);
1541
1542         dump_everything(how, endtime);
1543     }
1544
1545     /* if pets will contribute to score, populate mydogs list now
1546        (bones creation isn't a factor, but pline() messaging is; used to
1547        be done even sooner, but we need it to come after dump_everything()
1548        so that any accompanying pets are still on the map during dump) */
1549     if (how == ESCAPED || how == ASCENDED)
1550         keepdogs(TRUE);
1551
1552     /* finish_paybill should be called after disclosure but before bones */
1553     if (bones_ok && taken)
1554         finish_paybill();
1555
1556     /* grave creation should be after disclosure so it doesn't have
1557        this grave in the current level's features for #overview */
1558     if (bones_ok && u.ugrave_arise == NON_PM
1559         && !(mvitals[u.umonnum].mvflags & G_NOCORPSE)) {
1560         int mnum = u.umonnum;
1561
1562         if (!Upolyd) {
1563             /* Base corpse on race when not poly'd since original u.umonnum
1564                is based on role, and all role monsters are human. */
1565             mnum = (flags.female && urace.femalenum != NON_PM)
1566                        ? urace.femalenum
1567                        : urace.malenum;
1568         }
1569         corpse = mk_named_object(CORPSE, &mons[mnum], u.ux, u.uy, plname);
1570 /*JP
1571         Sprintf(pbuf, "%s, ", plname);
1572 */
1573         Sprintf(pbuf, "%s\82Ì\95æ\81C", plname);
1574         formatkiller(eos(pbuf), sizeof pbuf - strlen(pbuf), how, TRUE);
1575         make_grave(u.ux, u.uy, pbuf);
1576     }
1577     pbuf[0] = '\0'; /* clear grave text; also lint suppression */
1578
1579     /* calculate score, before creating bones [container gold] */
1580     {
1581         int deepest = deepest_lev_reached(FALSE);
1582
1583         umoney = money_cnt(invent);
1584         tmp = u.umoney0;
1585         umoney += hidden_gold(); /* accumulate gold from containers */
1586         tmp = umoney - tmp;      /* net gain */
1587
1588         if (tmp < 0L)
1589             tmp = 0L;
1590         if (how < PANICKED)
1591             tmp -= tmp / 10L;
1592         tmp += 50L * (long) (deepest - 1);
1593         if (deepest > 20)
1594             tmp += 1000L * (long) ((deepest > 30) ? 10 : deepest - 20);
1595         nowrap_add(u.urexp, tmp);
1596
1597         /* ascension gives a score bonus iff offering to original deity */
1598         if (how == ASCENDED && u.ualign.type == u.ualignbase[A_ORIGINAL]) {
1599             /* retaining original alignment: score *= 2;
1600                converting, then using helm-of-OA to switch back: *= 1.5 */
1601             tmp = (u.ualignbase[A_CURRENT] == u.ualignbase[A_ORIGINAL])
1602                       ? u.urexp
1603                       : (u.urexp / 2L);
1604             nowrap_add(u.urexp, tmp);
1605         }
1606     }
1607
1608     if (u.ugrave_arise >= LOW_PM && !done_stopprint) {
1609         /* give this feedback even if bones aren't going to be created,
1610            so that its presence or absence doesn't tip off the player to
1611            new bones or their lack; it might be a lie if makemon fails */
1612 #if 0 /*JP:T*/
1613         Your("%s as %s...",
1614              (u.ugrave_arise != PM_GREEN_SLIME)
1615                  ? "body rises from the dead"
1616                  : "revenant persists",
1617              an(mons[u.ugrave_arise].mname));
1618 #else
1619         Your("%s as %s\82É\82È\82Á\82½\81D\81D\81D",
1620              (u.ugrave_arise != PM_GREEN_SLIME)
1621                  ? "\91Ì\82Í\8e\80\91Ì\82©\82ç\91h\82Á\82Ä"
1622                  : "\96S\97ì\82Í",
1623              mons[u.ugrave_arise].mname);
1624 #endif
1625         display_nhwindow(WIN_MESSAGE, FALSE);
1626     }
1627
1628     if (bones_ok) {
1629 /*JP
1630         if (!wizard || paranoid_query(ParanoidBones, "Save bones?"))
1631 */
1632         if (!wizard || paranoid_query(ParanoidBones, "\8d\9c\82ð\82¤\82ß\82é\81H"))
1633             savebones(how, endtime, corpse);
1634         /* corpse may be invalid pointer now so
1635             ensure that it isn't used again */
1636         corpse = (struct obj *) 0;
1637     }
1638
1639     /* update gold for the rip output, which can't use hidden_gold()
1640        (containers will be gone by then if bones just got saved...) */
1641     done_money = umoney;
1642
1643     /* clean up unneeded windows */
1644     if (have_windows) {
1645         wait_synch();
1646         free_pickinv_cache(); /* extra persistent window if perm_invent */
1647         if (WIN_INVEN != WIN_ERR) {
1648             destroy_nhwindow(WIN_INVEN),  WIN_INVEN = WIN_ERR;
1649             /* precaution in case any late update_inventory() calls occur */
1650             iflags.perm_invent = 0;
1651         }
1652         display_nhwindow(WIN_MESSAGE, TRUE);
1653         destroy_nhwindow(WIN_MAP),  WIN_MAP = WIN_ERR;
1654         if (WIN_STATUS != WIN_ERR)
1655             destroy_nhwindow(WIN_STATUS),  WIN_STATUS = WIN_ERR;
1656         destroy_nhwindow(WIN_MESSAGE),  WIN_MESSAGE = WIN_ERR;
1657
1658         if (!done_stopprint || flags.tombstone)
1659             endwin = create_nhwindow(NHW_TEXT);
1660
1661         if (how < GENOCIDED && flags.tombstone && endwin != WIN_ERR)
1662             outrip(endwin, how, endtime);
1663     } else
1664         done_stopprint = 1; /* just avoid any more output */
1665
1666 #ifdef DUMPLOG
1667     /* 'how' reasons beyond genocide shouldn't show tombstone;
1668        for normal end of game, genocide doesn't either */
1669     if (how <= GENOCIDED) {
1670         dump_redirect(TRUE);
1671         if (iflags.in_dumplog)
1672             genl_outrip(0, how, endtime);
1673         dump_redirect(FALSE);
1674     }
1675 #endif
1676     if (u.uhave.amulet) {
1677 /*JP
1678         Strcat(killer.name, " (with the Amulet)");
1679 */
1680         Strcat(killer.name, "\96\82\8f\9c\82¯\82ð\8eè\82É");
1681     } else if (how == ESCAPED) {
1682         if (Is_astralevel(&u.uz)) /* offered Amulet to wrong deity */
1683 /*JP
1684             Strcat(killer.name, " (in celestial disgrace)");
1685 */
1686             Strcat(killer.name, "\93V\8fã\82Å\92p\90J\82ð\8eó\82¯\92E\8fo\82µ\82½");
1687         else if (carrying(FAKE_AMULET_OF_YENDOR))
1688 /*JP
1689             Strcat(killer.name, " (with a fake Amulet)");
1690 */
1691             Strcat(killer.name, "\8bU\95¨\82Ì\96\82\8f\9c\82¯\82ð\92Í\82Ü\82³\82ê\92E\8fo\82µ\82½");
1692         /* don't bother counting to see whether it should be plural */
1693     }
1694
1695 #if 0 /*JP:T*/
1696     Sprintf(pbuf, "%s %s the %s...", Goodbye(), plname,
1697             (how != ASCENDED)
1698                 ? (const char *) ((flags.female && urole.name.f)
1699                     ? urole.name.f
1700                     : urole.name.m)
1701                 : (const char *) (flags.female ? "Demigoddess" : "Demigod"));
1702 #else
1703     Sprintf(pbuf, "%s%s\82Ì%s\81D\81D\81D", Goodbye(),
1704             (how != ASCENDED)
1705                 ? (const char *) ((flags.female && urole.name.f)
1706                     ? urole.name.f
1707                     : urole.name.m)
1708                 : (const char *) (flags.female ? "\8f\97\90_" : "\90_"),
1709                 plname);
1710 #endif
1711     dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
1712     dump_forward_putstr(endwin, 0, "", done_stopprint);
1713
1714     if (how == ESCAPED || how == ASCENDED) {
1715         struct monst *mtmp;
1716         struct obj *otmp;
1717         register struct val_list *val;
1718         register int i;
1719
1720         for (val = valuables; val->list; val++)
1721             for (i = 0; i < val->size; i++) {
1722                 val->list[i].count = 0L;
1723             }
1724         get_valuables(invent);
1725
1726         /* add points for collected valuables */
1727         for (val = valuables; val->list; val++)
1728             for (i = 0; i < val->size; i++)
1729                 if (val->list[i].count != 0L) {
1730                     tmp = val->list[i].count
1731                           * (long) objects[val->list[i].typ].oc_cost;
1732                     nowrap_add(u.urexp, tmp);
1733                 }
1734
1735         /* count the points for artifacts */
1736         artifact_score(invent, TRUE, endwin);
1737
1738         viz_array[0][0] |= IN_SIGHT; /* need visibility for naming */
1739         mtmp = mydogs;
1740 /*JP
1741         Strcpy(pbuf, "You");
1742 */
1743         Strcpy(pbuf, "\82 \82È\82½");
1744         if (mtmp || Schroedingers_cat) {
1745             while (mtmp) {
1746 /*JP
1747                 Sprintf(eos(pbuf), " and %s", mon_nam(mtmp));
1748 */
1749                 Sprintf(eos(pbuf), "\82Æ%s", mon_nam(mtmp));
1750                 if (mtmp->mtame)
1751                     nowrap_add(u.urexp, mtmp->mhp);
1752                 mtmp = mtmp->nmon;
1753             }
1754             /* [it might be more robust to create a housecat and add it to
1755                mydogs; it doesn't have to be placed on the map for that] */
1756             if (Schroedingers_cat) {
1757                 int mhp, m_lev = adj_lev(&mons[PM_HOUSECAT]);
1758
1759                 mhp = d(m_lev, 8);
1760                 nowrap_add(u.urexp, mhp);
1761 /*JP
1762                 Strcat(eos(pbuf), " and Schroedinger's cat");
1763 */
1764                 Strcat(eos(pbuf), "\82Æ\83V\83\85\83\8c\83f\83B\83\93\83K\81[\82Ì\94L");
1765             }
1766 #if 1 /*JP*/
1767             if (!done_stopprint)
1768                 Strcat(pbuf, "\82Í");
1769 #endif
1770             dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
1771             pbuf[0] = '\0';
1772         } else {
1773 /*JP
1774             Strcat(pbuf, " ");
1775 */
1776             Strcat(pbuf, "\82Í");
1777         }
1778 #if 0 /*JP:T*/
1779         Sprintf(eos(pbuf), "%s with %ld point%s,",
1780                 how == ASCENDED ? "went to your reward"
1781                                  : "escaped from the dungeon",
1782                 u.urexp, plur(u.urexp));
1783 #else
1784         Sprintf(eos(pbuf), "%ld\83|\83C\83\93\83g\83}\81[\83N\82µ%s\81D",
1785                 u.urexp,
1786                 how==ASCENDED ? "\8f¸\93V\82µ\82½" : "\96À\8b{\82©\82ç\92E\8fo\82µ\82½");
1787 #endif
1788         dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
1789
1790         if (!done_stopprint)
1791             artifact_score(invent, FALSE, endwin); /* list artifacts */
1792 #ifdef DUMPLOG
1793         dump_redirect(TRUE);
1794         if (iflags.in_dumplog)
1795             artifact_score(invent, FALSE, 0);
1796         dump_redirect(FALSE);
1797 #endif
1798
1799         /* list valuables here */
1800         for (val = valuables; val->list; val++) {
1801             sort_valuables(val->list, val->size);
1802             for (i = 0; i < val->size && !done_stopprint; i++) {
1803                 int typ = val->list[i].typ;
1804                 long count = val->list[i].count;
1805
1806                 if (count == 0L)
1807                     continue;
1808                 if (objects[typ].oc_class != GEM_CLASS || typ <= LAST_GEM) {
1809                     otmp = mksobj(typ, FALSE, FALSE);
1810                     discover_object(otmp->otyp, TRUE, FALSE);
1811                     otmp->known = 1;  /* for fake amulets */
1812                     otmp->dknown = 1; /* seen it (blindness fix) */
1813                     if (has_oname(otmp))
1814                         free_oname(otmp);
1815                     otmp->quan = count;
1816 #if 0 /*JP:T*/
1817                     Sprintf(pbuf, "%8ld %s (worth %ld %s),", count,
1818                             xname(otmp), count * (long) objects[typ].oc_cost,
1819                             currency(2L));
1820 #else
1821                     Sprintf(pbuf, "%ld\8cÂ\82Ì%s(%ld%s\82Ì\89¿\92l)\81C", count,
1822                             xname(otmp), count * (long) objects[typ].oc_cost,
1823                             currency(2L));
1824 #endif
1825                     obfree(otmp, (struct obj *) 0);
1826                 } else {
1827 #if 0 /*JP:T*/
1828                     Sprintf(pbuf, "%8ld worthless piece%s of colored glass,",
1829                             count, plur(count));
1830 #else
1831                     Sprintf(pbuf, "%ld\8cÂ\82Ì\89¿\92l\82Ì\82È\82¢\90F\82Â\82«\83K\83\89\83X\81C",
1832                             count);
1833 #endif
1834                 }
1835                 dump_forward_putstr(endwin, 0, pbuf, 0);
1836             }
1837         }
1838
1839     } else {
1840         /* did not escape or ascend */
1841         if (u.uz.dnum == 0 && u.uz.dlevel <= 0) {
1842             /* level teleported out of the dungeon; `how' is DIED,
1843                due to falling or to "arriving at heaven prematurely" */
1844 #if 0 /*JP:T*/
1845             Sprintf(pbuf, "You %s beyond the confines of the dungeon",
1846                     (u.uz.dlevel < 0) ? "passed away" : ends[how]);
1847 #else
1848             Sprintf(pbuf, "\96À\8b{\82Ì\97Ì\88æ\82ð\89z\82¦%s\81D",
1849                     (u.uz.dlevel < 0) ? "\8fÁ\82¦\82³\82Á\82½" : ends[how]);
1850 #endif
1851         } else {
1852             /* more conventional demise */
1853             const char *where = dungeons[u.uz.dnum].dname;
1854
1855             if (Is_astralevel(&u.uz))
1856 /*JP
1857                 where = "The Astral Plane";
1858 */
1859                 where = "\90¸\97ì\8aE\82É\82Ä";
1860 /*JP
1861             Sprintf(pbuf, "You %s in %s", ends[how], where);
1862 */
1863             Sprintf(pbuf, "\82 \82È\82½\82Í%s", where);
1864             if (!In_endgame(&u.uz) && !Is_knox(&u.uz))
1865 #if 0 /*JP:T*/
1866                 Sprintf(eos(pbuf), " on dungeon level %d",
1867                         In_quest(&u.uz) ? dunlev(&u.uz) : depth(&u.uz));
1868 #else
1869                 Sprintf(eos(pbuf), "\82Ì\92n\89º%d\8aK\82Å",
1870                         In_quest(&u.uz) ? dunlev(&u.uz) : depth(&u.uz));
1871 #endif
1872         }
1873
1874 /*JP
1875         Sprintf(eos(pbuf), " with %ld point%s,", u.urexp, plur(u.urexp));
1876 */
1877         Sprintf(eos(pbuf), " %ld\83|\83C\83\93\83g\82ð\83}\81[\83N\82µ\81C", u.urexp);
1878         dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
1879     }
1880
1881 #if 0 /*JP:T*/
1882     Sprintf(pbuf, "and %ld piece%s of gold, after %ld move%s.", umoney,
1883             plur(umoney), moves, plur(moves));
1884 #else
1885     Sprintf(pbuf, "%ld\96\87\82Ì\8bà\89Ý\82ð\8e\9d\82Á\82Ä\81C%ld\95à\93®\82¢\82½\81D", umoney,
1886             moves);
1887 #endif
1888     dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
1889 #if 0 /*JP:T*/
1890     Sprintf(pbuf,
1891             "You were level %d with a maximum of %d hit point%s when you %s.",
1892             u.ulevel, u.uhpmax, plur(u.uhpmax), ends[how]);
1893 #else
1894     Sprintf(pbuf,
1895             "%s\82Æ\82«\81C\82 \82È\82½\82Í\83\8c\83x\83\8b%u\82Å\81C\8dÅ\91å\91Ì\97Í\82Í%d\82Å\82 \82Á\82½\81D",
1896             ends[how],u.ulevel, u.uhpmax);
1897 #endif
1898     dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
1899     dump_forward_putstr(endwin, 0, "", done_stopprint);
1900     if (!done_stopprint)
1901         display_nhwindow(endwin, TRUE);
1902     if (endwin != WIN_ERR)
1903         destroy_nhwindow(endwin);
1904
1905     dump_close_log();
1906     /* "So when I die, the first thing I will see in Heaven is a
1907      * score list?" */
1908     if (have_windows && !iflags.toptenwin)
1909         exit_nhwindows((char *) 0), have_windows = FALSE;
1910     topten(how, endtime);
1911     if (have_windows)
1912         exit_nhwindows((char *) 0);
1913
1914     if (done_stopprint) {
1915         raw_print("");
1916         raw_print("");
1917     }
1918     nh_terminate(EXIT_SUCCESS);
1919 }
1920
1921 void
1922 container_contents(list, identified, all_containers, reportempty)
1923 struct obj *list;
1924 boolean identified, all_containers, reportempty;
1925 {
1926     register struct obj *box, *obj;
1927     char buf[BUFSZ];
1928     boolean cat, dumping = iflags.in_dumplog;
1929
1930     for (box = list; box; box = box->nobj) {
1931         if (Is_container(box) || box->otyp == STATUE) {
1932             if (!box->cknown || (identified && !box->lknown)) {
1933                 box->cknown = 1; /* we're looking at the contents now */
1934                 if (identified)
1935                     box->lknown = 1;
1936                 update_inventory();
1937             }
1938             if (box->otyp == BAG_OF_TRICKS) {
1939                 continue; /* wrong type of container */
1940             } else if (box->cobj) {
1941                 winid tmpwin = create_nhwindow(NHW_MENU);
1942                 Loot *sortedcobj, *srtc;
1943                 unsigned sortflags;
1944
1945                 /* at this stage, the SchroedingerBox() flag is only set
1946                    if the cat inside the box is alive; the box actually
1947                    contains a cat corpse that we'll pretend is not there;
1948                    for dead cat, the flag will be clear and there'll be
1949                    a cat corpse inside the box; either way, inventory
1950                    reports the box as containing "1 item" */
1951                 cat = SchroedingersBox(box);
1952
1953 /*JP
1954                 Sprintf(buf, "Contents of %s:", the(xname(box)));
1955 */
1956                 Sprintf(buf, "%s\82Ì\92\86\90g\81F", the(xname(box)));
1957                 putstr(tmpwin, 0, buf);
1958                 if (!dumping)
1959                     putstr(tmpwin, 0, "");
1960                 buf[0] = buf[1] = ' '; /* two leading spaces */
1961                 if (box->cobj && !cat) {
1962                     sortflags = (((flags.sortloot == 'l'
1963                                    || flags.sortloot == 'f')
1964                                      ? SORTLOOT_LOOT : 0)
1965                                  | (flags.sortpack ? SORTLOOT_PACK : 0));
1966                     sortedcobj = sortloot(&box->cobj, sortflags, FALSE,
1967                                           (boolean FDECL((*), (OBJ_P))) 0);
1968                     for (srtc = sortedcobj; ((obj = srtc->obj) != 0); ++srtc) {
1969                         if (identified) {
1970                             discover_object(obj->otyp, TRUE, FALSE);
1971                             obj->known = obj->bknown = obj->dknown
1972                                 = obj->rknown = 1;
1973                             if (Is_container(obj) || obj->otyp == STATUE)
1974                                 obj->cknown = obj->lknown = 1;
1975                         }
1976                         Strcpy(&buf[2], doname_with_price(obj));
1977                         putstr(tmpwin, 0, buf);
1978                     }
1979                     unsortloot(&sortedcobj);
1980                 } else if (cat) {
1981 /*JP
1982                     Strcpy(&buf[2], "Schroedinger's cat!");
1983 */
1984                     Strcpy(&buf[2], "\83V\83\85\83\8c\83f\83B\83\93\83K\81[\82Ì\94L\81I");
1985                     putstr(tmpwin, 0, buf);
1986                 }
1987                 if (dumping)
1988                     putstr(0, 0, "");
1989                 display_nhwindow(tmpwin, TRUE);
1990                 destroy_nhwindow(tmpwin);
1991                 if (all_containers)
1992                     container_contents(box->cobj, identified, TRUE,
1993                                        reportempty);
1994             } else if (reportempty) {
1995 /*JP
1996                 pline("%s is empty.", upstart(thesimpleoname(box)));
1997 */
1998                 pline("%s\82Í\8bó\82Á\82Û\82¾\81D", xname(box));
1999                 display_nhwindow(WIN_MESSAGE, FALSE);
2000             }
2001         }
2002         if (!all_containers)
2003             break;
2004     }
2005 }
2006
2007 /* should be called with either EXIT_SUCCESS or EXIT_FAILURE */
2008 void
2009 nh_terminate(status)
2010 int status;
2011 {
2012     program_state.in_moveloop = 0; /* won't be returning to normal play */
2013 #ifdef MAC
2014     getreturn("to exit");
2015 #endif
2016     /* don't bother to try to release memory if we're in panic mode, to
2017        avoid trouble in case that happens to be due to memory problems */
2018     if (!program_state.panicking) {
2019         freedynamicdata();
2020         dlb_cleanup();
2021     }
2022
2023 #ifdef VMS
2024     /*
2025      *  This is liable to draw a warning if compiled with gcc, but it's
2026      *  more important to flag panic() -> really_done() -> nh_terminate()
2027      *  as __noreturn__ then to avoid the warning.
2028      */
2029     /* don't call exit() if already executing within an exit handler;
2030        that would cancel any other pending user-mode handlers */
2031     if (program_state.exiting)
2032         return;
2033 #endif
2034     program_state.exiting = 1;
2035 #if 1 /*JP*/
2036     jputchar('\0'); /* reset terminal */
2037 #endif
2038     nethack_exit(status);
2039 }
2040
2041 enum vanq_order_modes {
2042     VANQ_MLVL_MNDX = 0,
2043     VANQ_MSTR_MNDX,
2044     VANQ_ALPHA_SEP,
2045     VANQ_ALPHA_MIX,
2046     VANQ_MCLS_HTOL,
2047     VANQ_MCLS_LTOH,
2048     VANQ_COUNT_H_L,
2049     VANQ_COUNT_L_H,
2050
2051     NUM_VANQ_ORDER_MODES
2052 };
2053
2054 static const char *vanqorders[NUM_VANQ_ORDER_MODES] = {
2055     "traditional: by monster level, by internal monster index",
2056     "by monster toughness, by internal monster index",
2057     "alphabetically, first unique monsters, then others",
2058     "alphabetically, unique monsters and others intermixed",
2059     "by monster class, high to low level within class",
2060     "by monster class, low to high level within class",
2061     "by count, high to low, by internal index within tied count",
2062     "by count, low to high, by internal index within tied count",
2063 };
2064 static int vanq_sortmode = VANQ_MLVL_MNDX;
2065
2066 STATIC_PTR int CFDECLSPEC
2067 vanqsort_cmp(vptr1, vptr2)
2068 const genericptr vptr1;
2069 const genericptr vptr2;
2070 {
2071     int indx1 = *(short *) vptr1, indx2 = *(short *) vptr2,
2072         mlev1, mlev2, mstr1, mstr2, uniq1, uniq2, died1, died2, res;
2073     const char *name1, *name2, *punct;
2074     schar mcls1, mcls2;
2075
2076     switch (vanq_sortmode) {
2077     default:
2078     case VANQ_MLVL_MNDX:
2079         /* sort by monster level */
2080         mlev1 = mons[indx1].mlevel, mlev2 = mons[indx2].mlevel;
2081         res = mlev2 - mlev1; /* mlevel high to low */
2082         break;
2083     case VANQ_MSTR_MNDX:
2084         /* sort by monster toughness */
2085         mstr1 = mons[indx1].difficulty, mstr2 = mons[indx2].difficulty;
2086         res = mstr2 - mstr1; /* monstr high to low */
2087         break;
2088     case VANQ_ALPHA_SEP:
2089         uniq1 = ((mons[indx1].geno & G_UNIQ) && indx1 != PM_HIGH_PRIEST);
2090         uniq2 = ((mons[indx2].geno & G_UNIQ) && indx2 != PM_HIGH_PRIEST);
2091         if (uniq1 ^ uniq2) { /* one or other uniq, but not both */
2092             res = uniq2 - uniq1;
2093             break;
2094         } /* else both unique or neither unique */
2095         /*FALLTHRU*/
2096     case VANQ_ALPHA_MIX:
2097         name1 = mons[indx1].mname, name2 = mons[indx2].mname;
2098         res = strcmpi(name1, name2); /* caseblind alhpa, low to high */
2099         break;
2100     case VANQ_MCLS_HTOL:
2101     case VANQ_MCLS_LTOH:
2102         /* mons[].mlet is a small integer, 1..N, of type plain char;
2103            if 'char' happens to be unsigned, (mlet1 - mlet2) would yield
2104            an inappropriate result when mlet2 is greater than mlet1,
2105            so force our copies (mcls1, mcls2) to be signed */
2106         mcls1 = (schar) mons[indx1].mlet, mcls2 = (schar) mons[indx2].mlet;
2107         /* S_ANT through S_ZRUTY correspond to lowercase monster classes,
2108            S_ANGEL through S_ZOMBIE correspond to uppercase, and various
2109            punctuation characters are used for classes beyond those */
2110         if (mcls1 > S_ZOMBIE && mcls2 > S_ZOMBIE) {
2111             /* force a specific order to the punctuation classes that's
2112                different from the internal order;
2113                internal order is ok if neither or just one is punctuation
2114                since letters have lower values so come out before punct */
2115             static const char punctclasses[] = {
2116                 S_LIZARD, S_EEL, S_GOLEM, S_GHOST, S_DEMON, S_HUMAN, '\0'
2117             };
2118
2119             if ((punct = index(punctclasses, mcls1)) != 0)
2120                 mcls1 = (schar) (S_ZOMBIE + 1 + (int) (punct - punctclasses));
2121             if ((punct = index(punctclasses, mcls2)) != 0)
2122                 mcls2 = (schar) (S_ZOMBIE + 1 + (int) (punct - punctclasses));
2123         }
2124         res = mcls1 - mcls2; /* class */
2125         if (res == 0) {
2126             mlev1 = mons[indx1].mlevel, mlev2 = mons[indx2].mlevel;
2127             res = mlev1 - mlev2; /* mlevel low to high */
2128             if (vanq_sortmode == VANQ_MCLS_HTOL)
2129                 res = -res; /* mlevel high to low */
2130         }
2131         break;
2132     case VANQ_COUNT_H_L:
2133     case VANQ_COUNT_L_H:
2134         died1 = mvitals[indx1].died, died2 = mvitals[indx2].died;
2135         res = died2 - died1; /* dead count high to low */
2136         if (vanq_sortmode == VANQ_COUNT_L_H)
2137             res = -res; /* dead count low to high */
2138         break;
2139     }
2140     /* tiebreaker: internal mons[] index */
2141     if (res == 0)
2142         res = indx1 - indx2; /* mndx low to high */
2143     return res;
2144 }
2145
2146 /* returns -1 if cancelled via ESC */
2147 STATIC_OVL int
2148 set_vanq_order()
2149 {
2150     winid tmpwin;
2151     menu_item *selected;
2152     anything any;
2153     int i, n, choice;
2154
2155     tmpwin = create_nhwindow(NHW_MENU);
2156     start_menu(tmpwin);
2157     any = zeroany; /* zero out all bits */
2158     for (i = 0; i < SIZE(vanqorders); i++) {
2159         if (i == VANQ_ALPHA_MIX || i == VANQ_MCLS_HTOL) /* skip these */
2160             continue;
2161         any.a_int = i + 1;
2162         add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, vanqorders[i],
2163                  (i == vanq_sortmode) ? MENU_SELECTED : MENU_UNSELECTED);
2164     }
2165 /*JP
2166     end_menu(tmpwin, "Sort order for vanquished monster counts");
2167 */
2168     end_menu(tmpwin, "\93|\82µ\82½\93G\82Ì\83\\81[\83g\8f\87");
2169
2170     n = select_menu(tmpwin, PICK_ONE, &selected);
2171     destroy_nhwindow(tmpwin);
2172     if (n > 0) {
2173         choice = selected[0].item.a_int - 1;
2174         /* skip preselected entry if we have more than one item chosen */
2175         if (n > 1 && choice == vanq_sortmode)
2176             choice = selected[1].item.a_int - 1;
2177         free((genericptr_t) selected);
2178         vanq_sortmode = choice;
2179     }
2180     return (n < 0) ? -1 : vanq_sortmode;
2181 }
2182
2183 /* #vanquished command */
2184 int
2185 dovanquished()
2186 {
2187     list_vanquished('a', FALSE);
2188     return 0;
2189 }
2190
2191 /* high priests aren't unique but are flagged as such to simplify something */
2192 #define UniqCritterIndx(mndx) ((mons[mndx].geno & G_UNIQ) \
2193                                && mndx != PM_HIGH_PRIEST)
2194
2195 STATIC_OVL void
2196 list_vanquished(defquery, ask)
2197 char defquery;
2198 boolean ask;
2199 {
2200     register int i;
2201     int pfx, nkilled;
2202     unsigned ntypes, ni;
2203     long total_killed = 0L;
2204     winid klwin;
2205     short mindx[NUMMONS];
2206     char c, buf[BUFSZ], buftoo[BUFSZ];
2207     boolean dumping; /* for DUMPLOG; doesn't need to be conditional */
2208
2209     dumping = (defquery == 'd');
2210     if (dumping)
2211         defquery = 'y';
2212
2213     /* get totals first */
2214     ntypes = 0;
2215     for (i = LOW_PM; i < NUMMONS; i++) {
2216         if ((nkilled = (int) mvitals[i].died) == 0)
2217             continue;
2218         mindx[ntypes++] = i;
2219         total_killed += (long) nkilled;
2220     }
2221
2222     /* vanquished creatures list;
2223      * includes all dead monsters, not just those killed by the player
2224      */
2225     if (ntypes != 0) {
2226         char mlet, prev_mlet = 0; /* used as small integer, not character */
2227         boolean class_header, uniq_header, was_uniq = FALSE;
2228
2229 #if 0 /*JP:T*/
2230         c = ask ? yn_function(
2231                             "Do you want an account of creatures vanquished?",
2232                               ynaqchars, defquery)
2233                 : defquery;
2234 #else
2235         c = ask ? yn_function(
2236                             "\93|\82µ\82½\93G\82Ì\88ê\97\97\82ð\8c©\82Ü\82·\82©\81H",
2237                               ynqchars, defquery)
2238                 : defquery;
2239 #endif
2240         if (c == 'q')
2241             done_stopprint++;
2242         if (c == 'y' || c == 'a') {
2243             if (c == 'a') { /* ask player to choose sort order */
2244                 /* choose value for vanq_sortmode via menu; ESC cancels list
2245                    of vanquished monsters but does not set 'done_stopprint' */
2246                 if (set_vanq_order() < 0)
2247                     return;
2248             }
2249             uniq_header = (vanq_sortmode == VANQ_ALPHA_SEP);
2250             class_header = (vanq_sortmode == VANQ_MCLS_LTOH
2251                             || vanq_sortmode == VANQ_MCLS_HTOL);
2252
2253             klwin = create_nhwindow(NHW_MENU);
2254 /*JP
2255             putstr(klwin, 0, "Vanquished creatures:");
2256 */
2257             putstr(klwin, 0, "\93|\82µ\82½\93G\81F");
2258             if (!dumping)
2259                 putstr(klwin, 0, "");
2260
2261             qsort((genericptr_t) mindx, ntypes, sizeof *mindx, vanqsort_cmp);
2262             for (ni = 0; ni < ntypes; ni++) {
2263                 i = mindx[ni];
2264                 nkilled = mvitals[i].died;
2265                 mlet = mons[i].mlet;
2266                 if (class_header && mlet != prev_mlet) {
2267                     Strcpy(buf, def_monsyms[(int) mlet].explain);
2268                     putstr(klwin, ask ? 0 : iflags.menu_headings,
2269                            upstart(buf));
2270                     prev_mlet = mlet;
2271                 }
2272                 if (UniqCritterIndx(i)) {
2273 #if 0 /*JP*/
2274                     Sprintf(buf, "%s%s",
2275                             !type_is_pname(&mons[i]) ? "the " : "",
2276                             mons[i].mname);
2277 #else
2278                     Sprintf(buf, "%s", mons[i].mname);
2279 #endif
2280                     if (nkilled > 1) {
2281 #if 0 /*JP:T*/
2282                         switch (nkilled) {
2283                         case 2:
2284                             Sprintf(eos(buf), " (twice)");
2285                             break;
2286                         case 3:
2287                             Sprintf(eos(buf), " (thrice)");
2288                             break;
2289                         default:
2290                             Sprintf(eos(buf), " (%d times)", nkilled);
2291                             break;
2292                         }
2293 #else
2294                         Sprintf(eos(buf)," (%d\89ñ)", nkilled);
2295 #endif
2296                     }
2297                     was_uniq = TRUE;
2298                 } else {
2299                     if (uniq_header && was_uniq) {
2300                         putstr(klwin, 0, "");
2301                         was_uniq = FALSE;
2302                     }
2303                     /* trolls or undead might have come back,
2304                        but we don't keep track of that */
2305                     if (nkilled == 1)
2306                         Strcpy(buf, an(mons[i].mname));
2307                     else
2308 #if 0 /*JP:T*/
2309                         Sprintf(buf, "%3d %s", nkilled,
2310                                 makeplural(mons[i].mname));
2311 #else
2312                         Sprintf(buf, "%d\91Ì\82Ì%s", nkilled,
2313                                 mons[i].mname);
2314 #endif
2315                 }
2316                 /* number of leading spaces to match 3 digit prefix */
2317 #if 0 /*JP*/
2318                 pfx = !strncmpi(buf, "the ", 3) ? 0
2319                       : !strncmpi(buf, "an ", 3) ? 1
2320                         : !strncmpi(buf, "a ", 2) ? 2
2321                           : !digit(buf[2]) ? 4 : 0;
2322 #else
2323                 pfx = !digit(buf[2]) ? 4 : 0;
2324 #endif
2325                 if (class_header)
2326                     ++pfx;
2327                 Sprintf(buftoo, "%*s%s", pfx, "", buf);
2328                 putstr(klwin, 0, buftoo);
2329             }
2330             /*
2331              * if (Hallucination)
2332              *     putstr(klwin, 0, "and a partridge in a pear tree");
2333              */
2334             if (ntypes > 1) {
2335                 if (!dumping)
2336                     putstr(klwin, 0, "");
2337 /*JP
2338                 Sprintf(buf, "%ld creatures vanquished.", total_killed);
2339 */
2340                 Sprintf(buf, "%ld\95C\82Ì\93G\82ð\93|\82µ\82½\81D", total_killed);
2341                 putstr(klwin, 0, buf);
2342             }
2343             display_nhwindow(klwin, TRUE);
2344             destroy_nhwindow(klwin);
2345         }
2346     } else if (defquery == 'a') {
2347         /* #dovanquished rather than final disclosure, so pline() is ok */
2348 /*JP
2349         pline("No creatures have been vanquished.");
2350 */
2351         pline("\93|\82µ\82½\93G\82Í\82¢\82È\82©\82Á\82½\81D");
2352 #ifdef DUMPLOG
2353     } else if (dumping) {
2354 #if 0 /*JP:T*/
2355         putstr(0, 0, "No creatures were vanquished."); /* not pline() */
2356 #else
2357         putstr(0, 0, "\93|\82µ\82½\93G\82Í\82¢\82È\82©\82Á\82½\81D"); /* not pline() */
2358 #endif
2359 #endif
2360     }
2361 }
2362
2363 /* number of monster species which have been genocided */
2364 int
2365 num_genocides()
2366 {
2367     int i, n = 0;
2368
2369     for (i = LOW_PM; i < NUMMONS; ++i) {
2370         if (mvitals[i].mvflags & G_GENOD) {
2371             ++n;
2372             if (UniqCritterIndx(i))
2373                 impossible("unique creature '%d: %s' genocided?",
2374                            i, mons[i].mname);
2375         }
2376     }
2377     return n;
2378 }
2379
2380 STATIC_OVL int
2381 num_extinct()
2382 {
2383     int i, n = 0;
2384
2385     for (i = LOW_PM; i < NUMMONS; ++i) {
2386         if (UniqCritterIndx(i))
2387             continue;
2388         if ((mvitals[i].mvflags & G_GONE) == G_EXTINCT)
2389             ++n;
2390     }
2391     return n;
2392 }
2393
2394 STATIC_OVL void
2395 list_genocided(defquery, ask)
2396 char defquery;
2397 boolean ask;
2398 {
2399     register int i;
2400     int ngenocided, nextinct;
2401     char c;
2402     winid klwin;
2403     char buf[BUFSZ];
2404     boolean dumping; /* for DUMPLOG; doesn't need to be conditional */
2405
2406     dumping = (defquery == 'd');
2407     if (dumping)
2408         defquery = 'y';
2409
2410     ngenocided = num_genocides();
2411     nextinct = num_extinct();
2412
2413     /* genocided or extinct species list */
2414     if (ngenocided != 0 || nextinct != 0) {
2415 #if 0 /*JP:T*/
2416         Sprintf(buf, "Do you want a list of %sspecies%s%s?",
2417                 (nextinct && !ngenocided) ? "extinct " : "",
2418                 (ngenocided) ? " genocided" : "",
2419                 (nextinct && ngenocided) ? " and extinct" : "");
2420 #else
2421         Sprintf(buf, "%s%s%s\82µ\82½\8eí\82Ì\88ê\97\97\82ð\8c©\82Ü\82·\82©\81H",
2422                 (nextinct && !ngenocided) ? "\90â\96Å" : "",
2423                 (ngenocided) ? "\8bs\8eE" : "",
2424                 (nextinct && ngenocided) ? "\82¨\82æ\82Ñ\90â\96Å" : "");
2425 #endif
2426         c = ask ? yn_function(buf, ynqchars, defquery) : defquery;
2427         if (c == 'q')
2428             done_stopprint++;
2429         if (c == 'y') {
2430             klwin = create_nhwindow(NHW_MENU);
2431 #if 0 /*JP:T*/
2432             Sprintf(buf, "%s%s species:",
2433                     (ngenocided) ? "Genocided" : "Extinct",
2434                     (nextinct && ngenocided) ? " or extinct" : "");
2435 #else
2436             Sprintf(buf, "%s%s\82µ\82½\8eí:",
2437                     (ngenocided) ? "\8bs\8eE" : "\90â\96Å",
2438                     (nextinct && ngenocided) ? "\82Ü\82½\82Í\90â\96Å" : "");
2439 #endif
2440             putstr(klwin, 0, buf);
2441             if (!dumping)
2442                 putstr(klwin, 0, "");
2443
2444             for (i = LOW_PM; i < NUMMONS; i++) {
2445                 /* uniques can't be genocided but can become extinct;
2446                    however, they're never reported as extinct, so skip them */
2447                 if (UniqCritterIndx(i))
2448                     continue;
2449                 if (mvitals[i].mvflags & G_GONE) {
2450                     Sprintf(buf, " %s", makeplural(mons[i].mname));
2451                     /*
2452                      * "Extinct" is unfortunate terminology.  A species
2453                      * is marked extinct when its birth limit is reached,
2454                      * but there might be members of the species still
2455                      * alive, contradicting the meaning of the word.
2456                      */
2457                     if ((mvitals[i].mvflags & G_GONE) == G_EXTINCT)
2458 /*JP
2459                         Strcat(buf, " (extinct)");
2460 */
2461                         Strcat(buf, "(\90â\96Å)");
2462                     putstr(klwin, 0, buf);
2463                 }
2464             }
2465             if (!dumping)
2466                 putstr(klwin, 0, "");
2467             if (ngenocided > 0) {
2468 /*JP
2469                 Sprintf(buf, "%d species genocided.", ngenocided);
2470 */
2471                 Sprintf(buf, "%d\8eí\97Þ\82Ì\8eí\82ð\8bs\8eE\82µ\82½\81D", ngenocided);
2472                 putstr(klwin, 0, buf);
2473             }
2474             if (nextinct > 0) {
2475 /*JP
2476                 Sprintf(buf, "%d species extinct.", nextinct);
2477 */
2478                 Sprintf(buf, "%d\8eí\97Þ\82Ì\8eí\82ð\90â\96Å\82³\82¹\82½\81D", nextinct);
2479                 putstr(klwin, 0, buf);
2480             }
2481
2482             display_nhwindow(klwin, TRUE);
2483             destroy_nhwindow(klwin);
2484         }
2485 #ifdef DUMPLOG
2486     } else if (dumping) {
2487 /*JP
2488         putstr(0, 0, "No species were genocided or became extinct.");
2489 */
2490         putstr(0, 0, "\8bs\8eE\82µ\82½\82è\90â\96Å\82³\82¹\82½\82è\82µ\82½\8eí\82Í\82¢\82È\82©\82Á\82½\81D");
2491 #endif
2492     }
2493 }
2494
2495 /* set a delayed killer, ensure non-delayed killer is cleared out */
2496 void
2497 delayed_killer(id, format, killername)
2498 int id;
2499 int format;
2500 const char *killername;
2501 {
2502     struct kinfo *k = find_delayed_killer(id);
2503
2504     if (!k) {
2505         /* no match, add a new delayed killer to the list */
2506         k = (struct kinfo *) alloc(sizeof (struct kinfo));
2507         (void) memset((genericptr_t) k, 0, sizeof (struct kinfo));
2508         k->id = id;
2509         k->next = killer.next;
2510         killer.next = k;
2511     }
2512
2513     k->format = format;
2514     Strcpy(k->name, killername ? killername : "");
2515     killer.name[0] = 0;
2516 }
2517
2518 struct kinfo *
2519 find_delayed_killer(id)
2520 int id;
2521 {
2522     struct kinfo *k;
2523
2524     for (k = killer.next; k != (struct kinfo *) 0; k = k->next) {
2525         if (k->id == id)
2526             break;
2527     }
2528     return k;
2529 }
2530
2531 void
2532 dealloc_killer(kptr)
2533 struct kinfo *kptr;
2534 {
2535     struct kinfo *prev = &killer, *k;
2536
2537     if (kptr == (struct kinfo *) 0)
2538         return;
2539     for (k = killer.next; k != (struct kinfo *) 0; k = k->next) {
2540         if (k == kptr)
2541             break;
2542         prev = k;
2543     }
2544
2545     if (k == (struct kinfo *) 0) {
2546         impossible("dealloc_killer (#%d) not on list", kptr->id);
2547     } else {
2548         prev->next = k->next;
2549         free((genericptr_t) k);
2550         debugpline1("freed delayed killer #%d", kptr->id);
2551     }
2552 }
2553
2554 void
2555 save_killers(fd, mode)
2556 int fd;
2557 int mode;
2558 {
2559     struct kinfo *kptr;
2560
2561     if (perform_bwrite(mode)) {
2562         for (kptr = &killer; kptr != (struct kinfo *) 0; kptr = kptr->next) {
2563             bwrite(fd, (genericptr_t) kptr, sizeof (struct kinfo));
2564         }
2565     }
2566     if (release_data(mode)) {
2567         while (killer.next) {
2568             kptr = killer.next->next;
2569             free((genericptr_t) killer.next);
2570             killer.next = kptr;
2571         }
2572     }
2573 }
2574
2575 void
2576 restore_killers(fd)
2577 int fd;
2578 {
2579     struct kinfo *kptr;
2580
2581     for (kptr = &killer; kptr != (struct kinfo *) 0; kptr = kptr->next) {
2582         mread(fd, (genericptr_t) kptr, sizeof (struct kinfo));
2583         if (kptr->next) {
2584             kptr->next = (struct kinfo *) alloc(sizeof (struct kinfo));
2585         }
2586     }
2587 }
2588
2589 static int
2590 wordcount(p)
2591 char *p;
2592 {
2593     int words = 0;
2594
2595     while (*p) {
2596         while (*p && isspace((uchar) *p))
2597             p++;
2598         if (*p)
2599             words++;
2600         while (*p && !isspace((uchar) *p))
2601             p++;
2602     }
2603     return words;
2604 }
2605
2606 static void
2607 bel_copy1(inp, out)
2608 char **inp, *out;
2609 {
2610     char *in = *inp;
2611
2612     out += strlen(out); /* eos() */
2613     while (*in && isspace((uchar) *in))
2614         in++;
2615     while (*in && !isspace((uchar) *in))
2616         *out++ = *in++;
2617     *out = '\0';
2618     *inp = in;
2619 }
2620
2621 /*JP: files.c\82Å1\83\96\8f\8a\8eg\82í\82ê\82Ä\82¢\82é\82ª\82±\82±\82Í\89p\8cê\82Ì\82Ü\82Ü\82É\82µ\82Ä\82¨\82­*/
2622 char *
2623 build_english_list(in)
2624 char *in;
2625 {
2626     char *out, *p = in;
2627     int len = (int) strlen(p), words = wordcount(p);
2628
2629     /* +3: " or " - " "; +(words - 1): (N-1)*(", " - " ") */
2630     if (words > 1)
2631         len += 3 + (words - 1);
2632     out = (char *) alloc(len + 1);
2633     *out = '\0'; /* bel_copy1() appends */
2634
2635     switch (words) {
2636     case 0:
2637         impossible("no words in list");
2638         break;
2639     case 1:
2640         /* "single" */
2641         bel_copy1(&p, out);
2642         break;
2643     default:
2644         if (words == 2) {
2645             /* "first or second" */
2646             bel_copy1(&p, out);
2647             Strcat(out, " ");
2648         } else {
2649             /* "first, second, or third */
2650             do {
2651                 bel_copy1(&p, out);
2652                 Strcat(out, ", ");
2653             } while (--words > 1);
2654         }
2655         Strcat(out, "or ");
2656         bel_copy1(&p, out);
2657         break;
2658     }
2659     return out;
2660 }
2661
2662 /*end.c*/