OSDN Git Service

fix #41899
[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     putstr(0, 0, "Latest messages:");
806     for (i = 0, j = (int) saved_pline_index; i < DUMPLOG_MSG_COUNT;
807          ++i, j = (j + 1) % DUMPLOG_MSG_COUNT) {
808         strp = &saved_plines[j];
809         if (*strp) {
810             copynchars(&buf[1], *strp, BUFSZ - 1 - 1);
811             putstr(0, 0, buf);
812 #ifdef FREE_ALL_MEMORY
813             free(*strp), *strp = 0;
814 #endif
815         }
816     }
817 }
818 #endif
819
820 /*ARGSUSED*/
821 STATIC_OVL void
822 dump_everything(how, when)
823 int how;
824 time_t when; /* date+time at end of game */
825 {
826 #ifdef DUMPLOG
827     char pbuf[BUFSZ], datetimebuf[24]; /* [24]: room for 64-bit bogus value */
828
829     dump_redirect(TRUE);
830     if (!iflags.in_dumplog)
831         return;
832
833     init_symbols(); /* revert to default symbol set */
834
835     /* one line version ID, which includes build date+time;
836        it's conceivable that the game started with a different
837        build date+time or even with an older nethack version,
838        but we only have access to the one it finished under */
839     putstr(0, 0, getversionstring(pbuf));
840     putstr(0, 0, "");
841
842     /* game start and end date+time to disambiguate version date+time */
843     Strcpy(datetimebuf, yyyymmddhhmmss(ubirthday));
844     Sprintf(pbuf, "Game began %4.4s-%2.2s-%2.2s %2.2s:%2.2s:%2.2s",
845             &datetimebuf[0], &datetimebuf[4], &datetimebuf[6],
846             &datetimebuf[8], &datetimebuf[10], &datetimebuf[12]);
847     Strcpy(datetimebuf, yyyymmddhhmmss(when));
848     Sprintf(eos(pbuf), ", ended %4.4s-%2.2s-%2.2s %2.2s:%2.2s:%2.2s.",
849             &datetimebuf[0], &datetimebuf[4], &datetimebuf[6],
850             &datetimebuf[8], &datetimebuf[10], &datetimebuf[12]);
851     putstr(0, 0, pbuf);
852     putstr(0, 0, "");
853
854     /* character name and basic role info */
855     Sprintf(pbuf, "%s, %s %s %s %s", plname,
856             aligns[1 - u.ualign.type].adj,
857             genders[flags.female].adj,
858             urace.adj,
859             (flags.female && urole.name.f) ? urole.name.f : urole.name.m);
860     putstr(0, 0, pbuf);
861     putstr(0, 0, "");
862
863     dump_map();
864     putstr(0, 0, do_statusline1());
865     putstr(0, 0, do_statusline2());
866     putstr(0, 0, "");
867
868     dump_plines();
869     putstr(0, 0, "");
870     putstr(0, 0, "Inventory:");
871     (void) display_inventory((char *) 0, TRUE);
872     container_contents(invent, TRUE, TRUE, FALSE);
873     enlightenment((BASICENLIGHTENMENT | MAGICENLIGHTENMENT),
874                   (how >= PANICKED) ? ENL_GAMEOVERALIVE : ENL_GAMEOVERDEAD);
875     putstr(0, 0, "");
876     list_vanquished('d', FALSE); /* 'd' => 'y' */
877     putstr(0, 0, "");
878     list_genocided('d', FALSE); /* 'd' => 'y' */
879     putstr(0, 0, "");
880     show_conduct((how >= PANICKED) ? 1 : 2);
881     putstr(0, 0, "");
882     show_overview((how >= PANICKED) ? 1 : 2, how);
883     putstr(0, 0, "");
884     dump_redirect(FALSE);
885 #else
886     nhUse(how);
887     nhUse(when);
888 #endif
889 }
890
891 STATIC_OVL void
892 disclose(how, taken)
893 int how;
894 boolean taken;
895 {
896     char c = '\0', defquery;
897     char qbuf[QBUFSZ];
898     boolean ask = FALSE;
899
900     if (invent && !done_stopprint) {
901         if (taken)
902 #if 0 /*JP:T*/
903             Sprintf(qbuf, "Do you want to see what you had when you %s?",
904                     (how == QUIT) ? "quit" : "died");
905 #else
906             Sprintf(qbuf,"%s\82Æ\82«\89½\82ð\8e\9d\82Á\82Ä\82¢\82½\82©\8c©\82Ü\82·\82©\81H",
907                     (how == QUIT) ? "\82â\82ß\82½" : "\8e\80\82ñ\82¾");
908 #endif
909         else
910 /*JP
911             Strcpy(qbuf, "Do you want your possessions identified?");
912 */
913             Strcpy(qbuf,"\8e\9d\82¿\95¨\82ð\8e¯\95Ê\82µ\82Ü\82·\82©\81H"); 
914
915         ask = should_query_disclose_option('i', &defquery);
916         c = ask ? yn_function(qbuf, ynqchars, defquery) : defquery;
917         if (c == 'y') {
918             /* caller has already ID'd everything */
919             (void) display_inventory((char *) 0, TRUE);
920             container_contents(invent, TRUE, TRUE, FALSE);
921         }
922         if (c == 'q')
923             done_stopprint++;
924     }
925
926     if (!done_stopprint) {
927         ask = should_query_disclose_option('a', &defquery);
928 #if 0 /*JP:T*/
929         c = ask ? yn_function("Do you want to see your attributes?", ynqchars,
930                               defquery)
931                 : defquery;
932 #else
933         c = ask ? yn_function("\91®\90«\82ð\8c©\82Ü\82·\82©\81H", ynqchars,
934                               defquery)
935                 : defquery;
936 #endif
937         if (c == 'y')
938             enlightenment((BASICENLIGHTENMENT | MAGICENLIGHTENMENT),
939                           (how >= PANICKED) ? ENL_GAMEOVERALIVE
940                                             : ENL_GAMEOVERDEAD);
941         if (c == 'q')
942             done_stopprint++;
943     }
944
945     if (!done_stopprint) {
946         ask = should_query_disclose_option('v', &defquery);
947         list_vanquished(defquery, ask);
948     }
949
950     if (!done_stopprint) {
951         ask = should_query_disclose_option('g', &defquery);
952         list_genocided(defquery, ask);
953     }
954
955     if (!done_stopprint) {
956         ask = should_query_disclose_option('c', &defquery);
957 #if 0 /*JP:T*/
958         c = ask ? yn_function("Do you want to see your conduct?", ynqchars,
959                               defquery)
960                 : defquery;
961 #else
962         c = ask ? yn_function("\82Ç\82¤\82¢\82¤\8ds\93®\82ð\82Æ\82Á\82½\82©\8c©\82Ü\82·\82©\81H", ynqchars,
963                               defquery)
964                 : defquery;
965 #endif
966         if (c == 'y')
967             show_conduct((how >= PANICKED) ? 1 : 2);
968         if (c == 'q')
969             done_stopprint++;
970     }
971
972     if (!done_stopprint) {
973         ask = should_query_disclose_option('o', &defquery);
974 #if 0 /*JP:T*/
975         c = ask ? yn_function("Do you want to see the dungeon overview?",
976                               ynqchars, defquery)
977                 : defquery;
978 #else
979         c = ask ? yn_function("\96À\8b{\82Ì\8aT\97v\82ð\8c©\82Ü\82·\82©\81H",
980                               ynqchars, defquery)
981                 : defquery;
982 #endif
983         if (c == 'y')
984             show_overview((how >= PANICKED) ? 1 : 2, how);
985         if (c == 'q')
986             done_stopprint++;
987     }
988 }
989
990 /* try to get the player back in a viable state after being killed */
991 STATIC_OVL void
992 savelife(how)
993 int how;
994 {
995     int uhpmin = max(2 * u.ulevel, 10);
996
997     if (u.uhpmax < uhpmin)
998         u.uhpmax = uhpmin;
999     u.uhp = u.uhpmax;
1000     if (Upolyd) /* Unchanging, or death which bypasses losing hit points */
1001         u.mh = u.mhmax;
1002     if (u.uhunger < 500 || how == CHOKING) {
1003         init_uhunger();
1004     }
1005     /* cure impending doom of sickness hero won't have time to fix */
1006     if ((Sick & TIMEOUT) == 1L) {
1007         make_sick(0L, (char *) 0, FALSE, SICK_ALL);
1008     }
1009 /*JP
1010     nomovemsg = "You survived that attempt on your life.";
1011 */
1012     nomovemsg = "\82 \82È\82½\82Í\90\82«\82È\82ª\82ç\82¦\82½\81D";
1013     context.move = 0;
1014     if (multi > 0)
1015         multi = 0;
1016     else
1017         multi = -1;
1018     if (u.utrap && u.utraptype == TT_LAVA)
1019         reset_utrap(FALSE);
1020     context.botl = 1;
1021     u.ugrave_arise = NON_PM;
1022     HUnchanging = 0L;
1023     curs_on_u();
1024     if (!context.mon_moving)
1025         endmultishot(FALSE);
1026     if (u.uswallow) {
1027         /* might drop hero onto a trap that kills her all over again */
1028         expels(u.ustuck, u.ustuck->data, TRUE);
1029     } else if (u.ustuck) {
1030         if (Upolyd && sticks(youmonst.data))
1031             You("release %s.", mon_nam(u.ustuck));
1032         else
1033             pline("%s releases you.", Monnam(u.ustuck));
1034         unstuck(u.ustuck);
1035     }
1036 }
1037
1038 /*
1039  * Get valuables from the given list.  Revised code: the list always remains
1040  * intact.
1041  */
1042 STATIC_OVL void
1043 get_valuables(list)
1044 struct obj *list; /* inventory or container contents */
1045 {
1046     register struct obj *obj;
1047     register int i;
1048
1049     /* find amulets and gems, ignoring all artifacts */
1050     for (obj = list; obj; obj = obj->nobj)
1051         if (Has_contents(obj)) {
1052             get_valuables(obj->cobj);
1053         } else if (obj->oartifact) {
1054             continue;
1055         } else if (obj->oclass == AMULET_CLASS) {
1056             i = obj->otyp - FIRST_AMULET;
1057             if (!amulets[i].count) {
1058                 amulets[i].count = obj->quan;
1059                 amulets[i].typ = obj->otyp;
1060             } else
1061                 amulets[i].count += obj->quan; /* always adds one */
1062         } else if (obj->oclass == GEM_CLASS && obj->otyp < LUCKSTONE) {
1063             i = min(obj->otyp, LAST_GEM + 1) - FIRST_GEM;
1064             if (!gems[i].count) {
1065                 gems[i].count = obj->quan;
1066                 gems[i].typ = obj->otyp;
1067             } else
1068                 gems[i].count += obj->quan;
1069         }
1070     return;
1071 }
1072
1073 /*
1074  *  Sort collected valuables, most frequent to least.  We could just
1075  *  as easily use qsort, but we don't care about efficiency here.
1076  */
1077 STATIC_OVL void
1078 sort_valuables(list, size)
1079 struct valuable_data list[];
1080 int size; /* max value is less than 20 */
1081 {
1082     register int i, j;
1083     struct valuable_data ltmp;
1084
1085     /* move greater quantities to the front of the list */
1086     for (i = 1; i < size; i++) {
1087         if (list[i].count == 0)
1088             continue;   /* empty slot */
1089         ltmp = list[i]; /* structure copy */
1090         for (j = i; j > 0; --j)
1091             if (list[j - 1].count >= ltmp.count)
1092                 break;
1093             else {
1094                 list[j] = list[j - 1];
1095             }
1096         list[j] = ltmp;
1097     }
1098     return;
1099 }
1100
1101 #if 0
1102 /*
1103  * odds_and_ends() was used for 3.6.0 and 3.6.1.
1104  * Schroedinger's Cat is handled differently as of 3.6.2.
1105  */
1106 STATIC_DCL boolean FDECL(odds_and_ends, (struct obj *, int));
1107
1108 #define CAT_CHECK 2
1109
1110 STATIC_OVL boolean
1111 odds_and_ends(list, what)
1112 struct obj *list;
1113 int what;
1114 {
1115     struct obj *otmp;
1116
1117     for (otmp = list; otmp; otmp = otmp->nobj) {
1118         switch (what) {
1119         case CAT_CHECK: /* Schroedinger's Cat */
1120             /* Ascending is deterministic */
1121             if (SchroedingersBox(otmp))
1122                 return rn2(2);
1123             break;
1124         }
1125         if (Has_contents(otmp))
1126             return odds_and_ends(otmp->cobj, what);
1127     }
1128     return FALSE;
1129 }
1130 #endif
1131
1132 /* deal with some objects which may be in an abnormal state at end of game */
1133 STATIC_OVL void
1134 done_object_cleanup()
1135 {
1136     int ox, oy;
1137
1138     /* might have been killed while using a disposable item, so make sure
1139        it's gone prior to inventory disclosure and creation of bones */
1140     inven_inuse(TRUE);
1141     /*
1142      * Hero can die when throwing an object (by hitting an adjacent
1143      * gas spore, for instance, or being hit by mis-returning Mjollnir),
1144      * or while in transit (from falling down stairs).  If that happens,
1145      * some object(s) might be in limbo rather than on the map or in
1146      * any inventory.  Saving bones with an active light source in limbo
1147      * would trigger an 'object not local' panic.
1148      *
1149      * We used to use dealloc_obj() on thrownobj and kickedobj but
1150      * that keeps them out of bones and could leave uball in a confused
1151      * state (gone but still attached).  Place them on the map but
1152      * bypass flooreffects().  That could lead to minor anomalies in
1153      * bones, like undamaged paper at water or lava locations or piles
1154      * not being knocked down holes, but it seems better to get this
1155      * game over with than risk being tangled up in more and more details.
1156      */
1157     ox = u.ux + u.dx, oy = u.uy + u.dy;
1158     if (!isok(ox, oy) || !accessible(ox, oy))
1159         ox = u.ux, oy = u.uy;
1160     /* put thrown or kicked object on map (for bones); location might
1161        be incorrect (perhaps killed by divine lightning when throwing at
1162        a temple priest?) but this should be better than just vanishing
1163        (fragile stuff should be taken care of before getting here) */
1164     if (thrownobj && thrownobj->where == OBJ_FREE) {
1165         place_object(thrownobj, ox, oy);
1166         stackobj(thrownobj), thrownobj = 0;
1167     }
1168     if (kickedobj && kickedobj->where == OBJ_FREE) {
1169         place_object(kickedobj, ox, oy);
1170         stackobj(kickedobj), kickedobj = 0;
1171     }
1172     /* if Punished hero dies during level change or dies or quits while
1173        swallowed, uball and uchain will be in limbo; put them on floor
1174        so bones will have them and object list cleanup finds them */
1175     if (uchain && uchain->where == OBJ_FREE) {
1176         /* placebc(); */
1177         lift_covet_and_placebc(override_restriction);
1178     }
1179     /* persistent inventory window now obsolete since disclosure uses
1180        a normal popup one; avoids "Bad fruit #n" when saving bones */
1181     if (iflags.perm_invent) {
1182         iflags.perm_invent = FALSE;
1183         update_inventory(); /* make interface notice the change */
1184     }
1185     return;
1186 }
1187
1188 /* called twice; first to calculate total, then to list relevant items */
1189 STATIC_OVL void
1190 artifact_score(list, counting, endwin)
1191 struct obj *list;
1192 boolean counting; /* true => add up points; false => display them */
1193 winid endwin;
1194 {
1195     char pbuf[BUFSZ];
1196     struct obj *otmp;
1197     long value, points;
1198     short dummy; /* object type returned by artifact_name() */
1199
1200     for (otmp = list; otmp; otmp = otmp->nobj) {
1201         if (otmp->oartifact || otmp->otyp == BELL_OF_OPENING
1202             || otmp->otyp == SPE_BOOK_OF_THE_DEAD
1203             || otmp->otyp == CANDELABRUM_OF_INVOCATION) {
1204             value = arti_cost(otmp); /* zorkmid value */
1205             points = value * 5 / 2;  /* score value */
1206             if (counting) {
1207                 nowrap_add(u.urexp, points);
1208             } else {
1209                 discover_object(otmp->otyp, TRUE, FALSE);
1210                 otmp->known = otmp->dknown = otmp->bknown = otmp->rknown = 1;
1211                 /* assumes artifacts don't have quan > 1 */
1212 #if 0 /*JP:T*/
1213                 Sprintf(pbuf, "%s%s (worth %ld %s and %ld points)",
1214                         the_unique_obj(otmp) ? "The " : "",
1215                         otmp->oartifact ? artifact_name(xname(otmp), &dummy)
1216                                         : OBJ_NAME(objects[otmp->otyp]),
1217                         value, currency(value), points);
1218 #else
1219                 Sprintf(pbuf, "%s(%ld%s\81C%ld\83|\83C\83\93\83g\82Ì\89¿\92l)\81C",
1220                         otmp->oartifact ? artifact_name(xname(otmp), &dummy)
1221                                         : OBJ_NAME(objects[otmp->otyp]),
1222                         value, currency(value), points);
1223 #endif
1224                 putstr(endwin, 0, pbuf);
1225             }
1226         }
1227         if (Has_contents(otmp))
1228             artifact_score(otmp->cobj, counting, endwin);
1229     }
1230 }
1231
1232 /* Be careful not to call panic from here! */
1233 void
1234 done(how)
1235 int how;
1236 {
1237     boolean survive = FALSE;
1238
1239     if (how == TRICKED) {
1240         if (killer.name[0]) {
1241             paniclog("trickery", killer.name);
1242             killer.name[0] = '\0';
1243         }
1244         if (wizard) {
1245 /*JP
1246             You("are a very tricky wizard, it seems.");
1247 */
1248             You("\82Æ\82Ä\82à\88µ\82¢\82É\82­\82¢wizard\82Ì\82æ\82¤\82¾\81D");
1249             killer.format = KILLED_BY_AN; /* reset to 0 */
1250             return;
1251         }
1252     }
1253     if (program_state.panicking
1254 #ifdef HANGUPHANDLING
1255         || program_state.done_hup
1256 #endif
1257         ) {
1258         /* skip status update if panicking or disconnected */
1259         context.botl = context.botlx = iflags.time_botl = FALSE;
1260     } else {
1261         /* otherwise force full status update */
1262         context.botlx = TRUE;
1263         bot();
1264     }
1265
1266     if (iflags.debug_fuzzer) {
1267         if (!(program_state.panicking || how == PANICKED)) {
1268             savelife(how);
1269             /* periodically restore characteristics and lost exp levels
1270                or cure lycanthropy */
1271             if (!rn2(10)) {
1272                 struct obj *potion = mksobj((u.ulycn > LOW_PM && !rn2(3))
1273                                             ? POT_WATER : POT_RESTORE_ABILITY,
1274                                             TRUE, FALSE);
1275
1276                 bless(potion);
1277                 (void) peffects(potion); /* always -1 for restore ability */
1278                 /* not useup(); we haven't put this potion into inventory */
1279                 obfree(potion, (struct obj *) 0);
1280             }
1281             killer.name[0] = '\0';
1282             killer.format = 0;
1283             return;
1284         }
1285     } else
1286     if (how == ASCENDED || (!killer.name[0] && how == GENOCIDED))
1287         killer.format = NO_KILLER_PREFIX;
1288     /* Avoid killed by "a" burning or "a" starvation */
1289     if (!killer.name[0] && (how == STARVING || how == BURNING))
1290         killer.format = KILLED_BY;
1291     if (!killer.name[0] || how >= PANICKED)
1292 /*JP
1293         Strcpy(killer.name, deaths[how]);
1294 */
1295         Strcpy(killer.name, ends[how]);
1296
1297     if (how < PANICKED) {
1298         u.umortality++;
1299         /* in case caller hasn't already done this */
1300         if (u.uhp != 0 || (Upolyd && u.mh != 0)) {
1301             /* force HP to zero in case it is still positive (some
1302                deaths aren't triggered by loss of hit points), or
1303                negative (-1 is used as a flag in some circumstances
1304                which don't apply when actually dying due to HP loss) */
1305             u.uhp = u.mh = 0;
1306             context.botl = 1;
1307         }
1308     }
1309     if (Lifesaved && (how <= GENOCIDED)) {
1310 /*JP
1311         pline("But wait...");
1312 */
1313         pline("\82¿\82å\82Á\82Æ\82Ü\82Á\82½\81D\81D\81D");
1314         makeknown(AMULET_OF_LIFE_SAVING);
1315 /*JP
1316         Your("medallion %s!", !Blind ? "begins to glow" : "feels warm");
1317 */
1318         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½");
1319         if (how == CHOKING)
1320 /*JP
1321             You("vomit ...");
1322 */
1323             You("\93f\82¢\82½\81D\81D\81D");
1324 /*JP
1325         You_feel("much better!");
1326 */
1327         You("\8bC\95ª\82ª\82æ\82­\82È\82Á\82½\81I");
1328 /*JP
1329         pline_The("medallion crumbles to dust!");
1330 */
1331         pline("\96\82\8f\9c\82¯\82Í\82±\82È\82²\82È\82É\82­\82¾\82¯\82½\81I");
1332         if (uamul)
1333             useup(uamul);
1334
1335         (void) adjattrib(A_CON, -1, TRUE);
1336         savelife(how);
1337         if (how == GENOCIDED) {
1338 /*JP
1339             pline("Unfortunately you are still genocided...");
1340 */
1341             pline("\8ec\94O\82È\82ª\82ç\81C\82 \82È\82½\82Í\8bs\8eE\82³\82ê\82½\82Ü\82Ü\82¾\81D\81D\81D");
1342         } else {
1343             survive = TRUE;
1344         }
1345     }
1346     /* explore and wizard modes offer player the option to keep playing */
1347 #if 0 /*JP:T*/
1348     if (!survive && (wizard || discover) && how <= GENOCIDED
1349         && !paranoid_query(ParanoidDie, "Die?")) {
1350 #else
1351     if (!survive && (wizard || discover) && how <= GENOCIDED
1352         && !paranoid_query(ParanoidDie, "\8e\80\82ñ\82Å\82Ý\82é\81H")) {
1353 #endif
1354 /*JP
1355         pline("OK, so you don't %s.", (how == CHOKING) ? "choke" : "die");
1356 */
1357         You("\8e\80\82È\82È\82©\82Á\82½\81D");
1358         iflags.last_msg = PLNMSG_OK_DONT_DIE;
1359         savelife(how);
1360         survive = TRUE;
1361     }
1362
1363     if (survive) {
1364         killer.name[0] = '\0';
1365         killer.format = KILLED_BY_AN; /* reset to 0 */
1366         return;
1367     }
1368     really_done(how);
1369     /*NOTREACHED*/
1370 }
1371
1372 /* separated from done() in order to specify the __noreturn__ attribute */
1373 STATIC_OVL void
1374 really_done(how)
1375 int how;
1376 {
1377     boolean taken;
1378     char pbuf[BUFSZ];
1379     winid endwin = WIN_ERR;
1380     boolean bones_ok, have_windows = iflags.window_inited;
1381     struct obj *corpse = (struct obj *) 0;
1382     time_t endtime;
1383     long umoney;
1384     long tmp;
1385
1386     /*
1387      *  The game is now over...
1388      */
1389     program_state.gameover = 1;
1390     /* in case of a subsequent panic(), there's no point trying to save */
1391     program_state.something_worth_saving = 0;
1392 #ifdef HANGUPHANDLING
1393     if (program_state.done_hup)
1394         done_stopprint++;
1395 #endif
1396     /* render vision subsystem inoperative */
1397     iflags.vision_inited = 0;
1398
1399     /* maybe use up active invent item(s), place thrown/kicked missile,
1400        deal with ball and chain possibly being temporarily off the map */
1401     if (!program_state.panicking)
1402         done_object_cleanup();
1403     /* in case we're panicking; normally cleared by done_object_cleanup() */
1404     iflags.perm_invent = FALSE;
1405
1406     /* remember time of death here instead of having bones, rip, and
1407        topten figure it out separately and possibly getting different
1408        time or even day if player is slow responding to --More-- */
1409     urealtime.finish_time = endtime = getnow();
1410     urealtime.realtime += (long) (endtime - urealtime.start_timing);
1411     /* collect these for end of game disclosure (not used during play) */
1412     iflags.at_night = night();
1413     iflags.at_midnight = midnight();
1414
1415     dump_open_log(endtime);
1416     /* Sometimes you die on the first move.  Life's not fair.
1417      * On those rare occasions you get hosed immediately, go out
1418      * smiling... :-)  -3.
1419      */
1420     if (moves <= 1 && how < PANICKED && !done_stopprint)
1421 /*JP
1422         pline("Do not pass Go.  Do not collect 200 %s.", currency(200L));
1423 */
1424         pline("\92\8d\88Ó\88ê\95b\81C\89ö\89ä\88ê\90\81C\8e\80\96S\88ê\95à\81D");
1425
1426     if (have_windows)
1427         wait_synch(); /* flush screen output */
1428 #ifndef NO_SIGNAL
1429     (void) signal(SIGINT, (SIG_RET_TYPE) done_intr);
1430 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
1431     (void) signal(SIGQUIT, (SIG_RET_TYPE) done_intr);
1432     sethanguphandler(done_hangup);
1433 #endif
1434 #endif /* NO_SIGNAL */
1435
1436     bones_ok = (how < GENOCIDED) && can_make_bones();
1437
1438     if (bones_ok && launch_in_progress())
1439         force_launch_placement();
1440
1441     /* maintain ugrave_arise even for !bones_ok */
1442     if (how == PANICKED)
1443         u.ugrave_arise = (NON_PM - 3); /* no corpse, no grave */
1444     else if (how == BURNING || how == DISSOLVED) /* corpse burns up too */
1445         u.ugrave_arise = (NON_PM - 2); /* leave no corpse */
1446     else if (how == STONING)
1447         u.ugrave_arise = (NON_PM - 1); /* statue instead of corpse */
1448     else if (how == TURNED_SLIME
1449              /* it's possible to turn into slime even though green slimes
1450                 have been genocided:  genocide could occur after hero is
1451                 already infected or hero could eat a glob of one created
1452                 before genocide; don't try to arise as one if they're gone */
1453              && !(mvitals[PM_GREEN_SLIME].mvflags & G_GENOD))
1454         u.ugrave_arise = PM_GREEN_SLIME;
1455
1456     if (how == QUIT) {
1457         killer.format = NO_KILLER_PREFIX;
1458         if (u.uhp < 1) {
1459             how = DIED;
1460             u.umortality++; /* skipped above when how==QUIT */
1461 /*JP
1462             Strcpy(killer.name, "quit while already on Charon's boat");
1463 */
1464             Strcpy(killer.name, "\8eO\93r\82Ì\90ì\82Ì\93n\82µ\91D\82É\8fæ\82Á\82Ä\82¢\82é\8aÔ\82É\94²\82¯\82½");
1465         }
1466     }
1467     if (how == ESCAPED || how == PANICKED)
1468         killer.format = NO_KILLER_PREFIX;
1469
1470 #if 0 /*JP*//*\93ú\96{\8cê\82Å\82Í\8eg\82í\82È\82¢*/
1471     fixup_death(how); /* actually, fixup multi_reason */
1472 #endif
1473
1474     if (how != PANICKED) {
1475         boolean silently = done_stopprint ? TRUE : FALSE;
1476
1477         /* these affect score and/or bones, but avoid them during panic */
1478         taken = paybill((how == ESCAPED) ? -1 : (how != QUIT), silently);
1479         paygd(silently);
1480         clearpriests();
1481     } else
1482         taken = FALSE; /* lint; assert( !bones_ok ); */
1483
1484     clearlocks();
1485
1486     if (have_windows)
1487         display_nhwindow(WIN_MESSAGE, FALSE);
1488
1489     if (how != PANICKED) {
1490         struct obj *obj;
1491
1492         /*
1493          * This is needed for both inventory disclosure and dumplog.
1494          * Both are optional, so do it once here instead of duplicating
1495          * it in both of those places.
1496          */
1497         for (obj = invent; obj; obj = obj->nobj) {
1498             discover_object(obj->otyp, TRUE, FALSE);
1499             obj->known = obj->bknown = obj->dknown = obj->rknown = 1;
1500             if (Is_container(obj) || obj->otyp == STATUE)
1501                 obj->cknown = obj->lknown = 1;
1502             /* we resolve Schroedinger's cat now in case of both
1503                disclosure and dumplog, where the 50:50 chance for
1504                live cat has to be the same both times */
1505             if (SchroedingersBox(obj)) {
1506                 if (!Schroedingers_cat) {
1507                     /* tell observe_quantum_cat() not to create a cat; if it
1508                        chooses live cat in this situation, it will leave the
1509                        SchroedingersBox flag set (for container_contents()) */
1510                     observe_quantum_cat(obj, FALSE, FALSE);
1511                     if (SchroedingersBox(obj))
1512                         Schroedingers_cat = TRUE;
1513                 } else
1514                     obj->spe = 0; /* ordinary box with cat corpse in it */
1515             }
1516         }
1517
1518         if (strcmp(flags.end_disclose, "none"))
1519             disclose(how, taken);
1520
1521         dump_everything(how, endtime);
1522     }
1523
1524     /* if pets will contribute to score, populate mydogs list now
1525        (bones creation isn't a factor, but pline() messaging is; used to
1526        be done even sooner, but we need it to come after dump_everything()
1527        so that any accompanying pets are still on the map during dump) */
1528     if (how == ESCAPED || how == ASCENDED)
1529         keepdogs(TRUE);
1530
1531     /* finish_paybill should be called after disclosure but before bones */
1532     if (bones_ok && taken)
1533         finish_paybill();
1534
1535     /* grave creation should be after disclosure so it doesn't have
1536        this grave in the current level's features for #overview */
1537     if (bones_ok && u.ugrave_arise == NON_PM
1538         && !(mvitals[u.umonnum].mvflags & G_NOCORPSE)) {
1539         int mnum = u.umonnum;
1540
1541         if (!Upolyd) {
1542             /* Base corpse on race when not poly'd since original u.umonnum
1543                is based on role, and all role monsters are human. */
1544             mnum = (flags.female && urace.femalenum != NON_PM)
1545                        ? urace.femalenum
1546                        : urace.malenum;
1547         }
1548         corpse = mk_named_object(CORPSE, &mons[mnum], u.ux, u.uy, plname);
1549 /*JP
1550         Sprintf(pbuf, "%s, ", plname);
1551 */
1552         Sprintf(pbuf, "%s\82Ì\95æ\81C", plname);
1553         formatkiller(eos(pbuf), sizeof pbuf - strlen(pbuf), how, TRUE);
1554         make_grave(u.ux, u.uy, pbuf);
1555     }
1556     pbuf[0] = '\0'; /* clear grave text; also lint suppression */
1557
1558     /* calculate score, before creating bones [container gold] */
1559     {
1560         int deepest = deepest_lev_reached(FALSE);
1561
1562         umoney = money_cnt(invent);
1563         tmp = u.umoney0;
1564         umoney += hidden_gold(); /* accumulate gold from containers */
1565         tmp = umoney - tmp;      /* net gain */
1566
1567         if (tmp < 0L)
1568             tmp = 0L;
1569         if (how < PANICKED)
1570             tmp -= tmp / 10L;
1571         tmp += 50L * (long) (deepest - 1);
1572         if (deepest > 20)
1573             tmp += 1000L * (long) ((deepest > 30) ? 10 : deepest - 20);
1574         nowrap_add(u.urexp, tmp);
1575
1576         /* ascension gives a score bonus iff offering to original deity */
1577         if (how == ASCENDED && u.ualign.type == u.ualignbase[A_ORIGINAL]) {
1578             /* retaining original alignment: score *= 2;
1579                converting, then using helm-of-OA to switch back: *= 1.5 */
1580             tmp = (u.ualignbase[A_CURRENT] == u.ualignbase[A_ORIGINAL])
1581                       ? u.urexp
1582                       : (u.urexp / 2L);
1583             nowrap_add(u.urexp, tmp);
1584         }
1585     }
1586
1587     if (u.ugrave_arise >= LOW_PM && !done_stopprint) {
1588         /* give this feedback even if bones aren't going to be created,
1589            so that its presence or absence doesn't tip off the player to
1590            new bones or their lack; it might be a lie if makemon fails */
1591 #if 0 /*JP:T*/
1592         Your("%s as %s...",
1593              (u.ugrave_arise != PM_GREEN_SLIME)
1594                  ? "body rises from the dead"
1595                  : "revenant persists",
1596              an(mons[u.ugrave_arise].mname));
1597 #else
1598         Your("%s as %s\82É\82È\82Á\82½\81D\81D\81D",
1599              (u.ugrave_arise != PM_GREEN_SLIME)
1600                  ? "\91Ì\82Í\8e\80\91Ì\82©\82ç\91h\82Á\82Ä"
1601                  : "\96S\97ì\82Í",
1602              mons[u.ugrave_arise].mname);
1603 #endif
1604         display_nhwindow(WIN_MESSAGE, FALSE);
1605     }
1606
1607     if (bones_ok) {
1608 /*JP
1609         if (!wizard || paranoid_query(ParanoidBones, "Save bones?"))
1610 */
1611         if (!wizard || paranoid_query(ParanoidBones, "\8d\9c\82ð\82¤\82ß\82é\81H"))
1612             savebones(how, endtime, corpse);
1613         /* corpse may be invalid pointer now so
1614             ensure that it isn't used again */
1615         corpse = (struct obj *) 0;
1616     }
1617
1618     /* update gold for the rip output, which can't use hidden_gold()
1619        (containers will be gone by then if bones just got saved...) */
1620     done_money = umoney;
1621
1622     /* clean up unneeded windows */
1623     if (have_windows) {
1624         wait_synch();
1625         free_pickinv_cache(); /* extra persistent window if perm_invent */
1626         if (WIN_INVEN != WIN_ERR) {
1627             destroy_nhwindow(WIN_INVEN),  WIN_INVEN = WIN_ERR;
1628             /* precaution in case any late update_inventory() calls occur */
1629             iflags.perm_invent = 0;
1630         }
1631         display_nhwindow(WIN_MESSAGE, TRUE);
1632         destroy_nhwindow(WIN_MAP),  WIN_MAP = WIN_ERR;
1633         if (WIN_STATUS != WIN_ERR)
1634             destroy_nhwindow(WIN_STATUS),  WIN_STATUS = WIN_ERR;
1635         destroy_nhwindow(WIN_MESSAGE),  WIN_MESSAGE = WIN_ERR;
1636
1637         if (!done_stopprint || flags.tombstone)
1638             endwin = create_nhwindow(NHW_TEXT);
1639
1640         if (how < GENOCIDED && flags.tombstone && endwin != WIN_ERR)
1641             outrip(endwin, how, endtime);
1642     } else
1643         done_stopprint = 1; /* just avoid any more output */
1644
1645 #ifdef DUMPLOG
1646     /* 'how' reasons beyond genocide shouldn't show tombstone;
1647        for normal end of game, genocide doesn't either */
1648     if (how <= GENOCIDED) {
1649         dump_redirect(TRUE);
1650         if (iflags.in_dumplog)
1651             genl_outrip(0, how, endtime);
1652         dump_redirect(FALSE);
1653     }
1654 #endif
1655     if (u.uhave.amulet) {
1656 /*JP
1657         Strcat(killer.name, " (with the Amulet)");
1658 */
1659         Strcat(killer.name, "\96\82\8f\9c\82¯\82ð\8eè\82É");
1660     } else if (how == ESCAPED) {
1661         if (Is_astralevel(&u.uz)) /* offered Amulet to wrong deity */
1662 /*JP
1663             Strcat(killer.name, " (in celestial disgrace)");
1664 */
1665             Strcat(killer.name, "\93V\8fã\82Å\92p\90J\82ð\8eó\82¯\92E\8fo\82µ\82½");
1666         else if (carrying(FAKE_AMULET_OF_YENDOR))
1667 /*JP
1668             Strcat(killer.name, " (with a fake Amulet)");
1669 */
1670             Strcat(killer.name, "\8bU\95¨\82Ì\96\82\8f\9c\82¯\82ð\92Í\82Ü\82³\82ê\92E\8fo\82µ\82½");
1671         /* don't bother counting to see whether it should be plural */
1672     }
1673
1674 #if 0 /*JP:T*/
1675     Sprintf(pbuf, "%s %s the %s...", Goodbye(), plname,
1676             (how != ASCENDED)
1677                 ? (const char *) ((flags.female && urole.name.f)
1678                     ? urole.name.f
1679                     : urole.name.m)
1680                 : (const char *) (flags.female ? "Demigoddess" : "Demigod"));
1681 #else
1682     Sprintf(pbuf, "%s%s\82Ì%s\81D\81D\81D", Goodbye(),
1683             (how != ASCENDED)
1684                 ? (const char *) ((flags.female && urole.name.f)
1685                     ? urole.name.f
1686                     : urole.name.m)
1687                 : (const char *) (flags.female ? "\8f\97\90_" : "\90_"),
1688                 plname);
1689 #endif
1690     dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
1691     dump_forward_putstr(endwin, 0, "", done_stopprint);
1692
1693     if (how == ESCAPED || how == ASCENDED) {
1694         struct monst *mtmp;
1695         struct obj *otmp;
1696         register struct val_list *val;
1697         register int i;
1698
1699         for (val = valuables; val->list; val++)
1700             for (i = 0; i < val->size; i++) {
1701                 val->list[i].count = 0L;
1702             }
1703         get_valuables(invent);
1704
1705         /* add points for collected valuables */
1706         for (val = valuables; val->list; val++)
1707             for (i = 0; i < val->size; i++)
1708                 if (val->list[i].count != 0L) {
1709                     tmp = val->list[i].count
1710                           * (long) objects[val->list[i].typ].oc_cost;
1711                     nowrap_add(u.urexp, tmp);
1712                 }
1713
1714         /* count the points for artifacts */
1715         artifact_score(invent, TRUE, endwin);
1716
1717         viz_array[0][0] |= IN_SIGHT; /* need visibility for naming */
1718         mtmp = mydogs;
1719 /*JP
1720         Strcpy(pbuf, "You");
1721 */
1722         Strcpy(pbuf, "\82 \82È\82½");
1723         if (mtmp || Schroedingers_cat) {
1724             while (mtmp) {
1725 /*JP
1726                 Sprintf(eos(pbuf), " and %s", mon_nam(mtmp));
1727 */
1728                 Sprintf(eos(pbuf), "\82Æ%s", mon_nam(mtmp));
1729                 if (mtmp->mtame)
1730                     nowrap_add(u.urexp, mtmp->mhp);
1731                 mtmp = mtmp->nmon;
1732             }
1733             /* [it might be more robust to create a housecat and add it to
1734                mydogs; it doesn't have to be placed on the map for that] */
1735             if (Schroedingers_cat) {
1736                 int mhp, m_lev = adj_lev(&mons[PM_HOUSECAT]);
1737
1738                 mhp = d(m_lev, 8);
1739                 nowrap_add(u.urexp, mhp);
1740 /*JP
1741                 Strcat(eos(pbuf), " and Schroedinger's cat");
1742 */
1743                 Strcat(eos(pbuf), "\82Æ\83V\83\85\83\8c\83f\83B\83\93\83K\81[\82Ì\94L");
1744             }
1745 #if 1 /*JP*/
1746             if (!done_stopprint)
1747                 Strcat(pbuf, "\82Í");
1748 #endif
1749             dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
1750             pbuf[0] = '\0';
1751         } else {
1752 /*JP
1753             Strcat(pbuf, " ");
1754 */
1755             Strcat(pbuf, "\82Í");
1756         }
1757 #if 0 /*JP:T*/
1758         Sprintf(eos(pbuf), "%s with %ld point%s,",
1759                 how == ASCENDED ? "went to your reward"
1760                                  : "escaped from the dungeon",
1761                 u.urexp, plur(u.urexp));
1762 #else
1763         Sprintf(eos(pbuf), "%ld\83|\83C\83\93\83g\83}\81[\83N\82µ%s\81D",
1764                 u.urexp,
1765                 how==ASCENDED ? "\8f¸\93V\82µ\82½" : "\96À\8b{\82©\82ç\92E\8fo\82µ\82½");
1766 #endif
1767         dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
1768
1769         if (!done_stopprint)
1770             artifact_score(invent, FALSE, endwin); /* list artifacts */
1771 #ifdef DUMPLOG
1772         dump_redirect(TRUE);
1773         if (iflags.in_dumplog)
1774             artifact_score(invent, FALSE, 0);
1775         dump_redirect(FALSE);
1776 #endif
1777
1778         /* list valuables here */
1779         for (val = valuables; val->list; val++) {
1780             sort_valuables(val->list, val->size);
1781             for (i = 0; i < val->size && !done_stopprint; i++) {
1782                 int typ = val->list[i].typ;
1783                 long count = val->list[i].count;
1784
1785                 if (count == 0L)
1786                     continue;
1787                 if (objects[typ].oc_class != GEM_CLASS || typ <= LAST_GEM) {
1788                     otmp = mksobj(typ, FALSE, FALSE);
1789                     discover_object(otmp->otyp, TRUE, FALSE);
1790                     otmp->known = 1;  /* for fake amulets */
1791                     otmp->dknown = 1; /* seen it (blindness fix) */
1792                     if (has_oname(otmp))
1793                         free_oname(otmp);
1794                     otmp->quan = count;
1795 #if 0 /*JP:T*/
1796                     Sprintf(pbuf, "%8ld %s (worth %ld %s),", count,
1797                             xname(otmp), count * (long) objects[typ].oc_cost,
1798                             currency(2L));
1799 #else
1800                     Sprintf(pbuf, "%ld\8cÂ\82Ì%s(%ld%s\82Ì\89¿\92l)\81C", count,
1801                             xname(otmp), count * (long) objects[typ].oc_cost,
1802                             currency(2L));
1803 #endif
1804                     obfree(otmp, (struct obj *) 0);
1805                 } else {
1806 #if 0 /*JP:T*/
1807                     Sprintf(pbuf, "%8ld worthless piece%s of colored glass,",
1808                             count, plur(count));
1809 #else
1810                     Sprintf(pbuf, "%ld\8cÂ\82Ì\89¿\92l\82Ì\82È\82¢\90F\82Â\82«\83K\83\89\83X\81C",
1811                             count);
1812 #endif
1813                 }
1814                 dump_forward_putstr(endwin, 0, pbuf, 0);
1815             }
1816         }
1817
1818     } else {
1819         /* did not escape or ascend */
1820         if (u.uz.dnum == 0 && u.uz.dlevel <= 0) {
1821             /* level teleported out of the dungeon; `how' is DIED,
1822                due to falling or to "arriving at heaven prematurely" */
1823 #if 0 /*JP:T*/
1824             Sprintf(pbuf, "You %s beyond the confines of the dungeon",
1825                     (u.uz.dlevel < 0) ? "passed away" : ends[how]);
1826 #else
1827             Sprintf(pbuf, "\96À\8b{\82Ì\97Ì\88æ\82ð\89z\82¦%s\81D",
1828                     (u.uz.dlevel < 0) ? "\8fÁ\82¦\82³\82Á\82½" : ends[how]);
1829 #endif
1830         } else {
1831             /* more conventional demise */
1832             const char *where = dungeons[u.uz.dnum].dname;
1833
1834             if (Is_astralevel(&u.uz))
1835 /*JP
1836                 where = "The Astral Plane";
1837 */
1838                 where = "\90¸\97ì\8aE\82É\82Ä";
1839 /*JP
1840             Sprintf(pbuf, "You %s in %s", ends[how], where);
1841 */
1842             Sprintf(pbuf, "\82 \82È\82½\82Í%s", where);
1843             if (!In_endgame(&u.uz) && !Is_knox(&u.uz))
1844 #if 0 /*JP:T*/
1845                 Sprintf(eos(pbuf), " on dungeon level %d",
1846                         In_quest(&u.uz) ? dunlev(&u.uz) : depth(&u.uz));
1847 #else
1848                 Sprintf(eos(pbuf), "\82Ì\92n\89º%d\8aK\82Å",
1849                         In_quest(&u.uz) ? dunlev(&u.uz) : depth(&u.uz));
1850 #endif
1851         }
1852
1853 /*JP
1854         Sprintf(eos(pbuf), " with %ld point%s,", u.urexp, plur(u.urexp));
1855 */
1856         Sprintf(eos(pbuf), " %ld\83|\83C\83\93\83g\82ð\83}\81[\83N\82µ\81C", u.urexp);
1857         dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
1858     }
1859
1860 #if 0 /*JP:T*/
1861     Sprintf(pbuf, "and %ld piece%s of gold, after %ld move%s.", umoney,
1862             plur(umoney), moves, plur(moves));
1863 #else
1864     Sprintf(pbuf, "%ld\96\87\82Ì\8bà\89Ý\82ð\8e\9d\82Á\82Ä\81C%ld\95à\93®\82¢\82½\81D", umoney,
1865             moves);
1866 #endif
1867     dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
1868 #if 0 /*JP:T*/
1869     Sprintf(pbuf,
1870             "You were level %d with a maximum of %d hit point%s when you %s.",
1871             u.ulevel, u.uhpmax, plur(u.uhpmax), ends[how]);
1872 #else
1873     Sprintf(pbuf,
1874             "%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",
1875             ends[how],u.ulevel, u.uhpmax);
1876 #endif
1877     dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
1878     dump_forward_putstr(endwin, 0, "", done_stopprint);
1879     if (!done_stopprint)
1880         display_nhwindow(endwin, TRUE);
1881     if (endwin != WIN_ERR)
1882         destroy_nhwindow(endwin);
1883
1884     dump_close_log();
1885     /* "So when I die, the first thing I will see in Heaven is a
1886      * score list?" */
1887     if (have_windows && !iflags.toptenwin)
1888         exit_nhwindows((char *) 0), have_windows = FALSE;
1889     topten(how, endtime);
1890     if (have_windows)
1891         exit_nhwindows((char *) 0);
1892
1893     if (done_stopprint) {
1894         raw_print("");
1895         raw_print("");
1896     }
1897     nh_terminate(EXIT_SUCCESS);
1898 }
1899
1900 void
1901 container_contents(list, identified, all_containers, reportempty)
1902 struct obj *list;
1903 boolean identified, all_containers, reportempty;
1904 {
1905     register struct obj *box, *obj;
1906     char buf[BUFSZ];
1907     boolean cat, dumping = iflags.in_dumplog;
1908
1909     for (box = list; box; box = box->nobj) {
1910         if (Is_container(box) || box->otyp == STATUE) {
1911             if (!box->cknown || (identified && !box->lknown)) {
1912                 box->cknown = 1; /* we're looking at the contents now */
1913                 if (identified)
1914                     box->lknown = 1;
1915                 update_inventory();
1916             }
1917             if (box->otyp == BAG_OF_TRICKS) {
1918                 continue; /* wrong type of container */
1919             } else if (box->cobj) {
1920                 winid tmpwin = create_nhwindow(NHW_MENU);
1921                 Loot *sortedcobj, *srtc;
1922                 unsigned sortflags;
1923
1924                 /* at this stage, the SchroedingerBox() flag is only set
1925                    if the cat inside the box is alive; the box actually
1926                    contains a cat corpse that we'll pretend is not there;
1927                    for dead cat, the flag will be clear and there'll be
1928                    a cat corpse inside the box; either way, inventory
1929                    reports the box as containing "1 item" */
1930                 cat = SchroedingersBox(box);
1931
1932 /*JP
1933                 Sprintf(buf, "Contents of %s:", the(xname(box)));
1934 */
1935                 Sprintf(buf, "%s\82Ì\92\86\90g\81F", the(xname(box)));
1936                 putstr(tmpwin, 0, buf);
1937                 if (!dumping)
1938                     putstr(tmpwin, 0, "");
1939                 buf[0] = buf[1] = ' '; /* two leading spaces */
1940                 if (box->cobj && !cat) {
1941                     sortflags = (((flags.sortloot == 'l'
1942                                    || flags.sortloot == 'f')
1943                                      ? SORTLOOT_LOOT : 0)
1944                                  | (flags.sortpack ? SORTLOOT_PACK : 0));
1945                     sortedcobj = sortloot(&box->cobj, sortflags, FALSE,
1946                                           (boolean FDECL((*), (OBJ_P))) 0);
1947                     for (srtc = sortedcobj; ((obj = srtc->obj) != 0); ++srtc) {
1948                         if (identified) {
1949                             discover_object(obj->otyp, TRUE, FALSE);
1950                             obj->known = obj->bknown = obj->dknown
1951                                 = obj->rknown = 1;
1952                             if (Is_container(obj) || obj->otyp == STATUE)
1953                                 obj->cknown = obj->lknown = 1;
1954                         }
1955                         Strcpy(&buf[2], doname_with_price(obj));
1956                         putstr(tmpwin, 0, buf);
1957                     }
1958                     unsortloot(&sortedcobj);
1959                 } else if (cat) {
1960 /*JP
1961                     Strcpy(&buf[2], "Schroedinger's cat!");
1962 */
1963                     Strcpy(&buf[2], "\83V\83\85\83\8c\83f\83B\83\93\83K\81[\82Ì\94L\81I");
1964                     putstr(tmpwin, 0, buf);
1965                 }
1966                 if (dumping)
1967                     putstr(0, 0, "");
1968                 display_nhwindow(tmpwin, TRUE);
1969                 destroy_nhwindow(tmpwin);
1970                 if (all_containers)
1971                     container_contents(box->cobj, identified, TRUE,
1972                                        reportempty);
1973             } else if (reportempty) {
1974 /*JP
1975                 pline("%s is empty.", upstart(thesimpleoname(box)));
1976 */
1977                 pline("%s\82Í\8bó\82Á\82Û\82¾\81D", xname(box));
1978                 display_nhwindow(WIN_MESSAGE, FALSE);
1979             }
1980         }
1981         if (!all_containers)
1982             break;
1983     }
1984 }
1985
1986 /* should be called with either EXIT_SUCCESS or EXIT_FAILURE */
1987 void
1988 nh_terminate(status)
1989 int status;
1990 {
1991     program_state.in_moveloop = 0; /* won't be returning to normal play */
1992 #ifdef MAC
1993     getreturn("to exit");
1994 #endif
1995     /* don't bother to try to release memory if we're in panic mode, to
1996        avoid trouble in case that happens to be due to memory problems */
1997     if (!program_state.panicking) {
1998         freedynamicdata();
1999         dlb_cleanup();
2000     }
2001
2002 #ifdef VMS
2003     /*
2004      *  This is liable to draw a warning if compiled with gcc, but it's
2005      *  more important to flag panic() -> really_done() -> nh_terminate()
2006      *  as __noreturn__ then to avoid the warning.
2007      */
2008     /* don't call exit() if already executing within an exit handler;
2009        that would cancel any other pending user-mode handlers */
2010     if (program_state.exiting)
2011         return;
2012 #endif
2013     program_state.exiting = 1;
2014 #if 1 /*JP*/
2015     jputchar('\0'); /* reset terminal */
2016 #endif
2017     nethack_exit(status);
2018 }
2019
2020 enum vanq_order_modes {
2021     VANQ_MLVL_MNDX = 0,
2022     VANQ_MSTR_MNDX,
2023     VANQ_ALPHA_SEP,
2024     VANQ_ALPHA_MIX,
2025     VANQ_MCLS_HTOL,
2026     VANQ_MCLS_LTOH,
2027     VANQ_COUNT_H_L,
2028     VANQ_COUNT_L_H,
2029
2030     NUM_VANQ_ORDER_MODES
2031 };
2032
2033 static const char *vanqorders[NUM_VANQ_ORDER_MODES] = {
2034     "traditional: by monster level, by internal monster index",
2035     "by monster toughness, by internal monster index",
2036     "alphabetically, first unique monsters, then others",
2037     "alphabetically, unique monsters and others intermixed",
2038     "by monster class, high to low level within class",
2039     "by monster class, low to high level within class",
2040     "by count, high to low, by internal index within tied count",
2041     "by count, low to high, by internal index within tied count",
2042 };
2043 static int vanq_sortmode = VANQ_MLVL_MNDX;
2044
2045 STATIC_PTR int CFDECLSPEC
2046 vanqsort_cmp(vptr1, vptr2)
2047 const genericptr vptr1;
2048 const genericptr vptr2;
2049 {
2050     int indx1 = *(short *) vptr1, indx2 = *(short *) vptr2,
2051         mlev1, mlev2, mstr1, mstr2, uniq1, uniq2, died1, died2, res;
2052     const char *name1, *name2, *punct;
2053     schar mcls1, mcls2;
2054
2055     switch (vanq_sortmode) {
2056     default:
2057     case VANQ_MLVL_MNDX:
2058         /* sort by monster level */
2059         mlev1 = mons[indx1].mlevel, mlev2 = mons[indx2].mlevel;
2060         res = mlev2 - mlev1; /* mlevel high to low */
2061         break;
2062     case VANQ_MSTR_MNDX:
2063         /* sort by monster toughness */
2064         mstr1 = mons[indx1].difficulty, mstr2 = mons[indx2].difficulty;
2065         res = mstr2 - mstr1; /* monstr high to low */
2066         break;
2067     case VANQ_ALPHA_SEP:
2068         uniq1 = ((mons[indx1].geno & G_UNIQ) && indx1 != PM_HIGH_PRIEST);
2069         uniq2 = ((mons[indx2].geno & G_UNIQ) && indx2 != PM_HIGH_PRIEST);
2070         if (uniq1 ^ uniq2) { /* one or other uniq, but not both */
2071             res = uniq2 - uniq1;
2072             break;
2073         } /* else both unique or neither unique */
2074         /*FALLTHRU*/
2075     case VANQ_ALPHA_MIX:
2076         name1 = mons[indx1].mname, name2 = mons[indx2].mname;
2077         res = strcmpi(name1, name2); /* caseblind alhpa, low to high */
2078         break;
2079     case VANQ_MCLS_HTOL:
2080     case VANQ_MCLS_LTOH:
2081         /* mons[].mlet is a small integer, 1..N, of type plain char;
2082            if 'char' happens to be unsigned, (mlet1 - mlet2) would yield
2083            an inappropriate result when mlet2 is greater than mlet1,
2084            so force our copies (mcls1, mcls2) to be signed */
2085         mcls1 = (schar) mons[indx1].mlet, mcls2 = (schar) mons[indx2].mlet;
2086         /* S_ANT through S_ZRUTY correspond to lowercase monster classes,
2087            S_ANGEL through S_ZOMBIE correspond to uppercase, and various
2088            punctuation characters are used for classes beyond those */
2089         if (mcls1 > S_ZOMBIE && mcls2 > S_ZOMBIE) {
2090             /* force a specific order to the punctuation classes that's
2091                different from the internal order;
2092                internal order is ok if neither or just one is punctuation
2093                since letters have lower values so come out before punct */
2094             static const char punctclasses[] = {
2095                 S_LIZARD, S_EEL, S_GOLEM, S_GHOST, S_DEMON, S_HUMAN, '\0'
2096             };
2097
2098             if ((punct = index(punctclasses, mcls1)) != 0)
2099                 mcls1 = (schar) (S_ZOMBIE + 1 + (int) (punct - punctclasses));
2100             if ((punct = index(punctclasses, mcls2)) != 0)
2101                 mcls2 = (schar) (S_ZOMBIE + 1 + (int) (punct - punctclasses));
2102         }
2103         res = mcls1 - mcls2; /* class */
2104         if (res == 0) {
2105             mlev1 = mons[indx1].mlevel, mlev2 = mons[indx2].mlevel;
2106             res = mlev1 - mlev2; /* mlevel low to high */
2107             if (vanq_sortmode == VANQ_MCLS_HTOL)
2108                 res = -res; /* mlevel high to low */
2109         }
2110         break;
2111     case VANQ_COUNT_H_L:
2112     case VANQ_COUNT_L_H:
2113         died1 = mvitals[indx1].died, died2 = mvitals[indx2].died;
2114         res = died2 - died1; /* dead count high to low */
2115         if (vanq_sortmode == VANQ_COUNT_L_H)
2116             res = -res; /* dead count low to high */
2117         break;
2118     }
2119     /* tiebreaker: internal mons[] index */
2120     if (res == 0)
2121         res = indx1 - indx2; /* mndx low to high */
2122     return res;
2123 }
2124
2125 /* returns -1 if cancelled via ESC */
2126 STATIC_OVL int
2127 set_vanq_order()
2128 {
2129     winid tmpwin;
2130     menu_item *selected;
2131     anything any;
2132     int i, n, choice;
2133
2134     tmpwin = create_nhwindow(NHW_MENU);
2135     start_menu(tmpwin);
2136     any = zeroany; /* zero out all bits */
2137     for (i = 0; i < SIZE(vanqorders); i++) {
2138         if (i == VANQ_ALPHA_MIX || i == VANQ_MCLS_HTOL) /* skip these */
2139             continue;
2140         any.a_int = i + 1;
2141         add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, vanqorders[i],
2142                  (i == vanq_sortmode) ? MENU_SELECTED : MENU_UNSELECTED);
2143     }
2144 /*JP
2145     end_menu(tmpwin, "Sort order for vanquished monster counts");
2146 */
2147     end_menu(tmpwin, "\93|\82µ\82½\93G\82Ì\83\\81[\83g\8f\87");
2148
2149     n = select_menu(tmpwin, PICK_ONE, &selected);
2150     destroy_nhwindow(tmpwin);
2151     if (n > 0) {
2152         choice = selected[0].item.a_int - 1;
2153         /* skip preselected entry if we have more than one item chosen */
2154         if (n > 1 && choice == vanq_sortmode)
2155             choice = selected[1].item.a_int - 1;
2156         free((genericptr_t) selected);
2157         vanq_sortmode = choice;
2158     }
2159     return (n < 0) ? -1 : vanq_sortmode;
2160 }
2161
2162 /* #vanquished command */
2163 int
2164 dovanquished()
2165 {
2166     list_vanquished('a', FALSE);
2167     return 0;
2168 }
2169
2170 /* high priests aren't unique but are flagged as such to simplify something */
2171 #define UniqCritterIndx(mndx) ((mons[mndx].geno & G_UNIQ) \
2172                                && mndx != PM_HIGH_PRIEST)
2173
2174 STATIC_OVL void
2175 list_vanquished(defquery, ask)
2176 char defquery;
2177 boolean ask;
2178 {
2179     register int i;
2180     int pfx, nkilled;
2181     unsigned ntypes, ni;
2182     long total_killed = 0L;
2183     winid klwin;
2184     short mindx[NUMMONS];
2185     char c, buf[BUFSZ], buftoo[BUFSZ];
2186     boolean dumping; /* for DUMPLOG; doesn't need to be conditional */
2187
2188     dumping = (defquery == 'd');
2189     if (dumping)
2190         defquery = 'y';
2191
2192     /* get totals first */
2193     ntypes = 0;
2194     for (i = LOW_PM; i < NUMMONS; i++) {
2195         if ((nkilled = (int) mvitals[i].died) == 0)
2196             continue;
2197         mindx[ntypes++] = i;
2198         total_killed += (long) nkilled;
2199     }
2200
2201     /* vanquished creatures list;
2202      * includes all dead monsters, not just those killed by the player
2203      */
2204     if (ntypes != 0) {
2205         char mlet, prev_mlet = 0; /* used as small integer, not character */
2206         boolean class_header, uniq_header, was_uniq = FALSE;
2207
2208 #if 0 /*JP:T*/
2209         c = ask ? yn_function(
2210                             "Do you want an account of creatures vanquished?",
2211                               ynaqchars, defquery)
2212                 : defquery;
2213 #else
2214         c = ask ? yn_function(
2215                             "\93|\82µ\82½\93G\82Ì\88ê\97\97\82ð\8c©\82Ü\82·\82©\81H",
2216                               ynqchars, defquery)
2217                 : defquery;
2218 #endif
2219         if (c == 'q')
2220             done_stopprint++;
2221         if (c == 'y' || c == 'a') {
2222             if (c == 'a') { /* ask player to choose sort order */
2223                 /* choose value for vanq_sortmode via menu; ESC cancels list
2224                    of vanquished monsters but does not set 'done_stopprint' */
2225                 if (set_vanq_order() < 0)
2226                     return;
2227             }
2228             uniq_header = (vanq_sortmode == VANQ_ALPHA_SEP);
2229             class_header = (vanq_sortmode == VANQ_MCLS_LTOH
2230                             || vanq_sortmode == VANQ_MCLS_HTOL);
2231
2232             klwin = create_nhwindow(NHW_MENU);
2233 /*JP
2234             putstr(klwin, 0, "Vanquished creatures:");
2235 */
2236             putstr(klwin, 0, "\93|\82µ\82½\93G\81F");
2237             if (!dumping)
2238                 putstr(klwin, 0, "");
2239
2240             qsort((genericptr_t) mindx, ntypes, sizeof *mindx, vanqsort_cmp);
2241             for (ni = 0; ni < ntypes; ni++) {
2242                 i = mindx[ni];
2243                 nkilled = mvitals[i].died;
2244                 mlet = mons[i].mlet;
2245                 if (class_header && mlet != prev_mlet) {
2246                     Strcpy(buf, def_monsyms[(int) mlet].explain);
2247                     putstr(klwin, ask ? 0 : iflags.menu_headings,
2248                            upstart(buf));
2249                     prev_mlet = mlet;
2250                 }
2251                 if (UniqCritterIndx(i)) {
2252 #if 0 /*JP*/
2253                     Sprintf(buf, "%s%s",
2254                             !type_is_pname(&mons[i]) ? "the " : "",
2255                             mons[i].mname);
2256 #else
2257                     Sprintf(buf, "%s", mons[i].mname);
2258 #endif
2259                     if (nkilled > 1) {
2260 #if 0 /*JP:T*/
2261                         switch (nkilled) {
2262                         case 2:
2263                             Sprintf(eos(buf), " (twice)");
2264                             break;
2265                         case 3:
2266                             Sprintf(eos(buf), " (thrice)");
2267                             break;
2268                         default:
2269                             Sprintf(eos(buf), " (%d times)", nkilled);
2270                             break;
2271                         }
2272 #else
2273                         Sprintf(eos(buf)," (%d\89ñ)", nkilled);
2274 #endif
2275                     }
2276                     was_uniq = TRUE;
2277                 } else {
2278                     if (uniq_header && was_uniq) {
2279                         putstr(klwin, 0, "");
2280                         was_uniq = FALSE;
2281                     }
2282                     /* trolls or undead might have come back,
2283                        but we don't keep track of that */
2284                     if (nkilled == 1)
2285                         Strcpy(buf, an(mons[i].mname));
2286                     else
2287 #if 0 /*JP:T*/
2288                         Sprintf(buf, "%3d %s", nkilled,
2289                                 makeplural(mons[i].mname));
2290 #else
2291                         Sprintf(buf, "%d\91Ì\82Ì%s", nkilled,
2292                                 mons[i].mname);
2293 #endif
2294                 }
2295                 /* number of leading spaces to match 3 digit prefix */
2296 #if 0 /*JP*/
2297                 pfx = !strncmpi(buf, "the ", 3) ? 0
2298                       : !strncmpi(buf, "an ", 3) ? 1
2299                         : !strncmpi(buf, "a ", 2) ? 2
2300                           : !digit(buf[2]) ? 4 : 0;
2301 #else
2302                 pfx = !digit(buf[2]) ? 4 : 0;
2303 #endif
2304                 if (class_header)
2305                     ++pfx;
2306                 Sprintf(buftoo, "%*s%s", pfx, "", buf);
2307                 putstr(klwin, 0, buftoo);
2308             }
2309             /*
2310              * if (Hallucination)
2311              *     putstr(klwin, 0, "and a partridge in a pear tree");
2312              */
2313             if (ntypes > 1) {
2314                 if (!dumping)
2315                     putstr(klwin, 0, "");
2316 /*JP
2317                 Sprintf(buf, "%ld creatures vanquished.", total_killed);
2318 */
2319                 Sprintf(buf, "%ld\95C\82Ì\93G\82ð\93|\82µ\82½\81D", total_killed);
2320                 putstr(klwin, 0, buf);
2321             }
2322             display_nhwindow(klwin, TRUE);
2323             destroy_nhwindow(klwin);
2324         }
2325     } else if (defquery == 'a') {
2326         /* #dovanquished rather than final disclosure, so pline() is ok */
2327 /*JP
2328         pline("No creatures have been vanquished.");
2329 */
2330         pline("\93|\82µ\82½\93G\82Í\82¢\82È\82©\82Á\82½\81D");
2331 #ifdef DUMPLOG
2332     } else if (dumping) {
2333         putstr(0, 0, "No creatures were vanquished."); /* not pline() */
2334 #endif
2335     }
2336 }
2337
2338 /* number of monster species which have been genocided */
2339 int
2340 num_genocides()
2341 {
2342     int i, n = 0;
2343
2344     for (i = LOW_PM; i < NUMMONS; ++i) {
2345         if (mvitals[i].mvflags & G_GENOD) {
2346             ++n;
2347             if (UniqCritterIndx(i))
2348                 impossible("unique creature '%d: %s' genocided?",
2349                            i, mons[i].mname);
2350         }
2351     }
2352     return n;
2353 }
2354
2355 STATIC_OVL int
2356 num_extinct()
2357 {
2358     int i, n = 0;
2359
2360     for (i = LOW_PM; i < NUMMONS; ++i) {
2361         if (UniqCritterIndx(i))
2362             continue;
2363         if ((mvitals[i].mvflags & G_GONE) == G_EXTINCT)
2364             ++n;
2365     }
2366     return n;
2367 }
2368
2369 STATIC_OVL void
2370 list_genocided(defquery, ask)
2371 char defquery;
2372 boolean ask;
2373 {
2374     register int i;
2375     int ngenocided, nextinct;
2376     char c;
2377     winid klwin;
2378     char buf[BUFSZ];
2379     boolean dumping; /* for DUMPLOG; doesn't need to be conditional */
2380
2381     dumping = (defquery == 'd');
2382     if (dumping)
2383         defquery = 'y';
2384
2385     ngenocided = num_genocides();
2386     nextinct = num_extinct();
2387
2388     /* genocided or extinct species list */
2389     if (ngenocided != 0 || nextinct != 0) {
2390 #if 0 /*JP:T*/
2391         Sprintf(buf, "Do you want a list of %sspecies%s%s?",
2392                 (nextinct && !ngenocided) ? "extinct " : "",
2393                 (ngenocided) ? " genocided" : "",
2394                 (nextinct && ngenocided) ? " and extinct" : "");
2395 #else
2396         Sprintf(buf, "%s%s%s\82µ\82½\8eí\82Ì\88ê\97\97\82ð\8c©\82Ü\82·\82©\81H",
2397                 (nextinct && !ngenocided) ? "\90â\96Å" : "",
2398                 (ngenocided) ? "\8bs\8eE" : "",
2399                 (nextinct && ngenocided) ? "\82¨\82æ\82Ñ\90â\96Å" : "");
2400 #endif
2401         c = ask ? yn_function(buf, ynqchars, defquery) : defquery;
2402         if (c == 'q')
2403             done_stopprint++;
2404         if (c == 'y') {
2405             klwin = create_nhwindow(NHW_MENU);
2406 #if 0 /*JP:T*/
2407             Sprintf(buf, "%s%s species:",
2408                     (ngenocided) ? "Genocided" : "Extinct",
2409                     (nextinct && ngenocided) ? " or extinct" : "");
2410 #else
2411             Sprintf(buf, "%s%s\82µ\82½\8eí:",
2412                     (ngenocided) ? "\8bs\8eE" : "\90â\96Å",
2413                     (nextinct && ngenocided) ? "\82Ü\82½\82Í\90â\96Å" : "");
2414 #endif
2415             putstr(klwin, 0, buf);
2416             if (!dumping)
2417                 putstr(klwin, 0, "");
2418
2419             for (i = LOW_PM; i < NUMMONS; i++) {
2420                 /* uniques can't be genocided but can become extinct;
2421                    however, they're never reported as extinct, so skip them */
2422                 if (UniqCritterIndx(i))
2423                     continue;
2424                 if (mvitals[i].mvflags & G_GONE) {
2425                     Sprintf(buf, " %s", makeplural(mons[i].mname));
2426                     /*
2427                      * "Extinct" is unfortunate terminology.  A species
2428                      * is marked extinct when its birth limit is reached,
2429                      * but there might be members of the species still
2430                      * alive, contradicting the meaning of the word.
2431                      */
2432                     if ((mvitals[i].mvflags & G_GONE) == G_EXTINCT)
2433 /*JP
2434                         Strcat(buf, " (extinct)");
2435 */
2436                         Strcat(buf, "(\90â\96Å)");
2437                     putstr(klwin, 0, buf);
2438                 }
2439             }
2440             if (!dumping)
2441                 putstr(klwin, 0, "");
2442             if (ngenocided > 0) {
2443 /*JP
2444                 Sprintf(buf, "%d species genocided.", ngenocided);
2445 */
2446                 Sprintf(buf, "%d\8eí\97Þ\82Ì\8eí\82ð\8bs\8eE\82µ\82½\81D", ngenocided);
2447                 putstr(klwin, 0, buf);
2448             }
2449             if (nextinct > 0) {
2450 /*JP
2451                 Sprintf(buf, "%d species extinct.", nextinct);
2452 */
2453                 Sprintf(buf, "%d\8eí\97Þ\82Ì\8eí\82ð\90â\96Å\82³\82¹\82½\81D", nextinct);
2454                 putstr(klwin, 0, buf);
2455             }
2456
2457             display_nhwindow(klwin, TRUE);
2458             destroy_nhwindow(klwin);
2459         }
2460 #ifdef DUMPLOG
2461     } else if (dumping) {
2462         putstr(0, 0, "No species were genocided or became extinct.");
2463 #endif
2464     }
2465 }
2466
2467 /* set a delayed killer, ensure non-delayed killer is cleared out */
2468 void
2469 delayed_killer(id, format, killername)
2470 int id;
2471 int format;
2472 const char *killername;
2473 {
2474     struct kinfo *k = find_delayed_killer(id);
2475
2476     if (!k) {
2477         /* no match, add a new delayed killer to the list */
2478         k = (struct kinfo *) alloc(sizeof (struct kinfo));
2479         (void) memset((genericptr_t) k, 0, sizeof (struct kinfo));
2480         k->id = id;
2481         k->next = killer.next;
2482         killer.next = k;
2483     }
2484
2485     k->format = format;
2486     Strcpy(k->name, killername ? killername : "");
2487     killer.name[0] = 0;
2488 }
2489
2490 struct kinfo *
2491 find_delayed_killer(id)
2492 int id;
2493 {
2494     struct kinfo *k;
2495
2496     for (k = killer.next; k != (struct kinfo *) 0; k = k->next) {
2497         if (k->id == id)
2498             break;
2499     }
2500     return k;
2501 }
2502
2503 void
2504 dealloc_killer(kptr)
2505 struct kinfo *kptr;
2506 {
2507     struct kinfo *prev = &killer, *k;
2508
2509     if (kptr == (struct kinfo *) 0)
2510         return;
2511     for (k = killer.next; k != (struct kinfo *) 0; k = k->next) {
2512         if (k == kptr)
2513             break;
2514         prev = k;
2515     }
2516
2517     if (k == (struct kinfo *) 0) {
2518         impossible("dealloc_killer (#%d) not on list", kptr->id);
2519     } else {
2520         prev->next = k->next;
2521         free((genericptr_t) k);
2522         debugpline1("freed delayed killer #%d", kptr->id);
2523     }
2524 }
2525
2526 void
2527 save_killers(fd, mode)
2528 int fd;
2529 int mode;
2530 {
2531     struct kinfo *kptr;
2532
2533     if (perform_bwrite(mode)) {
2534         for (kptr = &killer; kptr != (struct kinfo *) 0; kptr = kptr->next) {
2535             bwrite(fd, (genericptr_t) kptr, sizeof (struct kinfo));
2536         }
2537     }
2538     if (release_data(mode)) {
2539         while (killer.next) {
2540             kptr = killer.next->next;
2541             free((genericptr_t) killer.next);
2542             killer.next = kptr;
2543         }
2544     }
2545 }
2546
2547 void
2548 restore_killers(fd)
2549 int fd;
2550 {
2551     struct kinfo *kptr;
2552
2553     for (kptr = &killer; kptr != (struct kinfo *) 0; kptr = kptr->next) {
2554         mread(fd, (genericptr_t) kptr, sizeof (struct kinfo));
2555         if (kptr->next) {
2556             kptr->next = (struct kinfo *) alloc(sizeof (struct kinfo));
2557         }
2558     }
2559 }
2560
2561 static int
2562 wordcount(p)
2563 char *p;
2564 {
2565     int words = 0;
2566
2567     while (*p) {
2568         while (*p && isspace((uchar) *p))
2569             p++;
2570         if (*p)
2571             words++;
2572         while (*p && !isspace((uchar) *p))
2573             p++;
2574     }
2575     return words;
2576 }
2577
2578 static void
2579 bel_copy1(inp, out)
2580 char **inp, *out;
2581 {
2582     char *in = *inp;
2583
2584     out += strlen(out); /* eos() */
2585     while (*in && isspace((uchar) *in))
2586         in++;
2587     while (*in && !isspace((uchar) *in))
2588         *out++ = *in++;
2589     *out = '\0';
2590     *inp = in;
2591 }
2592
2593 /*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­*/
2594 char *
2595 build_english_list(in)
2596 char *in;
2597 {
2598     char *out, *p = in;
2599     int len = (int) strlen(p), words = wordcount(p);
2600
2601     /* +3: " or " - " "; +(words - 1): (N-1)*(", " - " ") */
2602     if (words > 1)
2603         len += 3 + (words - 1);
2604     out = (char *) alloc(len + 1);
2605     *out = '\0'; /* bel_copy1() appends */
2606
2607     switch (words) {
2608     case 0:
2609         impossible("no words in list");
2610         break;
2611     case 1:
2612         /* "single" */
2613         bel_copy1(&p, out);
2614         break;
2615     default:
2616         if (words == 2) {
2617             /* "first or second" */
2618             bel_copy1(&p, out);
2619             Strcat(out, " ");
2620         } else {
2621             /* "first, second, or third */
2622             do {
2623                 bel_copy1(&p, out);
2624                 Strcat(out, ", ");
2625             } while (--words > 1);
2626         }
2627         Strcat(out, "or ");
2628         bel_copy1(&p, out);
2629         break;
2630     }
2631     return out;
2632 }
2633
2634 /*end.c*/