1 /* NetHack 3.7	end.c	$NHDT-Date: 1615304753 2021/03/09 15:45:53 $  $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.222 $ */
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 #ifndef NO_SIGNAL
10 #include <signal.h>
11 #endif
12 #include <ctype.h>
13 #ifndef LONG_MAX
14 #include <limits.h>
15 #endif
16 #include "dlb.h"
17 
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 #ifndef NO_SIGNAL
23 static void done_intr(int);
24 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
25 static void done_hangup(int);
26 #endif
27 #endif
28 static boolean sentient_arise(int);
29 static void disclose(int, boolean);
30 static void get_valuables(struct obj *);
31 static void sort_valuables(struct valuable_data *, int);
32 static void done_object_cleanup(void);
33 static void artifact_score(struct obj *, boolean, winid);
34 static void really_done(int) NORETURN;
35 static void savelife(int);
36 static boolean should_query_disclose_option(int, char *);
37 #if defined(DUMPLOG) || defined(DUMPHTML)
38 static void dump_plines(void);
39 #endif
40 static void dump_everything(int, time_t);
41 
42 #if defined(__BEOS__) || defined(MICRO) || defined(OS2) || defined(WIN32)
43 extern void nethack_exit(int) NORETURN;
44 #else
45 #define nethack_exit exit
46 #endif
47 
48 #define done_stopprint g.program_state.stopprint
49 
50 #ifndef PANICTRACE
51 #define NH_abort NH_abort_
52 #endif
53 
54 #ifdef AMIGA
55 #define NH_abort_() Abort(0)
56 #else
57 #ifdef SYSV
58 #define NH_abort_() (void) abort()
59 #else
60 #ifdef WIN32
61 #define NH_abort_() win32_abort()
62 #else
63 #define NH_abort_() abort()
64 #endif
65 #endif /* !SYSV */
66 #endif /* !AMIGA */
67 
68 #ifdef PANICTRACE
69 #include <errno.h>
70 #ifdef PANICTRACE_LIBC
71 #include <execinfo.h>
72 #endif
73 
74 /* What do we try and in what order?  Tradeoffs:
75  * libc: +no external programs required
76  *        -requires newish libc/glibc
77  *        -requires -rdynamic
78  * gdb:   +gives more detailed information
79  *        +works on more OS versions
80  *        -requires -g, which may preclude -O on some compilers
81  */
82 #ifdef SYSCF
83 #define SYSOPT_PANICTRACE_GDB sysopt.panictrace_gdb
84 #ifdef PANICTRACE_LIBC
85 #define SYSOPT_PANICTRACE_LIBC sysopt.panictrace_libc
86 #else
87 #define SYSOPT_PANICTRACE_LIBC 0
88 #endif
89 #else
90 #define SYSOPT_PANICTRACE_GDB (nh_getenv("NETHACK_USE_GDB") == 0 ? 0 : 2)
91 #ifdef PANICTRACE_LIBC
92 #define SYSOPT_PANICTRACE_LIBC 1
93 #else
94 #define SYSOPT_PANICTRACE_LIBC 0
95 #endif
96 #endif
97 
98 static void NH_abort(void);
99 #ifndef NO_SIGNAL
100 static void panictrace_handler(int);
101 #endif
102 static boolean NH_panictrace_libc(void);
103 static boolean NH_panictrace_gdb(void);
104 
105 #ifndef NO_SIGNAL
106 /* called as signal() handler, so sent at least one arg */
107 /*ARGUSED*/
108 void
panictrace_handler(int sig_unused UNUSED)109 panictrace_handler(int sig_unused UNUSED)
110 {
111 #define SIG_MSG "\nSignal received.\n"
112     int f2;
113 
114 #ifdef CURSES_GRAPHICS
115     if (iflags.window_inited && WINDOWPORT("curses")) {
116         extern void curses_uncurse_terminal(void); /* wincurs.h */
117 
118         /* it is risky calling this during a program-terminating signal,
119            but without it the subsequent backtrace is useless because
120            that ends up being scrawled all over the screen; call is
121            here rather than in NH_abort() because panic() calls both
122            exit_nhwindows(), which makes this same call under curses,
123            then NH_abort() and we don't want to call this twice */
124         curses_uncurse_terminal();
125     }
126 #endif
127 
128     f2 = (int) write(2, SIG_MSG, sizeof SIG_MSG - 1);
129     nhUse(f2);  /* what could we do if write to fd#2 (stderr) fails  */
130     NH_abort(); /* ... and we're already in the process of quitting? */
131 }
132 
133 void
panictrace_setsignals(boolean set)134 panictrace_setsignals(boolean set)
135 {
136 #define SETSIGNAL(sig) \
137     (void) signal(sig, set ? (SIG_RET_TYPE) panictrace_handler : SIG_DFL);
138 #ifdef SIGILL
139     SETSIGNAL(SIGILL);
140 #endif
141 #ifdef SIGTRAP
142     SETSIGNAL(SIGTRAP);
143 #endif
144 #ifdef SIGIOT
145     SETSIGNAL(SIGIOT);
146 #endif
147 #ifdef SIGBUS
148     SETSIGNAL(SIGBUS);
149 #endif
150 #ifdef SIGFPE
151     SETSIGNAL(SIGFPE);
152 #endif
153 #ifdef SIGSEGV
154     SETSIGNAL(SIGSEGV);
155 #endif
156 #ifdef SIGSTKFLT
157     SETSIGNAL(SIGSTKFLT);
158 #endif
159 #ifdef SIGSYS
160     SETSIGNAL(SIGSYS);
161 #endif
162 #ifdef SIGEMT
163     SETSIGNAL(SIGEMT);
164 #endif
165 #undef SETSIGNAL
166 }
167 #endif /* NO_SIGNAL */
168 
169 static void
NH_abort(void)170 NH_abort(void)
171 {
172     int gdb_prio = SYSOPT_PANICTRACE_GDB;
173     int libc_prio = SYSOPT_PANICTRACE_LIBC;
174     static volatile boolean aborting = FALSE;
175 
176     /* don't execute this code recursively if a second abort is requested
177        while this routine or the code it calls is executing */
178     if (aborting)
179         return;
180     aborting = TRUE;
181 
182 #ifndef VMS
183     if (gdb_prio == libc_prio && gdb_prio > 0)
184         gdb_prio++;
185 
186     if (gdb_prio > libc_prio) {
187         (void) (NH_panictrace_gdb() || (libc_prio && NH_panictrace_libc()));
188     } else {
189         (void) (NH_panictrace_libc() || (gdb_prio && NH_panictrace_gdb()));
190     }
191 
192 #else /* VMS */
193     /* overload otherwise unused priority for debug mode: 1 = show
194        traceback and exit; 2 = show traceback and stay in debugger */
195     /* if (wizard && gdb_prio == 1) gdb_prio = 2; */
196     vms_traceback(gdb_prio);
197     nhUse(libc_prio);
198 
199 #endif /* ?VMS */
200 
201 #ifndef NO_SIGNAL
202     panictrace_setsignals(FALSE);
203 #endif
204     NH_abort_();
205 }
206 
207 static boolean
NH_panictrace_libc(void)208 NH_panictrace_libc(void)
209 {
210 #ifdef PANICTRACE_LIBC
211     void *bt[20];
212     size_t count, x;
213     char **info, buf[BUFSZ];
214 
215     raw_print("  Generating more information you may report:\n");
216     count = backtrace(bt, SIZE(bt));
217     info = backtrace_symbols(bt, count);
218     for (x = 0; x < count; x++) {
219         copynchars(buf, info[x], (int) sizeof buf - 1);
220         /* try to remove up to 16 blank spaces by removing 8 twice */
221         (void) strsubst(buf, "        ", "");
222         (void) strsubst(buf, "        ", "");
223         raw_printf("[%02lu] %s", (unsigned long) x, buf);
224     }
225     /* free(info);   -- Don't risk it. */
226     return TRUE;
227 #else
228     return FALSE;
229 #endif /* !PANICTRACE_LIBC */
230 }
231 
232 /*
233  *   fooPATH  file system path for foo
234  *   fooVAR   (possibly const) variable containing fooPATH
235  */
236 #ifdef PANICTRACE_GDB
237 #ifdef SYSCF
238 #define GDBVAR sysopt.gdbpath
239 #define GREPVAR sysopt.greppath
240 #else /* SYSCF */
241 #define GDBVAR GDBPATH
242 #define GREPVAR GREPPATH
243 #endif /* SYSCF */
244 #endif /* PANICTRACE_GDB */
245 
246 static boolean
NH_panictrace_gdb(void)247 NH_panictrace_gdb(void)
248 {
249 #ifdef PANICTRACE_GDB
250     /* A (more) generic method to get a stack trace - invoke
251      * gdb on ourself. */
252     const char *gdbpath = GDBVAR;
253     const char *greppath = GREPVAR;
254     char buf[BUFSZ];
255     FILE *gdb;
256 
257     if (gdbpath == NULL || gdbpath[0] == 0)
258         return FALSE;
259     if (greppath == NULL || greppath[0] == 0)
260         return FALSE;
261 
262     sprintf(buf, "%s -n -q %s %d 2>&1 | %s '^#'",
263             gdbpath, ARGV0, getpid(), greppath);
264     gdb = popen(buf, "w");
265     if (gdb) {
266         raw_print("  Generating more information you may report:\n");
267         fprintf(gdb, "bt\nquit\ny");
268         fflush(gdb);
269         sleep(4); /* ugly */
270         pclose(gdb);
271         return TRUE;
272     } else {
273         return FALSE;
274     }
275 #else
276     return FALSE;
277 #endif /* !PANICTRACE_GDB */
278 }
279 #endif /* PANICTRACE */
280 
281 /*
282  * The order of these needs to match the macros in hack.h.
283  */
284 static NEARDATA const char *deaths[] = {
285     /* the array of death */
286     "died", "choked", "poisoned", "starvation", "drowning", "burning",
287     "dissolving under the heat and pressure", "crushed", "turned to stone",
288     "turned into slime", "genocided", "panic", "trickery", "quit",
289     "escaped", "ascended"
290 };
291 
292 static NEARDATA const char *ends[] = {
293     /* "when you %s" */
294     "died", "choked", "were poisoned",
295     "starved", "drowned", "burned",
296     "dissolved in the lava",
297     "were crushed", "turned to stone",
298     "turned into slime", "were genocided",
299     "panicked", "were tricked", "quit",
300     "escaped", "ascended"
301 };
302 
303 static boolean Schroedingers_cat = FALSE;
304 
305 /* called as signal() handler, so sent at least one arg */
306 /*ARGSUSED*/
307 void
done1(int sig_unused UNUSED)308 done1(int sig_unused UNUSED)
309 {
310 #ifndef NO_SIGNAL
311     (void) signal(SIGINT, SIG_IGN);
312 #endif
313     if (flags.ignintr) {
314 #ifndef NO_SIGNAL
315         (void) signal(SIGINT, (SIG_RET_TYPE) done1);
316 #endif
317         clear_nhwindow(WIN_MESSAGE);
318         curs_on_u();
319         wait_synch();
320         if (g.multi > 0)
321             nomul(0);
322     } else {
323         (void) done2();
324     }
325 }
326 
327 /* "#quit" command or keyboard interrupt */
328 int
done2(void)329 done2(void)
330 {
331     if (iflags.debug_fuzzer)
332         return 0;
333     if (!paranoid_query(ParanoidQuit, "Really quit? (This will end your game permanently!)")) {
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 (g.multi > 0)
341             nomul(0);
342         if (g.multi == 0) {
343             u.uinvulnerable = FALSE; /* avoid ctrl-C bug -dlc */
344             u.usleep = 0;
345         }
346         return 0;
347     }
348 #if (defined(UNIX) || defined(VMS) || defined(LATTICE))
349     if (wizard) {
350         int c;
351 #ifdef VMS
352         extern int debuggable; /* sys/vms/vmsmisc.c, vmsunix.c */
353 
354         c = !debuggable ? 'n' : ynq("Enter debugger?");
355 #else
356 #ifdef LATTICE
357         c = ynq("Create SnapShot?");
358 #else
359         c = ynq("Dump core?");
360 #endif
361 #endif
362         if (c == 'y') {
363 #ifndef NO_SIGNAL
364             (void) signal(SIGINT, (SIG_RET_TYPE) done1);
365 #endif
366             exit_nhwindows((char *) 0);
367             NH_abort();
368         } else if (c == 'q')
369             done_stopprint++;
370     }
371 #endif
372 #ifndef LINT
373     done(QUIT);
374 #endif
375     return 0;
376 }
377 
378 #ifndef NO_SIGNAL
379 /* called as signal() handler, so sent at least 1 arg */
380 /*ARGSUSED*/
381 static void
done_intr(int sig_unused UNUSED)382 done_intr(int sig_unused UNUSED)
383 {
384     done_stopprint++;
385     (void) signal(SIGINT, SIG_IGN);
386 #if defined(UNIX) || defined(VMS)
387     (void) signal(SIGQUIT, SIG_IGN);
388 #endif
389     return;
390 }
391 
392 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
393 /* signal() handler */
394 static void
done_hangup(int sig)395 done_hangup(int sig)
396 {
397     g.program_state.done_hup++;
398     sethanguphandler((void (*)(int)) SIG_IGN);
399     done_intr(sig);
400     return;
401 }
402 #endif
403 #endif /* NO_SIGNAL */
404 
405 /* If the player is killed by something that allows their body to continue
406  * existing as some kind of monster that's not a ghost, they might retain
407  * their original mind and agency (and the game won't end after all). However,
408  * this comes with a drawback - the new form has intrinsic unchanging, and dying
409  * in that form will cause permanent death.
410  * Returns true if and only if the player can continue playing.
411  * Assumes u.ugrave_arise has already been set to the correct monster number.
412  */
413 static boolean
sentient_arise(int how)414 sentient_arise(int how)
415 {
416     int mon_nm;
417     if (how == TURNED_SLIME) {
418         /* special case, isn't set by done_in_by */
419         mon_nm = PM_GREEN_SLIME;
420     } else {
421         mon_nm = u.ugrave_arise;
422     }
423     if (mon_nm < LOW_PM || (g.mvitals[mon_nm].mvflags & G_GENOD)) {
424         return FALSE;
425     }
426     /* already got one shot... don't give third chances.
427      * or was unluckily wearing an amulet of unchanging and can't resurrect as
428      * a monster anyway */
429     if (Unchanging) {
430         return FALSE;
431     }
432     /* different monsters have different chances of keeping mind intact */
433     if ((mon_nm == PM_GREEN_SLIME && !rn2(2)) ||
434         (mon_nm == PM_WRAITH && !rn2(6)) ||
435         (mon_nm == PM_VAMPIRE && !rn2(10)) ||
436         (mon_nm == PM_GHOUL && !rn2(5)) ||
437         (mons[mon_nm].mlet == S_ZOMBIE && !rn2(4)) ||
438         (mons[mon_nm].mlet == S_MUMMY && !rn2(8))) {
439         u.ugrave_arise = NON_PM;
440 
441         if (mon_nm != PM_GREEN_SLIME) {
442             /* green slime already has transformation messages */
443             pline("Your body becomes corrupted...");
444         }
445         /* Set unchanging FIRST (polymon doesn't care about it):
446          * otherwise it's possible for the hero to get blasted by artifacts or
447          * whatever after transforming in polymon, and if that calls rehumanize
448          * before unchanging is set, the hero will rehumanize normally. */
449         HUnchanging |= FROMOUTSIDE;
450         if (mon_nm != PM_GREEN_SLIME) {
451             /* slimed_to_death() already polyed us into green slime, we don't
452              * want to call it again as that will trigger newman() */
453             polymon(mon_nm, POLYMON_ALL_MSGS);
454         }
455         else {
456             /* but... at the same time, need to undo done()'s setting of mh to 0
457              * if we did turn into a green slime (mhmax should still be set from
458              * the first polymon) */
459             u.mh = u.mhmax;
460         }
461         livelog_printf(LL_LIFESAVE, "became permanently corrupted into a %s",
462                        pmname(&mons[mon_nm], Ugender));
463         /* bug: if u.uhp <= 0, monsters won't attack for some reason
464          * set to max, not like you're ever going to need it again... */
465         u.uhp = u.uhpmax;
466         pline("But wait...");
467         pline("Your mind is intact.  You still remember your quest.");
468         return TRUE;
469     }
470     return FALSE;
471 }
472 
473 /* The player has just died to mtmp, but might be in human or monster form, and
474  * if the latter they might rehumanize.
475  * Set killer accordingly; do nothing else.
476  */
477 void
format_monkiller(struct monst * mtmp)478 format_monkiller(struct monst *mtmp)
479 {
480     char buf[BUFSZ];
481     struct permonst *mptr = mtmp->data,
482                     *champtr = ((mtmp->cham >= LOW_PM)
483                                    ? &mons[mtmp->cham]
484                                    : mptr);
485     boolean distorted = (boolean) (Hallucination && canspotmon(mtmp)),
486             mimicker = (M_AP_TYPE(mtmp) == M_AP_MONSTER),
487             imitator = (mptr != champtr || mimicker);
488     char *mwounds = mon_wounds(mtmp, FALSE, TRUE);
489 
490     buf[0] = '\0';
491     g.killer.format = KILLED_BY_AN;
492     /* "killed by the high priest of Crom" is okay,
493        "killed by the high priest" alone isn't */
494     if ((mptr->geno & G_UNIQ) != 0 && !(imitator && !mimicker)
495         && !(mptr == &mons[PM_HIGH_CLERIC] && !mtmp->ispriest)) {
496         if (!type_is_pname(mptr))
497             Strcat(buf, "the ");
498         g.killer.format = KILLED_BY;
499     }
500     /* _the_ <invisible> <distorted> ghost of Dudley */
501     if (has_ebones(mtmp) && has_mgivenname(mtmp)) {
502         Strcat(buf, "the ");
503         g.killer.format = KILLED_BY;
504     }
505 
506     if (mwounds) {
507         Strcat(buf, mwounds);
508         strkitten(buf, ' ');
509     }
510     if (mtmp->minvis)
511         Strcat(buf, "invisible ");
512     if (distorted)
513         Strcat(buf, "hallucinogen-distorted ");
514 
515     if (imitator) {
516         char shape[BUFSZ];
517         const char *realnm = pmname(champtr, Mgender(mtmp)),
518                              *fakenm = pmname(mptr, Mgender(mtmp));
519         boolean alt = is_vampshifter(mtmp);
520 
521         if (mimicker) {
522             /* realnm is already correct because champtr==mptr;
523                set up fake mptr for type_is_pname/the_unique_pm */
524             mptr = &mons[mtmp->mappearance];
525             fakenm = pmname(mptr, Mgender(mtmp));
526         } else if (alt && strstri(realnm, "vampire")
527                    && !strcmp(fakenm, "vampire bat")) {
528             /* special case: use "vampire in bat form" in preference
529                to redundant looking "vampire in vampire bat form" */
530             fakenm = "bat";
531         }
532         /* for the alternate format, always suppress any article;
533            pname and the_unique should also have s_suffix() applied,
534            but vampires don't take on any shapes which warrant that */
535         if (alt || type_is_pname(mptr)) /* no article */
536             Strcpy(shape, fakenm);
537         else if (the_unique_pm(mptr)) /* "the"; don't use the() here */
538             Sprintf(shape, "the %s", fakenm);
539         else /* "a"/"an" */
540             Strcpy(shape, an(fakenm));
541         /* omit "called" to avoid excessive verbosity */
542         Sprintf(eos(buf),
543                 alt ? "%s in %s form"
544                     : mimicker ? "%s disguised as %s"
545                                : "%s imitating %s",
546                 realnm, shape);
547         mptr = mtmp->data; /* reset for mimicker case */
548     } else if (mtmp->isshk) {
549         const char *shknm = shkname(mtmp),
550                    *honorific = shkname_is_pname(mtmp) ? ""
551                                    : mtmp->female ? "Ms. " : "Mr. ";
552 
553         Sprintf(eos(buf), "%s%s, the shopkeeper", honorific, shknm);
554         g.killer.format = KILLED_BY;
555     } else if (mtmp->ispriest || mtmp->isminion) {
556         /* m_monnam() suppresses "the" prefix plus "invisible", and
557            it overrides the effect of Hallucination on priestname() */
558         Strcat(buf, m_monnam(mtmp));
559     } else {
560         Strcat(buf, pmname(mptr, Mgender(mtmp)));
561         if (has_mgivenname(mtmp)) {
562             Sprintf(eos(buf), " %s %s",
563                     has_ebones(mtmp) ? "of" : "called",
564                     MGIVENNAME(mtmp));
565         }
566     }
567 
568     Strcpy(g.killer.name, buf);
569 
570     /* might need to fix up multi_reason if 'mtmp' caused the reason */
571     if (g.multi_reason
572         && g.multi_reason > g.multireasonbuf
573         && g.multi_reason < g.multireasonbuf + sizeof g.multireasonbuf - 1) {
574         char reasondummy, *p;
575         unsigned reasonmid = 0;
576 
577         /*
578          * multireasonbuf[] contains 'm_id:reason' and multi_reason
579          * points at the text past the colon, so we have something
580          * like "42:paralyzed by a ghoul"; if mtmp->m_id matches 42
581          * then we truncate 'reason' at its first space so that final
582          * death reason becomes "Killed by a ghoul, while paralyzed."
583          * instead of "Killed by a ghoul, while paralyzed by a ghoul."
584          * (3.6.x gave "Killed by a ghoul, while paralyzed by a monster."
585          * which is potenitally misleading when the monster is also
586          * the killer.)
587          *
588          * Note that if the hero is life-saved and then killed again
589          * before the helplessness has cleared, the second death will
590          * report the truncated helplessness reason even if some other
591          * monster peforms the /coup de grace/.
592          */
593         if (sscanf(g.multireasonbuf, "%u:%c", &reasonmid, &reasondummy) == 2
594             && mtmp->m_id == reasonmid) {
595             if ((p = index(g.multireasonbuf, ' ')) != 0)
596                 *p = '\0';
597         }
598     }
599 }
600 
601 DISABLE_WARNING_FORMAT_NONLITERAL /* one compiler warns if the format
602                                      string is the result of a ? x : y */
603 
604 void
done_in_by(struct monst * mtmp,int how)605 done_in_by(struct monst *mtmp, int how)
606 {
607     struct permonst *mptr = mtmp->data;
608 
609     You((how == STONING) ? "turn to stone..." : "die...");
610     mark_synch(); /* flush buffered screen output */
611 
612     format_monkiller(mtmp);
613 
614     /*
615      * Chicken and egg issue:
616      *  Ordinarily Unchanging ought to override something like this,
617      *  but the transformation occurs at death.  With the current code,
618      *  the effectiveness of Unchanging stops first, but a case could
619      *  be made that it should last long enough to prevent undead
620      *  transformation.  (Turning to slime isn't an issue here because
621      *  Unchanging prevents that from happening.)
622      */
623     if (mptr->mlet == S_WRAITH)
624         u.ugrave_arise = PM_WRAITH;
625     else if (mptr->mlet == S_MUMMY && g.urace.mummynum != NON_PM)
626         u.ugrave_arise = g.urace.mummynum;
627     else if (zombie_maker(mtmp) && zombie_form(g.youmonst.data) != NON_PM)
628         u.ugrave_arise = zombie_form(g.youmonst.data);
629     else if (mptr->mlet == S_VAMPIRE && Race_if(PM_HUMAN))
630         u.ugrave_arise = PM_VAMPIRE;
631     else if (mptr == &mons[PM_GHOUL])
632         u.ugrave_arise = PM_GHOUL;
633     else if (mptr->mlet == S_ZOMBIE && mptr != &mons[PM_SKELETON])
634         u.ugrave_arise = g.urace.zombienum;
635     /* this could happen if a high-end vampire kills the hero
636        when ordinary vampires are genocided; ditto for wraiths */
637     if (u.ugrave_arise >= LOW_PM
638         && (g.mvitals[u.ugrave_arise].mvflags & G_GENOD))
639         u.ugrave_arise = NON_PM;
640 
641     done(how);
642     return;
643 }
644 
645 RESTORE_WARNING_FORMAT_NONLITERAL
646 
647 /* some special cases for overriding while-helpless reason */
648 static const struct {
649     int why, unmulti;
650     const char *exclude, *include;
651 } death_fixups[] = {
652     /* "petrified by <foo>, while getting stoned" -- "while getting stoned"
653        prevented any last-second recovery, but it was not the cause of
654        "petrified by <foo>" */
655     { STONING, 1, "getting stoned", (char *) 0 },
656     /* "died of starvation, while fainted from lack of food" is accurate
657        but sounds a fairly silly (and doesn't actually appear unless you
658        splice together death and while-helpless from xlogfile) */
659     { STARVING, 0, "fainted from lack of food", "fainted" },
660 };
661 
662 /* clear away while-helpless when the cause of death caused that
663    helplessness (ie, "petrified by <foo> while getting stoned") */
664 static void
fixup_death(int how)665 fixup_death(int how)
666 {
667     int i;
668 
669     if (g.multi_reason) {
670         for (i = 0; i < SIZE(death_fixups); ++i)
671             if (death_fixups[i].why == how
672                 && !strcmp(death_fixups[i].exclude, g.multi_reason)) {
673                 if (death_fixups[i].include) /* substitute alternate reason */
674                     g.multi_reason = death_fixups[i].include;
675                 else /* remove the helplessness reason */
676                     g.multi_reason = (char *) 0;
677                 g.multireasonbuf[0] = '\0'; /* dynamic buf stale either way */
678                 if (death_fixups[i].unmulti) /* possibly hide helplessness */
679                     g.multi = 0L;
680                 break;
681             }
682     }
683 }
684 
685 #if defined(WIN32) && !defined(SYSCF)
686 #define NOTIFY_NETHACK_BUGS
687 #endif
688 
689 DISABLE_WARNING_FORMAT_NONLITERAL
690 
691 /*VARARGS1*/
692 void
VA_DECL(const char *,str)693 panic VA_DECL(const char *, str)
694 {
695     VA_START(str);
696     VA_INIT(str, char *);
697 
698     if (g.program_state.panicking++)
699         NH_abort(); /* avoid loops - this should never happen*/
700 
701     if (iflags.window_inited) {
702         raw_print("\r\nOops...");
703         wait_synch(); /* make sure all pending output gets flushed */
704         exit_nhwindows((char *) 0);
705         iflags.window_inited = 0; /* they're gone; force raw_print()ing */
706     }
707 
708     raw_print(g.program_state.gameover
709                   ? "Postgame wrapup disrupted."
710                   : !g.program_state.something_worth_saving
711                         ? "Program initialization has failed."
712                         : "Suddenly, the dungeon collapses.");
713 #ifndef MICRO
714 #ifdef NOTIFY_NETHACK_BUGS
715     if (!wizard)
716         raw_printf("Report the following error to \"%s\" or at \"%s\".",
717                    DEVTEAM_EMAIL, DEVTEAM_URL);
718     else if (g.program_state.something_worth_saving)
719         raw_print("\nError save file being written.\n");
720 #else /* !NOTIFY_NETHACK_BUGS */
721     if (!wizard) {
722         const char *maybe_rebuild = !g.program_state.something_worth_saving
723                                      ? "."
724                                      : "\nand it may be possible to rebuild.";
725 
726         if (sysopt.support)
727             raw_printf("To report this error, %s%s", sysopt.support,
728                        maybe_rebuild);
729         else if (sysopt.fmtd_wizard_list) /* formatted SYSCF WIZARDS */
730             raw_printf("To report this error, contact %s%s",
731                        sysopt.fmtd_wizard_list, maybe_rebuild);
732         else
733             raw_printf("Report error to \"%s\"%s", WIZARD_NAME,
734                        maybe_rebuild);
735     }
736 #endif /* ?NOTIFY_NETHACK_BUGS */
737     /* XXX can we move this above the prints?  Then we'd be able to
738      * suppress "it may be possible to rebuild" based on dosave0()
739      * or say it's NOT possible to rebuild. */
740     if (g.program_state.something_worth_saving && !iflags.debug_fuzzer) {
741         set_error_savefile();
742         if (dosave0()) {
743             /* os/win port specific recover instructions */
744             if (sysopt.recover)
745                 raw_printf("%s", sysopt.recover);
746         }
747     }
748 #endif /* !MICRO */
749     {
750         char buf[BUFSZ];
751 
752 #if !defined(NO_VSNPRINTF)
753         (void) vsnprintf(buf, sizeof buf, str, VA_ARGS);
754 #else
755         Vsprintf(buf, str, VA_ARGS);
756 #endif
757         raw_print(buf);
758         paniclog("panic", buf);
759     }
760 #ifdef WIN32
761     interject(INTERJECT_PANIC);
762 #endif
763 #if defined(UNIX) || defined(VMS) || defined(LATTICE) || defined(WIN32)
764     if (wizard)
765         NH_abort(); /* generate core dump */
766 #endif
767     VA_END();
768     really_done(PANICKED);
769 }
770 
771 RESTORE_WARNING_FORMAT_NONLITERAL
772 
773 static boolean
should_query_disclose_option(int category,char * defquery)774 should_query_disclose_option(int category, char *defquery)
775 {
776     int idx;
777     char disclose, *dop;
778 
779     *defquery = 'n';
780     if ((dop = index(disclosure_options, category)) != 0) {
781         idx = (int) (dop - disclosure_options);
782         if (idx < 0 || idx >= NUM_DISCLOSURE_OPTIONS) {
783             impossible(
784                    "should_query_disclose_option: bad disclosure index %d %c",
785                        idx, category);
786             *defquery = DISCLOSE_PROMPT_DEFAULT_YES;
787             return TRUE;
788         }
789         disclose = flags.end_disclose[idx];
790         if (disclose == DISCLOSE_YES_WITHOUT_PROMPT) {
791             *defquery = 'y';
792             return FALSE;
793         } else if (disclose == DISCLOSE_SPECIAL_WITHOUT_PROMPT) {
794             *defquery = 'a';
795             return FALSE;
796         } else if (disclose == DISCLOSE_NO_WITHOUT_PROMPT) {
797             *defquery = 'n';
798             return FALSE;
799         } else if (disclose == DISCLOSE_PROMPT_DEFAULT_YES) {
800             *defquery = 'y';
801             return TRUE;
802         } else if (disclose == DISCLOSE_PROMPT_DEFAULT_SPECIAL) {
803             *defquery = 'a';
804             return TRUE;
805         } else {
806             *defquery = 'n';
807             return TRUE;
808         }
809     }
810     impossible("should_query_disclose_option: bad category %c", category);
811     return TRUE;
812 }
813 
814 #if defined(DUMPLOG) || defined(DUMPHTML)
815 static void
dump_plines(void)816 dump_plines(void)
817 {
818     int i, j;
819     char buf[BUFSZ], **strp;
820 
821     Strcpy(buf, " "); /* one space for indentation */
822     putstr(0, ATR_HEADING, "Latest messages:");
823     for (i = 0, j = (int) g.saved_pline_index; i < DUMPLOG_MSG_COUNT;
824          ++i, j = (j + 1) % DUMPLOG_MSG_COUNT) {
825         strp = &g.saved_plines[j];
826         if (*strp) {
827             copynchars(&buf[1], *strp, BUFSZ - 1 - 1);
828             putstr(0, 0, buf);
829 #ifdef FREE_ALL_MEMORY
830             free(*strp), *strp = 0;
831 #endif
832         }
833     }
834 }
835 #endif
836 
837 /*ARGSUSED*/
838 static void
dump_everything(int how,time_t when)839 dump_everything(int how,
840                 time_t when) /* date+time at end of game */
841 {
842 #if defined(DUMPLOG) || defined(DUMPHTML)
843     char pbuf[BUFSZ], datetimebuf[24]; /* [24]: room for 64-bit bogus value */
844 
845     dump_redirect(TRUE);
846     if (!iflags.in_dumplog)
847         return;
848 
849     init_symbols(); /* revert to default symbol set */
850 
851     /* one line version ID, which includes build date+time;
852        it's conceivable that the game started with a different
853        build date+time or even with an older nethack version,
854        but we only have access to the one it finished under */
855     putstr(0, ATR_SUBHEAD, getversionstring(pbuf));
856     putstr(NHW_DUMPTXT, 0, "");
857 
858     /* game start and end date+time to disambiguate version date+time */
859     Strcpy(datetimebuf, yyyymmddhhmmss(ubirthday));
860     Sprintf(pbuf, "Game began %4.4s-%2.2s-%2.2s %2.2s:%2.2s:%2.2s",
861             &datetimebuf[0], &datetimebuf[4], &datetimebuf[6],
862             &datetimebuf[8], &datetimebuf[10], &datetimebuf[12]);
863     Strcpy(datetimebuf, yyyymmddhhmmss(when));
864     Sprintf(eos(pbuf), ", ended %4.4s-%2.2s-%2.2s %2.2s:%2.2s:%2.2s.",
865             &datetimebuf[0], &datetimebuf[4], &datetimebuf[6],
866             &datetimebuf[8], &datetimebuf[10], &datetimebuf[12]);
867     putstr(0, ATR_SUBHEAD, pbuf);
868     putstr(NHW_DUMPTXT, 0, "");
869 
870     /* character name and basic role info */
871     Sprintf(pbuf, "%s, %s %s %s %s", g.plname,
872             aligns[1 - u.ualign.type].adj,
873             genders[flags.female].adj,
874             g.urace.adj,
875             (flags.female && g.urole.name.f) ? g.urole.name.f : g.urole.name.m);
876     putstr(0, ATR_SUBHEAD, pbuf);
877     putstr(NHW_DUMPTXT, 0, "");
878 
879     dump_start_screendump();
880     dump_map();
881     /* NHW_MAP -> ASCII dump only */
882     putstr(NHW_DUMPTXT, 0, do_statusline1());
883     putstr(NHW_DUMPTXT, 0, do_statusline2());
884     /* the next two lines are for the HTML status */
885     status_initialize(TRUE);
886     bot();
887 
888     dump_end_screendump();
889     putstr(NHW_DUMPTXT, 0, "");
890 
891     dump_plines();
892     putstr(NHW_DUMPTXT, 0, "");
893     putstr(0, ATR_HEADING, "Inventory:");
894     (void) display_inventory((char *) 0, TRUE);
895     container_contents(g.invent, TRUE, TRUE, FALSE);
896     enlightenment((BASICENLIGHTENMENT | MAGICENLIGHTENMENT),
897                   (how >= PANICKED) ? ENL_GAMEOVERALIVE : ENL_GAMEOVERDEAD);
898     putstr(NHW_DUMPTXT, 0, "");
899     list_vanquished('d', FALSE); /* 'd' => 'y' */
900     putstr(NHW_DUMPTXT, 0, "");
901     list_genocided('d', FALSE); /* 'd' => 'y' */
902     putstr(NHW_DUMPTXT, 0, "");
903     show_conduct((how >= PANICKED) ? 1 : 2);
904     putstr(NHW_DUMPTXT, 0, "");
905     show_overview((how >= PANICKED) ? 1 : 2, how);
906     putstr(NHW_DUMPTXT, 0, "");
907     dump_redirect(FALSE);
908 #else
909     nhUse(how);
910     nhUse(when);
911 #endif
912 }
913 
914 static void
disclose(int how,boolean taken)915 disclose(int how, boolean taken)
916 {
917     char c = '\0', defquery;
918     char qbuf[QBUFSZ];
919     boolean ask = FALSE;
920 
921     if (g.invent && !done_stopprint) {
922         if (taken)
923             Sprintf(qbuf, "Do you want to see what you had when you %s?",
924                     (how == QUIT) ? "quit" : "died");
925         else
926             Strcpy(qbuf, "Do you want your possessions identified?");
927 
928         ask = should_query_disclose_option('i', &defquery);
929         c = ask ? yn_function(qbuf, ynqchars, defquery) : defquery;
930         if (c == 'y') {
931             /* caller has already ID'd everything */
932             (void) display_inventory((char *) 0, FALSE);
933             container_contents(g.invent, TRUE, TRUE, FALSE);
934         }
935         if (c == 'q')
936             done_stopprint++;
937     }
938 
939     if (!done_stopprint) {
940         ask = should_query_disclose_option('a', &defquery);
941         c = ask ? yn_function("Do you want to see your attributes?", ynqchars,
942                               defquery)
943                 : defquery;
944         if (c == 'y')
945             enlightenment((BASICENLIGHTENMENT | MAGICENLIGHTENMENT),
946                           (how >= PANICKED) ? ENL_GAMEOVERALIVE
947                                             : ENL_GAMEOVERDEAD);
948         if (c == 'q')
949             done_stopprint++;
950     }
951 
952     if (!done_stopprint) {
953         ask = should_query_disclose_option('v', &defquery);
954         list_vanquished(defquery, ask);
955     }
956 
957     if (!done_stopprint) {
958         ask = should_query_disclose_option('g', &defquery);
959         list_genocided(defquery, ask);
960     }
961 
962     if (!done_stopprint) {
963         if (should_query_disclose_option('c', &defquery)) {
964             int acnt = count_achievements();
965 
966             Sprintf(qbuf, "Do you want to see your conduct%s?",
967                     /* this was distinguishing between one achievement and
968                        multiple achievements, but "conduct and achievement"
969                        looked strange if multiple conducts got shown (which
970                        is usual for an early game death); we could switch
971                        to plural vs singular for conducts but the less
972                        specific "conduct and achievements" is sufficient */
973                     (acnt > 0) ? " and achievements" : "");
974             c = yn_function(qbuf, ynqchars, defquery);
975         } else {
976             c = defquery;
977         }
978         if (c == 'y')
979             show_conduct((how >= PANICKED) ? 1 : 2);
980         if (c == 'q')
981             done_stopprint++;
982     }
983 
984     if (!done_stopprint) {
985         ask = should_query_disclose_option('o', &defquery);
986         c = ask ? yn_function("Do you want to see the dungeon overview?",
987                               ynqchars, defquery)
988                 : defquery;
989         if (c == 'y')
990             show_overview((how >= PANICKED) ? 1 : 2, how);
991         if (c == 'q')
992             done_stopprint++;
993     }
994 }
995 
996 /* try to get the player back in a viable state after being killed */
997 static void
savelife(int how)998 savelife(int how)
999 {
1000     int uhpmin = max(2 * u.ulevel, 10);
1001 
1002     if (u.uhpmax < uhpmin)
1003         u.uhpmax = uhpmin;
1004     u.uhp = min(u.uhpmax, 100);
1005 
1006     if (Upolyd) /* Unchanging, or death which bypasses losing hit points */
1007         u.mh = min(u.mhmax, 100);
1008 
1009     if (u.uhunger < 500 || how == CHOKING) {
1010         init_uhunger();
1011     }
1012     /* cure impending doom of sickness hero won't have time to fix */
1013     if ((Sick & TIMEOUT) == 1L) {
1014         make_sick(0L, (char *) 0, FALSE, SICK_ALL);
1015     }
1016     g.nomovemsg = "You survived that attempt on your life.";
1017     g.context.move = 0;
1018     if (g.multi > 0)
1019         g.multi = 0;
1020     else
1021         g.multi = -1;
1022     if (u.utrap && u.utraptype == TT_LAVA)
1023         reset_utrap(FALSE);
1024     g.context.botl = 1;
1025     u.ugrave_arise = NON_PM;
1026     curs_on_u();
1027     if (!g.context.mon_moving)
1028         endmultishot(FALSE);
1029     if (u.uswallow) {
1030         /* might drop hero onto a trap that kills her all over again */
1031         expels(u.ustuck, u.ustuck->data, TRUE);
1032     } else if (u.ustuck) {
1033         if (Upolyd && sticks(g.youmonst.data))
1034             You("release %s.", mon_nam(u.ustuck));
1035         else
1036             pline("%s releases you.", Monnam(u.ustuck));
1037         unstuck(u.ustuck);
1038     }
1039 }
1040 
1041 /*
1042  * Get valuables from the given list.  Revised code: the list always remains
1043  * intact.
1044  */
1045 static void
get_valuables(struct obj * list)1046 get_valuables(struct obj *list) /* inventory or container contents */
1047 {
1048     register struct obj *obj;
1049     register int i;
1050 
1051     /* find amulets and gems, ignoring all artifacts */
1052     for (obj = list; obj; obj = obj->nobj)
1053         if (Has_contents(obj)) {
1054             get_valuables(obj->cobj);
1055         } else if (obj->oartifact) {
1056             continue;
1057         } else if (obj->oclass == AMULET_CLASS) {
1058             i = obj->otyp - FIRST_AMULET;
1059             if (!g.amulets[i].count) {
1060                 g.amulets[i].count = obj->quan;
1061                 g.amulets[i].typ = obj->otyp;
1062             } else
1063                 g.amulets[i].count += obj->quan; /* always adds one */
1064         } else if (obj->oclass == GEM_CLASS && obj->otyp < LUCKSTONE) {
1065             i = min(obj->otyp, LAST_GEM + 1) - FIRST_GEM;
1066             if (!g.gems[i].count) {
1067                 g.gems[i].count = obj->quan;
1068                 g.gems[i].typ = obj->otyp;
1069             } else
1070                 g.gems[i].count += obj->quan;
1071         }
1072     return;
1073 }
1074 
1075 /*
1076  *  Sort collected valuables, most frequent to least.  We could just
1077  *  as easily use qsort, but we don't care about efficiency here.
1078  */
1079 static void
sort_valuables(struct valuable_data list[],int size)1080 sort_valuables(struct valuable_data list[],
1081                int size) /* max value is less than 20 */
1082 {
1083     register int i, j;
1084     struct valuable_data ltmp;
1085 
1086     /* move greater quantities to the front of the list */
1087     for (i = 1; i < size; i++) {
1088         if (list[i].count == 0)
1089             continue;   /* empty slot */
1090         ltmp = list[i]; /* structure copy */
1091         for (j = i; j > 0; --j)
1092             if (list[j - 1].count >= ltmp.count)
1093                 break;
1094             else {
1095                 list[j] = list[j - 1];
1096             }
1097         list[j] = ltmp;
1098     }
1099     return;
1100 }
1101 
1102 #if 0
1103 /*
1104  * odds_and_ends() was used for 3.6.0 and 3.6.1.
1105  * Schroedinger's Cat is handled differently as of 3.6.2.
1106  */
1107 static boolean odds_and_ends(struct obj *, int);
1108 
1109 #define CAT_CHECK 2
1110 
1111 static boolean
1112 odds_and_ends(struct obj *list, int what)
1113 {
1114     struct obj *otmp;
1115 
1116     for (otmp = list; otmp; otmp = otmp->nobj) {
1117         switch (what) {
1118         case CAT_CHECK: /* Schroedinger's Cat */
1119             /* Ascending is deterministic */
1120             if (SchroedingersBox(otmp))
1121                 return rn2(2);
1122             break;
1123         }
1124         if (Has_contents(otmp))
1125             return odds_and_ends(otmp->cobj, what);
1126     }
1127     return FALSE;
1128 }
1129 #endif
1130 
1131 /* deal with some objects which may be in an abnormal state at end of game */
1132 static void
done_object_cleanup(void)1133 done_object_cleanup(void)
1134 {
1135     int ox, oy;
1136 
1137     /* might have been killed while using a disposable item, so make sure
1138        it's gone prior to inventory disclosure and creation of bones */
1139     inven_inuse(TRUE);
1140     /*
1141      * Hero can die when throwing an object (by hitting an adjacent
1142      * gas spore, for instance, or being hit by mis-returning Mjollnir),
1143      * or while in transit (from falling down stairs).  If that happens,
1144      * some object(s) might be in limbo rather than on the map or in
1145      * any inventory.  Saving bones with an active light source in limbo
1146      * would trigger an 'object not local' panic.
1147      *
1148      * We used to use dealloc_obj() on g.thrownobj and g.kickedobj but
1149      * that keeps them out of bones and could leave uball in a confused
1150      * state (gone but still attached).  Place them on the map but
1151      * bypass flooreffects().  That could lead to minor anomalies in
1152      * bones, like undamaged paper at water or lava locations or piles
1153      * not being knocked down holes, but it seems better to get this
1154      * game over with than risk being tangled up in more and more details.
1155      */
1156     ox = u.ux + u.dx, oy = u.uy + u.dy;
1157     if (!isok(ox, oy) || !accessible(ox, oy))
1158         ox = u.ux, oy = u.uy;
1159     /* put thrown or kicked object on map (for bones); location might
1160        be incorrect (perhaps killed by divine lightning when throwing at
1161        a temple priest?) but this should be better than just vanishing
1162        (fragile stuff should be taken care of before getting here) */
1163     if (g.thrownobj && g.thrownobj->where == OBJ_FREE) {
1164         place_object(g.thrownobj, ox, oy);
1165         stackobj(g.thrownobj), g.thrownobj = 0;
1166     }
1167     if (g.kickedobj && g.kickedobj->where == OBJ_FREE) {
1168         place_object(g.kickedobj, ox, oy);
1169         stackobj(g.kickedobj), g.kickedobj = 0;
1170     }
1171     /* if Punished hero dies during level change or dies or quits while
1172        swallowed, uball and uchain will be in limbo; put them on floor
1173        so bones will have them and object list cleanup finds them */
1174     if (uchain && uchain->where == OBJ_FREE) {
1175         /* placebc(); */
1176         lift_covet_and_placebc(override_restriction);
1177     }
1178     /* persistent inventory window now obsolete since disclosure uses
1179        a normal popup one; avoids "Bad fruit #n" when saving bones */
1180     if (iflags.perm_invent) {
1181         iflags.perm_invent = FALSE;
1182         update_inventory(); /* make interface notice the change */
1183     }
1184     return;
1185 }
1186 
1187 /* called twice; first to calculate total, then to list relevant items */
1188 static void
artifact_score(struct obj * list,boolean counting,winid endwin)1189 artifact_score(struct obj *list,
1190                boolean counting, /* true => add up points;
1191                                     false => display them */
1192                winid endwin)
1193 {
1194     char pbuf[BUFSZ];
1195     struct obj *otmp;
1196     long value, points;
1197     short dummy; /* object type returned by artifact_name() */
1198 
1199     for (otmp = list; otmp; otmp = otmp->nobj) {
1200         if (otmp->oartifact || otmp->otyp == BELL_OF_OPENING
1201             || otmp->otyp == SPE_BOOK_OF_THE_DEAD
1202             || otmp->otyp == CANDELABRUM_OF_INVOCATION) {
1203             value = arti_cost(otmp); /* zorkmid value */
1204             points = value * 5 / 2;  /* score value */
1205             if (counting) {
1206                 nowrap_add(u.urexp, points);
1207             } else {
1208                 discover_object(otmp->otyp, TRUE, FALSE);
1209                 otmp->known = otmp->dknown = otmp->bknown = otmp->rknown = 1;
1210                 /* assumes artifacts don't have quan > 1 */
1211                 Sprintf(pbuf, "%s%s (worth %ld %s and %ld points)",
1212                         the_unique_obj(otmp) ? "The " : "",
1213                         otmp->oartifact ? artifact_name(xname(otmp), &dummy)
1214                                         : OBJ_NAME(objects[otmp->otyp]),
1215                         value, currency(value), points);
1216                 putstr(endwin, 0, pbuf);
1217             }
1218         }
1219         if (Has_contents(otmp))
1220             artifact_score(otmp->cobj, counting, endwin);
1221     }
1222 }
1223 
1224 /* Be careful not to call panic from here! */
1225 void
done(int how)1226 done(int how)
1227 {
1228     boolean survive = FALSE;
1229 
1230     if (how == TRICKED) {
1231         if (g.killer.name[0]) {
1232             paniclog("trickery", g.killer.name);
1233             g.killer.name[0] = '\0';
1234         }
1235         if (wizard) {
1236             You("are a very tricky wizard, it seems.");
1237             g.killer.format = KILLED_BY_AN; /* reset to 0 */
1238             return;
1239         }
1240     }
1241     if (g.program_state.panicking
1242 #ifdef HANGUPHANDLING
1243         || g.program_state.done_hup
1244 #endif
1245         || (how == QUIT && done_stopprint)) {
1246         /* skip status update if panicking or disconnected
1247            or answer of 'q' to "Really quit?" */
1248         g.context.botl = g.context.botlx = iflags.time_botl = FALSE;
1249     } else {
1250         /* otherwise force full status update */
1251         g.context.botlx = TRUE;
1252         bot();
1253     }
1254 
1255     if (iflags.debug_fuzzer) {
1256         if (!(g.program_state.panicking || how == PANICKED)) {
1257             savelife(how);
1258             /* periodically restore characteristics and lost exp levels
1259                or cure lycanthropy */
1260             if (!rn2(10)) {
1261                 struct obj *potion = mksobj((u.ulycn > LOW_PM && !rn2(3))
1262                                             ? POT_WATER : POT_RESTORE_ABILITY,
1263                                             TRUE, FALSE);
1264 
1265                 bless(potion);
1266                 (void) peffects(potion); /* always -1 for restore ability */
1267                 /* not useup(); we haven't put this potion into inventory */
1268                 obfree(potion, (struct obj *) 0);
1269             }
1270             g.killer.name[0] = '\0';
1271             g.killer.format = 0;
1272             return;
1273         }
1274     } else
1275     if (how == ASCENDED || (!g.killer.name[0] && how == GENOCIDED))
1276         g.killer.format = NO_KILLER_PREFIX;
1277     /* Avoid killed by "a" burning or "a" starvation */
1278     if (!g.killer.name[0] && (how == STARVING || how == BURNING))
1279         g.killer.format = KILLED_BY;
1280     if (!g.killer.name[0] || how >= PANICKED)
1281         Strcpy(g.killer.name, deaths[how]);
1282 
1283     if (how < PANICKED) {
1284         u.umortality++;
1285         /* in case caller hasn't already done this */
1286         if (how != TURNED_SLIME && (u.uhp != 0 || (Upolyd && u.mh > 0))) {
1287             /* for deaths not triggered by loss of hit points, force
1288                current HP to zero */
1289             u.uhp = u.mh = 0;
1290             g.context.botl = 1;
1291         }
1292     }
1293     if (Lifesaved && (how <= GENOCIDED) && !nonliving(g.youmonst.data)) {
1294         pline("But wait...");
1295         makeknown(AMULET_OF_LIFE_SAVING);
1296         Your("medallion %s!", !Blind ? "begins to glow" : "feels warm");
1297         /* It's cursed? Well, that's just too bad. */
1298         if (faulty_lifesaver(uamul)) {
1299             pline("But... the chain on your medallion breaks and it falls to the %s!",
1300                   surface(u.ux, u.uy));
1301             You_hear(Hallucination ? "someone playing Yakety Sax!"
1302                                    : "the sound of a distant trombone...");
1303             useup(uamul);
1304         }
1305         else {
1306             if (how == CHOKING)
1307                 You("vomit ...");
1308             You_feel("much better!");
1309             pline_The("medallion crumbles to dust!");
1310             if (uamul)
1311                 useup(uamul);
1312 
1313             /* Getting lifesaved isn't great for your overall health.
1314              * Current and maximum Con go down by 2 (e.g. if max Con was
1315              * formerly 16, you'll need exercise/gain ability to restore them,
1316              * not just restore ability.
1317              * A possible extension here is to reduce ATTRMAX by 1, making your
1318              * Con cap permanently lower no matter what the player does, but
1319              * this currently isn't saved. */
1320             (void) adjattrib(A_CON, -2, AA_NOMSG);
1321             AMAX(A_CON) = (AMAX(A_CON) >= 5 ? AMAX(A_CON) - 2 : 3);
1322 
1323             savelife(how);
1324             if (how == GENOCIDED) {
1325                 pline("Unfortunately you are still genocided...");
1326             } else {
1327                 char killbuf[BUFSZ];
1328                 formatkiller(killbuf, BUFSZ, how, FALSE);
1329                 livelog_printf(LL_LIFESAVE, "averted death (%s)", killbuf);
1330                 survive = TRUE;
1331             }
1332         }
1333     }
1334     /* maybe give the player a second chance if they were killed by the
1335      * appropriate monster */
1336     if (!survive && sentient_arise(how)) {
1337         survive = TRUE;
1338     }
1339 
1340     /* explore and wizard modes offer player the option to keep playing */
1341     if (!survive && (wizard || discover) && how <= GENOCIDED
1342         && !paranoid_query(ParanoidDie, "Die?")) {
1343         pline("OK, so you don't %s.", (how == CHOKING) ? "choke" : "die");
1344         iflags.last_msg = PLNMSG_OK_DONT_DIE;
1345         savelife(how);
1346         survive = TRUE;
1347     }
1348 
1349     if (survive) {
1350         g.killer.name[0] = '\0';
1351         g.killer.format = KILLED_BY_AN; /* reset to 0 */
1352         return;
1353     }
1354 
1355     really_done(how);
1356     /*NOTREACHED*/
1357 }
1358 
1359 /* separated from done() in order to specify the __noreturn__ attribute */
1360 static void
really_done(int how)1361 really_done(int how)
1362 {
1363     boolean taken;
1364     char pbuf[BUFSZ];
1365     winid endwin = WIN_ERR;
1366     boolean bones_ok, have_windows = iflags.window_inited;
1367     struct obj *corpse = (struct obj *) 0;
1368     time_t endtime;
1369     long umoney;
1370     long tmp;
1371 
1372     /*
1373      *  The game is now over...
1374      */
1375     g.program_state.gameover = 1;
1376     /* in case of a subsequent panic(), there's no point trying to save */
1377     g.program_state.something_worth_saving = 0;
1378 #ifdef HANGUPHANDLING
1379     if (g.program_state.done_hup)
1380         done_stopprint++;
1381 #endif
1382     /* render vision subsystem inoperative */
1383     iflags.vision_inited = 0;
1384 
1385     /* maybe use up active invent item(s), place thrown/kicked missile,
1386        deal with ball and chain possibly being temporarily off the map */
1387     if (!g.program_state.panicking)
1388         done_object_cleanup();
1389     /* in case we're panicking; normally cleared by done_object_cleanup() */
1390     iflags.perm_invent = FALSE;
1391 
1392     /* remember time of death here instead of having bones, rip, and
1393        topten figure it out separately and possibly getting different
1394        time or even day if player is slow responding to --More-- */
1395     urealtime.finish_time = endtime = getnow();
1396     urealtime.realtime += (long) (endtime - urealtime.start_timing);
1397     /* collect these for end of game disclosure (not used during play) */
1398     iflags.at_night = night();
1399     iflags.at_midnight = midnight();
1400 
1401     /* final achievement tracking; only show blind and nudist if some
1402        tangible progress has been made; always show ascension last */
1403     if (u.uachieved[0] || !flags.beginner) {
1404         if (u.uroleplay.blind)
1405             record_achievement(ACH_BLND); /* blind the whole game */
1406         if (u.uroleplay.nudist)
1407             record_achievement(ACH_NUDE); /* never wore armor */
1408     }
1409     if (how == ASCENDED)
1410         record_achievement(ACH_UWIN);
1411 
1412     dump_open_log(endtime);
1413     /* Sometimes you die on the first move.  Life's not fair.
1414      * On those rare occasions you get hosed immediately, go out
1415      * smiling... :-)  -3.
1416      */
1417     if (g.moves <= 1 && how < PANICKED && !done_stopprint)
1418         pline("Do not pass Go.  Do not collect 200 %s.", currency(200L));
1419 
1420     if (have_windows)
1421         wait_synch(); /* flush screen output */
1422 #ifndef NO_SIGNAL
1423     (void) signal(SIGINT, (SIG_RET_TYPE) done_intr);
1424 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
1425     (void) signal(SIGQUIT, (SIG_RET_TYPE) done_intr);
1426     sethanguphandler(done_hangup);
1427 #endif
1428 #endif /* NO_SIGNAL */
1429 
1430     bones_ok = (how < GENOCIDED) && can_make_bones();
1431 
1432     if (bones_ok && launch_in_progress())
1433         force_launch_placement();
1434 
1435     /* maintain ugrave_arise even for !bones_ok */
1436     if (how == PANICKED)
1437         u.ugrave_arise = (NON_PM - 3); /* no corpse, no grave */
1438     else if (how == BURNING || how == DISSOLVED) /* corpse burns up too */
1439         u.ugrave_arise = (NON_PM - 2); /* leave no corpse */
1440     else if (how == STONING)
1441         u.ugrave_arise = (NON_PM - 1); /* statue instead of corpse */
1442     else if (how == TURNED_SLIME
1443              /* it's possible to turn into slime even though green slimes
1444                 have been genocided:  genocide could occur after hero is
1445                 already infected or hero could eat a glob of one created
1446                 before genocide; don't try to arise as one if they're gone */
1447              && !(g.mvitals[PM_GREEN_SLIME].mvflags & G_GENOD)) {
1448         if (Hallucination)
1449             pline("Next time, watch out for slime!");
1450         u.ugrave_arise = PM_GREEN_SLIME;
1451     }
1452 
1453     if (how == QUIT) {
1454         g.killer.format = NO_KILLER_PREFIX;
1455         if (u.uhp < 1) {
1456             how = DIED;
1457             u.umortality++; /* skipped above when how==QUIT */
1458             Strcpy(g.killer.name, "quit while already on Charon's boat");
1459         }
1460     }
1461     if (how == ESCAPED || how == PANICKED)
1462         g.killer.format = NO_KILLER_PREFIX;
1463 
1464     fixup_death(how); /* actually, fixup g.multi_reason */
1465 
1466     if (how != PANICKED) {
1467         boolean silently = done_stopprint ? TRUE : FALSE;
1468 
1469         /* these affect score and/or bones, but avoid them during panic */
1470         taken = paybill((how == ESCAPED) ? -1 : (how != QUIT), silently);
1471         paygd(silently);
1472         clearpriests();
1473     } else
1474         taken = FALSE; /* lint; assert( !bones_ok ); */
1475 
1476     clearlocks();
1477 
1478     if (have_windows)
1479         display_nhwindow(WIN_MESSAGE, FALSE);
1480 
1481     if (how != PANICKED) {
1482         struct obj *obj;
1483 
1484         /*
1485          * This is needed for both inventory disclosure and dumplog.
1486          * Both are optional, so do it once here instead of duplicating
1487          * it in both of those places.
1488          */
1489         for (obj = g.invent; obj; obj = obj->nobj) {
1490             discover_object(obj->otyp, TRUE, FALSE);
1491             obj->known = obj->bknown = obj->dknown = obj->rknown = 1;
1492             set_cknown_lknown(obj); /* set flags when applicable */
1493             /* we resolve Schroedinger's cat now in case of both
1494                disclosure and dumplog, where the 50:50 chance for
1495                live cat has to be the same both times */
1496             if (SchroedingersBox(obj)) {
1497                 if (!Schroedingers_cat) {
1498                     /* tell observe_quantum_cat() not to create a cat; if it
1499                        chooses live cat in this situation, it will leave the
1500                        SchroedingersBox flag set (for container_contents()) */
1501                     observe_quantum_cat(obj, FALSE, FALSE);
1502                     if (SchroedingersBox(obj))
1503                         Schroedingers_cat = TRUE;
1504                 } else
1505                     obj->spe = 0; /* ordinary box with cat corpse in it */
1506             }
1507         }
1508 
1509         if (strcmp(flags.end_disclose, "none"))
1510             disclose(how, taken);
1511 
1512         dump_everything(how, endtime);
1513     }
1514 
1515     /* if pets will contribute to score, populate g.mydogs list now
1516        (bones creation isn't a factor, but pline() messaging is; used to
1517        be done even sooner, but we need it to come after dump_everything()
1518        so that any accompanying pets are still on the map during dump) */
1519     if (how == ESCAPED || how == ASCENDED)
1520         keepdogs(TRUE, FALSE);
1521 
1522     /* finish_paybill should be called after disclosure but before bones */
1523     if (bones_ok && taken)
1524         finish_paybill();
1525 
1526     /* grave creation should be after disclosure so it doesn't have
1527        this grave in the current level's features for #overview */
1528     if (bones_ok && u.ugrave_arise == NON_PM
1529         && !(g.mvitals[u.umonnum].mvflags & G_NOCORPSE)) {
1530         int mnum = u.umonnum;
1531 
1532         if (!Upolyd) {
1533             /* Base corpse on race when not poly'd since original u.umonnum
1534                is based on role, and all role monsters are human. */
1535             mnum = (flags.female && g.urace.femalenum != NON_PM)
1536                        ? g.urace.femalenum
1537                        : g.urace.malenum;
1538         }
1539         if (!Withering) {
1540             /* withering suppresses the actual corpse from being created but
1541              * still allows creation of a grave (and it doesn't matter whether
1542              * or not withering was the actual cause of death; dying while
1543              * withering is assumed to just wither the rest of the body without
1544              * any vitality to stop it) */
1545             corpse = mk_named_object(CORPSE, &mons[mnum], u.ux, u.uy, g.plname);
1546         }
1547         if (yn("Do you want to write your own epitaph?") != 'y') {
1548             Sprintf(pbuf, "%s, ", g.plname);
1549             formatkiller(eos(pbuf), sizeof pbuf - strlen(pbuf), how, TRUE);
1550         }
1551         else {
1552             char ebuf[101]; /* arbitrary, but should be enough */
1553             getlin("Enter your epitaph (100 characters max):", ebuf);
1554             ebuf[100] = '\0';
1555             Sprintf(pbuf, "%s. %s", g.plname, ebuf);
1556         }
1557         make_grave(u.ux, u.uy, pbuf);
1558     }
1559     pbuf[0] = '\0'; /* clear grave text; also lint suppression */
1560 
1561     /* calculate score, before creating bones [container gold] */
1562     {
1563         int deepest = deepest_lev_reached(FALSE);
1564 
1565         umoney = money_cnt(g.invent);
1566         tmp = u.umoney0;
1567         umoney += hidden_gold(TRUE); /* accumulate gold from containers */
1568         tmp = umoney - tmp;          /* net gain */
1569 
1570         /* There's not really any particular reason that a dungeon that was 52
1571          * levels deep should be worth more than a dungeon that was 45 levels
1572          * deep. The only people who realistically care about score at these
1573          * stages of the game are those who are trying to get the minimum score,
1574          * which is only possible when the sanctum is on level 45, and it really
1575          * annoys them that an unknowable variable decided at the start of the
1576          * game controls whether they can actually get it or not.
1577          */
1578         if (deepest > 45)
1579             deepest = 45;
1580 
1581         if (tmp < 0L)
1582             tmp = 0L;
1583         if (how < PANICKED)
1584             tmp -= tmp / 10L;
1585         tmp += 50L * (long) (deepest - 1);
1586         if (deepest > 20)
1587             tmp += 1000L * (long) ((deepest > 30) ? 10 : deepest - 20);
1588         nowrap_add(u.urexp, tmp);
1589 
1590         /* ascension gives a score bonus iff offering to original deity */
1591         if (how == ASCENDED) {
1592             if (u.ualign.type == u.ualignbase[A_ORIGINAL]) {
1593                 /* retaining original alignment: score *= 2;
1594                    converting, then using helm-of-OA to switch back: *= 1.5 */
1595                 tmp = (u.ualignbase[A_CURRENT] == u.ualignbase[A_ORIGINAL])
1596                         ? u.urexp
1597                         : (u.urexp / 2L);
1598                 nowrap_add(u.urexp, tmp);
1599             }
1600             else {
1601                 Strcat(g.killer.name, " (in dishonor)");
1602             }
1603         }
1604     }
1605 
1606     if (u.ugrave_arise >= LOW_PM && !done_stopprint) {
1607         /* give this feedback even if bones aren't going to be created,
1608            so that its presence or absence doesn't tip off the player to
1609            new bones or their lack; it might be a lie if makemon fails */
1610         Your("%s as %s...",
1611              (u.ugrave_arise != PM_GREEN_SLIME)
1612                  ? "body rises from the dead"
1613                  : "revenant persists",
1614              an(pmname(&mons[u.ugrave_arise], Ugender)));
1615         display_nhwindow(WIN_MESSAGE, FALSE);
1616     }
1617 
1618     if (bones_ok) {
1619         /* Wizard mode always has the option of leaving bones.
1620          * Non-wizards may or may not, with increasing chance as depth
1621          * increases.
1622          * This logic used to be in can_save_bones, but was moved here. */
1623         if ((wizard && paranoid_query(ParanoidBones, "Save bones?"))
1624             || (depth(&u.uz) > 0 /* bulletproofing for endgame */
1625                 && rn2(1 + (depth(&u.uz) / 4)))) {
1626             savebones(how, endtime, corpse);
1627         }
1628         /* corpse may be invalid pointer now so
1629             ensure that it isn't used again */
1630         corpse = (struct obj *) 0;
1631     }
1632 
1633     /* update gold for the rip output, which can't use hidden_gold()
1634        (containers will be gone by then if bones just got saved...) */
1635     g.done_money = umoney;
1636 
1637     /* clean up unneeded windows */
1638     if (have_windows) {
1639         wait_synch();
1640         free_pickinv_cache(); /* extra persistent window if perm_invent */
1641         if (WIN_INVEN != WIN_ERR) {
1642             destroy_nhwindow(WIN_INVEN),  WIN_INVEN = WIN_ERR;
1643             /* precaution in case any late update_inventory() calls occur */
1644             iflags.perm_invent = 0;
1645         }
1646         display_nhwindow(WIN_MESSAGE, TRUE);
1647         destroy_nhwindow(WIN_MAP),  WIN_MAP = WIN_ERR;
1648         if (WIN_STATUS != WIN_ERR)
1649             destroy_nhwindow(WIN_STATUS),  WIN_STATUS = WIN_ERR;
1650         destroy_nhwindow(WIN_MESSAGE),  WIN_MESSAGE = WIN_ERR;
1651 
1652         if (!done_stopprint || flags.tombstone)
1653             endwin = create_nhwindow(NHW_TEXT);
1654 
1655         if (how < GENOCIDED && flags.tombstone && endwin != WIN_ERR)
1656             outrip(endwin, how, endtime);
1657     } else
1658         done_stopprint = 1; /* just avoid any more output */
1659 
1660 #if defined(DUMPLOG) || defined(DUMPHTML)
1661     /* 'how' reasons beyond genocide shouldn't show tombstone;
1662        for normal end of game, genocide doesn't either */
1663     if (how <= GENOCIDED) {
1664         dump_redirect(TRUE);
1665         if (iflags.in_dumplog)
1666             outrip(0, how, endtime);
1667         dump_redirect(FALSE);
1668     }
1669 #endif
1670     if (u.uhave.amulet) {
1671         Strcat(g.killer.name, " (with the Amulet)");
1672     } else if (how == ESCAPED) {
1673         if (Is_astralevel(&u.uz)) /* offered Amulet to wrong deity */
1674             Strcat(g.killer.name, " (in celestial disgrace)");
1675         else if (carrying(FAKE_AMULET_OF_YENDOR))
1676             Strcat(g.killer.name, " (with a fake Amulet)");
1677         /* don't bother counting to see whether it should be plural */
1678     }
1679 
1680     Sprintf(pbuf, "%s %s the %s...", Goodbye(), g.plname,
1681             (how != ASCENDED)
1682                 ? (const char *) ((flags.female && g.urole.name.f)
1683                     ? g.urole.name.f
1684                     : g.urole.name.m)
1685                 : (const char *) (flags.female ? "Demigoddess" : "Demigod"));
1686 
1687 #if defined(DUMPLOG) || defined(DUMPHTML)
1688     dump_redirect(TRUE);
1689     if (iflags.in_dumplog)
1690         /* dump attributes don't work unless dump_redirect is on */
1691         putstr(endwin, ATR_SUBHEAD, pbuf);
1692     dump_redirect(FALSE);
1693 #endif
1694 
1695     if (!done_stopprint)
1696         putstr(endwin, 0, pbuf);
1697     dump_forward_putstr(endwin, 0, "", done_stopprint);
1698 
1699     if (how == ESCAPED || how == ASCENDED) {
1700         struct monst *mtmp;
1701         struct obj *otmp;
1702         register struct val_list *val;
1703         register int i;
1704 
1705         for (val = g.valuables; val->list; val++)
1706             for (i = 0; i < val->size; i++) {
1707                 val->list[i].count = 0L;
1708             }
1709         get_valuables(g.invent);
1710 
1711         /* add points for collected valuables */
1712         for (val = g.valuables; val->list; val++)
1713             for (i = 0; i < val->size; i++)
1714                 if (val->list[i].count != 0L) {
1715                     tmp = val->list[i].count
1716                           * (long) objects[val->list[i].typ].oc_cost;
1717                     nowrap_add(u.urexp, tmp);
1718                 }
1719 
1720         /* count the points for artifacts */
1721         artifact_score(g.invent, TRUE, endwin);
1722 
1723         g.viz_array[0][0] |= IN_SIGHT; /* need visibility for naming */
1724         mtmp = g.mydogs;
1725         Strcpy(pbuf, "You");
1726         if (mtmp || Schroedingers_cat) {
1727             while (mtmp) {
1728                 Sprintf(eos(pbuf), " and %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                g.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                 Strcat(eos(pbuf), " and Schroedinger's cat");
1741             }
1742             dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
1743             pbuf[0] = '\0';
1744         } else {
1745             Strcat(pbuf, " ");
1746         }
1747         Sprintf(eos(pbuf), "%s with %ld point%s,",
1748                 how == ASCENDED ? "went to your reward"
1749                                  : "escaped from the dungeon",
1750                 u.urexp, plur(u.urexp));
1751         dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
1752 
1753         if (!done_stopprint)
1754             artifact_score(g.invent, FALSE, endwin); /* list artifacts */
1755 #if defined(DUMPLOG) || defined(DUMPHTML)
1756         dump_redirect(TRUE);
1757         if (iflags.in_dumplog)
1758             artifact_score(g.invent, FALSE, 0);
1759         dump_redirect(FALSE);
1760 #endif
1761 
1762         /* list valuables here */
1763         for (val = g.valuables; val->list; val++) {
1764             sort_valuables(val->list, val->size);
1765             for (i = 0; i < val->size && !done_stopprint; i++) {
1766                 int typ = val->list[i].typ;
1767                 long count = val->list[i].count;
1768 
1769                 if (count == 0L)
1770                     continue;
1771                 if (objects[typ].oc_class != GEM_CLASS || typ <= LAST_GEM) {
1772                     otmp = mksobj(typ, FALSE, FALSE);
1773                     discover_object(otmp->otyp, TRUE, FALSE);
1774                     otmp->known = 1;  /* for fake amulets */
1775                     otmp->dknown = 1; /* seen it (blindness fix) */
1776                     if (has_oname(otmp))
1777                         free_oname(otmp);
1778                     otmp->quan = count;
1779                     /* don't show material */
1780                     otmp->material = objects[otmp->otyp].oc_material;
1781                     Sprintf(pbuf, "%8ld %s (worth %ld %s),", count,
1782                             xname(otmp), count * (long) objects[typ].oc_cost,
1783                             currency(2L));
1784                     obfree(otmp, (struct obj *) 0);
1785                 } else {
1786                     Sprintf(pbuf, "%8ld worthless piece%s of colored glass,",
1787                             count, plur(count));
1788                 }
1789                 dump_forward_putstr(endwin, 0, pbuf, 0);
1790             }
1791         }
1792 
1793     } else {
1794         /* did not escape or ascend */
1795         if (u.uz.dnum == 0 && u.uz.dlevel <= 0) {
1796             /* level teleported out of the dungeon; `how' is DIED,
1797                due to falling or to "arriving at heaven prematurely" */
1798             Sprintf(pbuf, "You %s beyond the confines of the dungeon",
1799                     (u.uz.dlevel < 0) ? "passed away" : ends[how]);
1800         } else {
1801             /* more conventional demise */
1802             const char *where = g.dungeons[u.uz.dnum].dname;
1803 
1804             if (Is_astralevel(&u.uz))
1805                 where = "The Astral Plane";
1806             Sprintf(pbuf, "You %s in %s", ends[how], where);
1807             if (!In_endgame(&u.uz) && !Is_knox(&u.uz))
1808                 Sprintf(eos(pbuf), " on dungeon level %d",
1809                         In_quest(&u.uz) ? dunlev(&u.uz) : depth(&u.uz));
1810         }
1811 
1812         Sprintf(eos(pbuf), " with %ld point%s,", u.urexp, plur(u.urexp));
1813         dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
1814     }
1815 
1816     Sprintf(pbuf, "and %ld piece%s of gold, after %ld move%s.", umoney,
1817             plur(umoney), g.moves, plur(g.moves));
1818     dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
1819     Sprintf(pbuf,
1820             "You were level %d with a maximum of %d hit point%s when you %s.",
1821             u.ulevel, u.uhpmax, plur(u.uhpmax), ends[how]);
1822     dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
1823     dump_forward_putstr(endwin, 0, "", done_stopprint);
1824     if (!done_stopprint)
1825         display_nhwindow(endwin, TRUE);
1826     if (endwin != WIN_ERR)
1827         destroy_nhwindow(endwin);
1828 
1829     dump_close_log();
1830     /* "So when I die, the first thing I will see in Heaven is a
1831      * score list?" */
1832     if (have_windows && !iflags.toptenwin)
1833         exit_nhwindows((char *) 0), have_windows = FALSE;
1834     /* update 'logfile' and 'xlogfile', if enabled, and maybe 'record' */
1835     topten(how, endtime);
1836     if (have_windows)
1837         exit_nhwindows((char *) 0);
1838 
1839     if (done_stopprint) {
1840         raw_print("");
1841         raw_print("");
1842     }
1843     livelog_dump_url(LL_DUMP_ALL | (how == ASCENDED ? LL_DUMP_ASC : 0));
1844     nh_terminate(EXIT_SUCCESS);
1845 }
1846 
1847 void
container_contents(struct obj * list,boolean identified,boolean all_containers,boolean reportempty)1848 container_contents(struct obj *list, boolean identified,
1849                    boolean all_containers, boolean reportempty)
1850 {
1851     register struct obj *box, *obj;
1852     char buf[BUFSZ];
1853     boolean cat, dumping = iflags.in_dumplog;
1854 
1855     for (box = list; box; box = box->nobj) {
1856         if (Is_container(box) || box->otyp == STATUE) {
1857             if (!box->cknown || (identified && !box->lknown)) {
1858                 box->cknown = 1; /* we're looking at the contents now */
1859                 if (identified)
1860                     box->lknown = 1;
1861                 update_inventory();
1862             }
1863             if (box->otyp == BAG_OF_TRICKS) {
1864                 continue; /* wrong type of container */
1865             } else if (box->cobj) {
1866                 winid tmpwin = create_nhwindow(NHW_MENU);
1867                 Loot *sortedcobj, *srtc;
1868                 unsigned sortflags;
1869 
1870                 /* at this stage, the SchroedingerBox() flag is only set
1871                    if the cat inside the box is alive; the box actually
1872                    contains a cat corpse that we'll pretend is not there;
1873                    for dead cat, the flag will be clear and there'll be
1874                    a cat corpse inside the box; either way, inventory
1875                    reports the box as containing "1 item" */
1876                 cat = SchroedingersBox(box);
1877 
1878                 Sprintf(buf, "Contents of %s:", the(xname(box)));
1879                 putstr(tmpwin, ATR_SUBHEAD, buf);
1880                 if (!dumping)
1881                     putstr(tmpwin, 0, "");
1882                 buf[0] = buf[1] = ' '; /* two leading spaces */
1883                 if (box->cobj && !cat) {
1884                     sortflags = (((flags.sortloot == 'l'
1885                                    || flags.sortloot == 'f')
1886                                      ? SORTLOOT_LOOT : 0)
1887                                  | (flags.sortpack ? SORTLOOT_PACK : 0));
1888                     sortedcobj = sortloot(&box->cobj, sortflags, FALSE,
1889                                           (boolean (*)(OBJ_P)) 0);
1890                     for (srtc = sortedcobj; ((obj = srtc->obj) != 0); ++srtc) {
1891                         if (identified) {
1892                             discover_object(obj->otyp, TRUE, FALSE);
1893                             obj->known = obj->bknown = obj->dknown
1894                                 = obj->rknown = 1;
1895                             if (Is_container(obj) || obj->otyp == STATUE)
1896                                 obj->cknown = obj->lknown = 1;
1897                         }
1898                         Strcpy(&buf[2], doname_with_price(obj));
1899                         putstr(tmpwin, 0, buf);
1900                     }
1901                     unsortloot(&sortedcobj);
1902                 } else if (cat) {
1903                     Strcpy(&buf[2], "Schroedinger's cat!");
1904                     putstr(tmpwin, 0, buf);
1905                 }
1906                 if (dumping)
1907                     putstr(0, 0, "");
1908                 display_nhwindow(tmpwin, TRUE);
1909                 destroy_nhwindow(tmpwin);
1910                 if (all_containers)
1911                     container_contents(box->cobj, identified, TRUE,
1912                                        reportempty);
1913             } else if (reportempty) {
1914                 pline("%s is empty.", upstart(thesimpleoname(box)));
1915                 display_nhwindow(WIN_MESSAGE, FALSE);
1916             }
1917         }
1918         if (!all_containers)
1919             break;
1920     }
1921 }
1922 
1923 /* should be called with either EXIT_SUCCESS or EXIT_FAILURE */
1924 void
nh_terminate(int status)1925 nh_terminate(int status)
1926 {
1927     g.program_state.in_moveloop = 0; /* won't be returning to normal play */
1928 #ifdef MAC
1929     getreturn("to exit");
1930 #endif
1931     /* don't bother to try to release memory if we're in panic mode, to
1932        avoid trouble in case that happens to be due to memory problems */
1933     if (!g.program_state.panicking) {
1934         freedynamicdata();
1935         dlb_cleanup();
1936     }
1937 
1938 #ifdef VMS
1939     /*
1940      *  This is liable to draw a warning if compiled with gcc, but it's
1941      *  more important to flag panic() -> really_done() -> nh_terminate()
1942      *  as __noreturn__ then to avoid the warning.
1943      */
1944     /* don't call exit() if already executing within an exit handler;
1945        that would cancel any other pending user-mode handlers */
1946     if (g.program_state.exiting)
1947         return;
1948 #endif
1949     g.program_state.exiting = 1;
1950     nethack_exit(status);
1951 }
1952 
1953 /* set a delayed killer, ensure non-delayed killer is cleared out */
1954 void
delayed_killer(int id,int format,const char * killername)1955 delayed_killer(int id, int format, const char *killername)
1956 {
1957     struct kinfo *k = find_delayed_killer(id);
1958 
1959     if (!k) {
1960         /* no match, add a new delayed killer to the list */
1961         k = (struct kinfo *) alloc(sizeof (struct kinfo));
1962         (void) memset((genericptr_t) k, 0, sizeof (struct kinfo));
1963         k->id = id;
1964         k->next = g.killer.next;
1965         g.killer.next = k;
1966     }
1967 
1968     k->format = format;
1969     Strcpy(k->name, killername ? killername : "");
1970     g.killer.name[0] = 0;
1971 }
1972 
1973 struct kinfo *
find_delayed_killer(int id)1974 find_delayed_killer(int id)
1975 {
1976     struct kinfo *k;
1977 
1978     for (k = g.killer.next; k != (struct kinfo *) 0; k = k->next) {
1979         if (k->id == id)
1980             break;
1981     }
1982     return k;
1983 }
1984 
1985 void
dealloc_killer(struct kinfo * kptr)1986 dealloc_killer(struct kinfo *kptr)
1987 {
1988     struct kinfo *prev = &g.killer, *k;
1989 
1990     if (kptr == (struct kinfo *) 0)
1991         return;
1992     for (k = g.killer.next; k != (struct kinfo *) 0; k = k->next) {
1993         if (k == kptr)
1994             break;
1995         prev = k;
1996     }
1997 
1998     if (k == (struct kinfo *) 0) {
1999         impossible("dealloc_killer (#%d) not on list", kptr->id);
2000     } else {
2001         prev->next = k->next;
2002         free((genericptr_t) k);
2003         debugpline1("freed delayed killer #%d", kptr->id);
2004     }
2005 }
2006 
2007 void
save_killers(NHFILE * nhfp)2008 save_killers(NHFILE *nhfp)
2009 {
2010     struct kinfo *kptr;
2011 
2012     if (perform_bwrite(nhfp)) {
2013         for (kptr = &g.killer; kptr != (struct kinfo *) 0; kptr = kptr->next) {
2014             if (nhfp->structlevel)
2015 	        bwrite(nhfp->fd, (genericptr_t)kptr, sizeof(struct kinfo));
2016         }
2017     }
2018     if (release_data(nhfp)) {
2019         while (g.killer.next) {
2020             kptr = g.killer.next->next;
2021             free((genericptr_t) g.killer.next);
2022             g.killer.next = kptr;
2023         }
2024     }
2025 }
2026 
2027 void
restore_killers(NHFILE * nhfp)2028 restore_killers(NHFILE *nhfp)
2029 {
2030     struct kinfo *kptr;
2031 
2032     for (kptr = &g.killer; kptr != (struct kinfo *) 0; kptr = kptr->next) {
2033         if (nhfp->structlevel)
2034 	    mread(nhfp->fd, (genericptr_t)kptr, sizeof(struct kinfo));
2035         if (kptr->next) {
2036             kptr->next = (struct kinfo *) alloc(sizeof (struct kinfo));
2037         }
2038     }
2039 }
2040 
2041 static int
wordcount(char * p)2042 wordcount(char *p)
2043 {
2044     int words = 0;
2045 
2046     while (*p) {
2047         while (*p && isspace((uchar) *p))
2048             p++;
2049         if (*p)
2050             words++;
2051         while (*p && !isspace((uchar) *p))
2052             p++;
2053     }
2054     return words;
2055 }
2056 
2057 static void
bel_copy1(char ** inp,char * out)2058 bel_copy1(char **inp, char *out)
2059 {
2060     char *in = *inp;
2061 
2062     out += strlen(out); /* eos() */
2063     while (*in && isspace((uchar) *in))
2064         in++;
2065     while (*in && !isspace((uchar) *in))
2066         *out++ = *in++;
2067     *out = '\0';
2068     *inp = in;
2069 }
2070 
2071 char *
build_english_list(char * in)2072 build_english_list(char *in)
2073 {
2074     char *out, *p = in;
2075     int len = (int) strlen(p), words = wordcount(p);
2076 
2077     /* +3: " or " - " "; +(words - 1): (N-1)*(", " - " ") */
2078     if (words > 1)
2079         len += 3 + (words - 1);
2080     out = (char *) alloc(len + 1);
2081     *out = '\0'; /* bel_copy1() appends */
2082 
2083     switch (words) {
2084     case 0:
2085         impossible("no words in list");
2086         break;
2087     case 1:
2088         /* "single" */
2089         bel_copy1(&p, out);
2090         break;
2091     default:
2092         if (words == 2) {
2093             /* "first or second" */
2094             bel_copy1(&p, out);
2095             Strcat(out, " ");
2096         } else {
2097             /* "first, second, or third */
2098             do {
2099                 bel_copy1(&p, out);
2100                 Strcat(out, ", ");
2101             } while (--words > 1);
2102         }
2103         Strcat(out, "or ");
2104         bel_copy1(&p, out);
2105         break;
2106     }
2107     return out;
2108 }
2109 
2110 /*end.c*/
2111