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