fix 'crash when reviving shopkeeper'
[aNetHack.git] / src / end.c
blob437b9072d55eca3e49b82b8f8cc3b6df60ec544b
1 /* NetHack 3.6 end.c $NHDT-Date: 1461919723 2016/04/29 08:48:43 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.116 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
5 #define NEED_VARARGS /* comment line for pre-compiled headers */
7 #include "hack.h"
8 #include "lev.h"
9 #ifndef NO_SIGNAL
10 #include <signal.h>
11 #endif
12 #include <ctype.h>
13 #include <limits.h>
14 #include "dlb.h"
16 /* add b to long a, convert wraparound to max value */
17 #define nowrap_add(a, b) (a = ((a + b) < 0 ? LONG_MAX : (a + b)))
19 /* these probably ought to be generated by makedefs, like LAST_GEM */
20 #define FIRST_GEM DILITHIUM_CRYSTAL
21 #define FIRST_AMULET AMULET_OF_ESP
22 #define LAST_AMULET AMULET_OF_YENDOR
24 struct valuable_data {
25 long count;
26 int typ;
29 static struct valuable_data
30 gems[LAST_GEM + 1 - FIRST_GEM + 1], /* 1 extra for glass */
31 amulets[LAST_AMULET + 1 - FIRST_AMULET];
33 static struct val_list {
34 struct valuable_data *list;
35 int size;
36 } valuables[] = { { gems, sizeof gems / sizeof *gems },
37 { amulets, sizeof amulets / sizeof *amulets },
38 { 0, 0 } };
40 #ifndef NO_SIGNAL
41 STATIC_PTR void FDECL(done_intr, (int));
42 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
43 static void FDECL(done_hangup, (int));
44 #endif
45 #endif
46 STATIC_DCL void FDECL(disclose, (int, BOOLEAN_P));
47 STATIC_DCL void FDECL(get_valuables, (struct obj *));
48 STATIC_DCL void FDECL(sort_valuables, (struct valuable_data *, int));
49 STATIC_DCL void FDECL(artifact_score, (struct obj *, BOOLEAN_P, winid));
50 STATIC_DCL void FDECL(really_done, (int)) NORETURN;
51 STATIC_DCL boolean FDECL(odds_and_ends, (struct obj *, int));
52 STATIC_DCL void FDECL(savelife, (int));
53 STATIC_PTR int FDECL(CFDECLSPEC vanqsort_cmp, (const genericptr,
54 const genericptr));
55 STATIC_DCL int NDECL(set_vanq_order);
56 STATIC_DCL void FDECL(list_vanquished, (CHAR_P, BOOLEAN_P));
57 STATIC_DCL void FDECL(list_genocided, (CHAR_P, BOOLEAN_P));
58 STATIC_DCL boolean FDECL(should_query_disclose_option, (int, char *));
59 STATIC_DCL int NDECL(num_extinct);
61 #if defined(__BEOS__) || defined(MICRO) || defined(WIN32) || defined(OS2)
62 extern void FDECL(nethack_exit, (int));
63 #else
64 #define nethack_exit exit
65 #endif
67 #define done_stopprint program_state.stopprint
69 #ifndef PANICTRACE
70 #define NH_abort NH_abort_
71 #endif
73 #ifdef AMIGA
74 #define NH_abort_() Abort(0)
75 #else
76 #ifdef SYSV
77 #define NH_abort_() (void) abort()
78 #else
79 #ifdef WIN32
80 #define NH_abort_() win32_abort()
81 #else
82 #define NH_abort_() abort()
83 #endif
84 #endif /* !SYSV */
85 #endif /* !AMIGA */
87 #ifdef PANICTRACE
88 #include <errno.h>
89 #ifdef PANICTRACE_LIBC
90 #include <execinfo.h>
91 #endif
93 /* What do we try and in what order? Tradeoffs:
94 * libc: +no external programs required
95 * -requires newish libc/glibc
96 * -requires -rdynamic
97 * gdb: +gives more detailed information
98 * +works on more OS versions
99 * -requires -g, which may preclude -O on some compilers
101 #ifdef SYSCF
102 #define SYSOPT_PANICTRACE_GDB sysopt.panictrace_gdb
103 #ifdef PANICTRACE_LIBC
104 #define SYSOPT_PANICTRACE_LIBC sysopt.panictrace_libc
105 #else
106 #define SYSOPT_PANICTRACE_LIBC 0
107 #endif
108 #else
109 #define SYSOPT_PANICTRACE_GDB (nh_getenv("NETHACK_USE_GDB") == 0 ? 0 : 2)
110 #ifdef PANICTRACE_LIBC
111 #define SYSOPT_PANICTRACE_LIBC 1
112 #else
113 #define SYSOPT_PANICTRACE_LIBC 0
114 #endif
115 #endif
117 static void NDECL(NH_abort);
118 #ifndef NO_SIGNAL
119 static void FDECL(panictrace_handler, (int));
120 #endif
121 static boolean NDECL(NH_panictrace_libc);
122 static boolean NDECL(NH_panictrace_gdb);
124 #ifndef NO_SIGNAL
125 /*ARGSUSED*/
126 void panictrace_handler(
127 sig_unused) /* called as signal() handler, so sent at least one arg */
128 int sig_unused UNUSED;
130 #define SIG_MSG "\nSignal received.\n"
131 (void) write(2, SIG_MSG, sizeof(SIG_MSG) - 1);
132 NH_abort();
135 void
136 panictrace_setsignals(set)
137 boolean set;
139 #define SETSIGNAL(sig) \
140 (void) signal(sig, set ? (SIG_RET_TYPE) panictrace_handler : SIG_DFL);
141 #ifdef SIGILL
142 SETSIGNAL(SIGILL);
143 #endif
144 #ifdef SIGTRAP
145 SETSIGNAL(SIGTRAP);
146 #endif
147 #ifdef SIGIOT
148 SETSIGNAL(SIGIOT);
149 #endif
150 #ifdef SIGBUS
151 SETSIGNAL(SIGBUS);
152 #endif
153 #ifdef SIGFPE
154 SETSIGNAL(SIGFPE);
155 #endif
156 #ifdef SIGSEGV
157 SETSIGNAL(SIGSEGV);
158 #endif
159 #ifdef SIGSTKFLT
160 SETSIGNAL(SIGSTKFLT);
161 #endif
162 #ifdef SIGSYS
163 SETSIGNAL(SIGSYS);
164 #endif
165 #ifdef SIGEMT
166 SETSIGNAL(SIGEMT);
167 #endif
168 #undef SETSIGNAL
170 #endif /* NO_SIGNAL */
172 static void
173 NH_abort()
175 int gdb_prio = SYSOPT_PANICTRACE_GDB;
176 int libc_prio = SYSOPT_PANICTRACE_LIBC;
177 static boolean aborting = FALSE;
179 if (aborting)
180 return;
181 aborting = TRUE;
183 #ifndef VMS
184 if (gdb_prio == libc_prio && gdb_prio > 0)
185 gdb_prio++;
187 if (gdb_prio > libc_prio) {
188 (void) (NH_panictrace_gdb() || (libc_prio && NH_panictrace_libc()));
189 } else {
190 (void) (NH_panictrace_libc() || (gdb_prio && NH_panictrace_gdb()));
193 #else /* VMS */
194 /* overload otherwise unused priority for debug mode: 1 = show
195 traceback and exit; 2 = show traceback and stay in debugger */
196 /* if (wizard && gdb_prio == 1) gdb_prio = 2; */
197 vms_traceback(gdb_prio);
198 (void) libc_prio; /* half-hearted attempt at lint suppression */
200 #endif /* ?VMS */
202 #ifndef NO_SIGNAL
203 panictrace_setsignals(FALSE);
204 #endif
205 NH_abort_();
208 static boolean
209 NH_panictrace_libc()
211 #ifdef PANICTRACE_LIBC
212 void *bt[20];
213 size_t count, x;
214 char **info;
216 raw_print("Generating more information you may report:\n");
217 count = backtrace(bt, SIZE(bt));
218 info = backtrace_symbols(bt, count);
219 for (x = 0; x < count; x++) {
220 raw_printf("[%lu] %s", (unsigned long) x, info[x]);
222 /* free(info); -- Don't risk it. */
223 return TRUE;
224 #else
225 return FALSE;
226 #endif /* !PANICTRACE_LIBC */
230 * fooPATH file system path for foo
231 * fooVAR (possibly const) variable containing fooPATH
233 #ifdef PANICTRACE_GDB
234 #ifdef SYSCF
235 #define GDBVAR sysopt.gdbpath
236 #define GREPVAR sysopt.greppath
237 #else /* SYSCF */
238 #define GDBVAR GDBPATH
239 #define GREPVAR GREPPATH
240 #endif /* SYSCF */
241 #endif /* PANICTRACE_GDB */
243 static boolean
244 NH_panictrace_gdb()
246 #ifdef PANICTRACE_GDB
247 /* A (more) generic method to get a stack trace - invoke
248 * gdb on ourself. */
249 char *gdbpath = GDBVAR;
250 char *greppath = GREPVAR;
251 char buf[BUFSZ];
252 FILE *gdb;
254 if (gdbpath == NULL || gdbpath[0] == 0)
255 return FALSE;
256 if (greppath == NULL || greppath[0] == 0)
257 return FALSE;
259 sprintf(buf, "%s -n -q %s %d 2>&1 | %s '^#'", gdbpath, ARGV0, getpid(),
260 greppath);
261 gdb = popen(buf, "w");
262 if (gdb) {
263 raw_print("Generating more information you may report:\n");
264 fprintf(gdb, "bt\nquit\ny");
265 fflush(gdb);
266 sleep(4); /* ugly */
267 pclose(gdb);
268 return TRUE;
269 } else {
270 return FALSE;
272 #else
273 return FALSE;
274 #endif /* !PANICTRACE_GDB */
276 #endif /* PANICTRACE */
279 * The order of these needs to match the macros in hack.h.
281 static NEARDATA const char *deaths[] = {
282 /* the array of death */
283 "died", "choked", "poisoned", "starvation", "drowning", "burning",
284 "dissolving under the heat and pressure", "crushed", "turned to stone",
285 "turned into slime", "genocided", "panic", "trickery", "quit",
286 "escaped", "ascended"
289 static NEARDATA const char *ends[] = {
290 /* "when you %s" */
291 "died", "choked", "were poisoned",
292 "starved", "drowned", "burned",
293 "dissolved in the lava",
294 "were crushed", "turned to stone",
295 "turned into slime", "were genocided",
296 "panicked", "were tricked", "quit",
297 "escaped", "ascended"
300 static boolean Schroedingers_cat = FALSE;
302 /*ARGSUSED*/
303 void
304 done1(sig_unused) /* called as signal() handler, so sent at least one arg */
305 int sig_unused UNUSED;
307 #ifndef NO_SIGNAL
308 (void) signal(SIGINT, SIG_IGN);
309 #endif
310 if (flags.ignintr) {
311 #ifndef NO_SIGNAL
312 (void) signal(SIGINT, (SIG_RET_TYPE) done1);
313 #endif
314 clear_nhwindow(WIN_MESSAGE);
315 curs_on_u();
316 wait_synch();
317 if (multi > 0)
318 nomul(0);
319 } else {
320 (void) done2();
324 /* "#quit" command or keyboard interrupt */
326 done2()
328 if (!paranoid_query(ParanoidQuit, "Really quit?")) {
329 #ifndef NO_SIGNAL
330 (void) signal(SIGINT, (SIG_RET_TYPE) done1);
331 #endif
332 clear_nhwindow(WIN_MESSAGE);
333 curs_on_u();
334 wait_synch();
335 if (multi > 0)
336 nomul(0);
337 if (multi == 0) {
338 u.uinvulnerable = FALSE; /* avoid ctrl-C bug -dlc */
339 u.usleep = 0;
341 return 0;
343 #if (defined(UNIX) || defined(VMS) || defined(LATTICE))
344 if (wizard) {
345 int c;
346 #ifdef VMS
347 extern int debuggable; /* sys/vms/vmsmisc.c, vmsunix.c */
349 c = !debuggable ? 'n' : ynq("Enter debugger?");
350 #else
351 #ifdef LATTICE
352 c = ynq("Create SnapShot?");
353 #else
354 c = ynq("Dump core?");
355 #endif
356 #endif
357 if (c == 'y') {
358 #ifndef NO_SIGNAL
359 (void) signal(SIGINT, (SIG_RET_TYPE) done1);
360 #endif
361 exit_nhwindows((char *) 0);
362 NH_abort();
363 } else if (c == 'q')
364 done_stopprint++;
366 #endif
367 #ifndef LINT
368 done(QUIT);
369 #endif
370 return 0;
373 #ifndef NO_SIGNAL
374 /*ARGSUSED*/
375 STATIC_PTR void
376 done_intr(sig_unused) /* called as signal() handler, so sent at least 1 arg */
377 int sig_unused UNUSED;
379 done_stopprint++;
380 (void) signal(SIGINT, SIG_IGN);
381 #if defined(UNIX) || defined(VMS)
382 (void) signal(SIGQUIT, SIG_IGN);
383 #endif
384 return;
387 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
388 /* signal() handler */
389 static void
390 done_hangup(sig)
391 int sig;
393 program_state.done_hup++;
394 sethanguphandler((void FDECL((*), (int) )) SIG_IGN);
395 done_intr(sig);
396 return;
398 #endif
399 #endif /* NO_SIGNAL */
401 void
402 done_in_by(mtmp, how)
403 struct monst *mtmp;
404 int how;
406 char buf[BUFSZ];
407 struct permonst *mptr = mtmp->data,
408 *champtr = ((mtmp->cham >= LOW_PM)
409 ? &mons[mtmp->cham]
410 : mptr);
411 boolean distorted = (boolean) (Hallucination && canspotmon(mtmp)),
412 mimicker = (mtmp->m_ap_type == M_AP_MONSTER),
413 imitator = (mptr != champtr || mimicker);
415 You((how == STONING) ? "turn to stone..." : "die...");
416 mark_synch(); /* flush buffered screen output */
417 buf[0] = '\0';
418 killer.format = KILLED_BY_AN;
419 /* "killed by the high priest of Crom" is okay,
420 "killed by the high priest" alone isn't */
421 if ((mptr->geno & G_UNIQ) != 0 && !(imitator && !mimicker)
422 && !(mptr == &mons[PM_HIGH_PRIEST] && !mtmp->ispriest)) {
423 if (!type_is_pname(mptr))
424 Strcat(buf, "the ");
425 killer.format = KILLED_BY;
427 /* _the_ <invisible> <distorted> ghost of Dudley */
428 if (mptr == &mons[PM_GHOST] && has_mname(mtmp)) {
429 Strcat(buf, "the ");
430 killer.format = KILLED_BY;
432 if (mtmp->minvis)
433 Strcat(buf, "invisible ");
434 if (distorted)
435 Strcat(buf, "hallucinogen-distorted ");
437 if (imitator) {
438 char shape[BUFSZ];
439 const char *realnm = champtr->mname, *fakenm = mptr->mname;
440 boolean alt = is_vampshifter(mtmp);
442 if (mimicker) {
443 /* realnm is already correct because champtr==mptr;
444 set up fake mptr for type_is_pname/the_unique_pm */
445 mptr = &mons[mtmp->mappearance];
446 fakenm = mptr->mname;
447 } else if (alt && strstri(realnm, "vampire")
448 && !strcmp(fakenm, "vampire bat")) {
449 /* special case: use "vampire in bat form" in preference
450 to redundant looking "vampire in vampire bat form" */
451 fakenm = "bat";
453 /* for the alternate format, always suppress any article;
454 pname and the_unique should also have s_suffix() applied,
455 but vampires don't take on any shapes which warrant that */
456 if (alt || type_is_pname(mptr)) /* no article */
457 Strcpy(shape, fakenm);
458 else if (the_unique_pm(mptr)) /* "the"; don't use the() here */
459 Sprintf(shape, "the %s", fakenm);
460 else /* "a"/"an" */
461 Strcpy(shape, an(fakenm));
462 /* omit "called" to avoid excessive verbosity */
463 Sprintf(eos(buf),
464 alt ? "%s in %s form"
465 : mimicker ? "%s disguised as %s"
466 : "%s imitating %s",
467 realnm, shape);
468 mptr = mtmp->data; /* reset for mimicker case */
469 } else if (mptr == &mons[PM_GHOST]) {
470 Strcat(buf, "ghost");
471 if (has_mname(mtmp))
472 Sprintf(eos(buf), " of %s", MNAME(mtmp));
473 } else if (mtmp->isshk) {
474 const char *shknm = shkname(mtmp),
475 *honorific = shkname_is_pname(mtmp) ? ""
476 : mtmp->female ? "Ms. " : "Mr. ";
478 Sprintf(eos(buf), "%s%s, the shopkeeper", honorific, shknm);
479 killer.format = KILLED_BY;
480 } else if (mtmp->ispriest || mtmp->isminion) {
481 /* m_monnam() suppresses "the" prefix plus "invisible", and
482 it overrides the effect of Hallucination on priestname() */
483 Strcat(buf, m_monnam(mtmp));
484 } else {
485 Strcat(buf, mptr->mname);
486 if (has_mname(mtmp))
487 Sprintf(eos(buf), " called %s", MNAME(mtmp));
490 Strcpy(killer.name, buf);
491 if (mptr->mlet == S_WRAITH)
492 u.ugrave_arise = PM_WRAITH;
493 else if (mptr->mlet == S_MUMMY && urace.mummynum != NON_PM)
494 u.ugrave_arise = urace.mummynum;
495 else if (mptr->mlet == S_VAMPIRE && Race_if(PM_HUMAN))
496 u.ugrave_arise = PM_VAMPIRE;
497 else if (mptr == &mons[PM_GHOUL])
498 u.ugrave_arise = PM_GHOUL;
499 /* this could happen if a high-end vampire kills the hero
500 when ordinary vampires are genocided; ditto for wraiths */
501 if (u.ugrave_arise >= LOW_PM
502 && (mvitals[u.ugrave_arise].mvflags & G_GENOD))
503 u.ugrave_arise = NON_PM;
505 done(how);
506 return;
509 /* some special cases for overriding while-helpless reason */
510 static const struct {
511 int why, unmulti;
512 const char *exclude, *include;
513 } death_fixups[] = {
514 /* "petrified by <foo>, while getting stoned" -- "while getting stoned"
515 prevented any last-second recovery, but it was not the cause of
516 "petrified by <foo>" */
517 { STONING, 1, "getting stoned", (char *) 0 },
518 /* "died of starvation, while fainted from lack of food" is accurate
519 but sounds a fairly silly (and doesn't actually appear unless you
520 splice together death and while-helpless from xlogfile) */
521 { STARVING, 0, "fainted from lack of food", "fainted" },
524 /* clear away while-helpless when the cause of death caused that
525 helplessness (ie, "petrified by <foo> while getting stoned") */
526 STATIC_DCL void
527 fixup_death(how)
528 int how;
530 int i;
532 if (multi_reason) {
533 for (i = 0; i < SIZE(death_fixups); ++i)
534 if (death_fixups[i].why == how
535 && !strcmp(death_fixups[i].exclude, multi_reason)) {
536 if (death_fixups[i].include) /* substitute alternate reason */
537 multi_reason = death_fixups[i].include;
538 else /* remove the helplessness reason */
539 multi_reason = (char *) 0;
540 if (death_fixups[i].unmulti) /* possibly hide helplessness */
541 multi = 0L;
542 break;
547 #if defined(WIN32) && !defined(SYSCF)
548 #define NOTIFY_NETHACK_BUGS
549 #endif
551 /*VARARGS1*/
552 void panic
553 VA_DECL(const char *, str)
555 VA_START(str);
556 VA_INIT(str, char *);
558 if (program_state.panicking++)
559 NH_abort(); /* avoid loops - this should never happen*/
561 if (iflags.window_inited) {
562 raw_print("\r\nOops...");
563 wait_synch(); /* make sure all pending output gets flushed */
564 exit_nhwindows((char *) 0);
565 iflags.window_inited = 0; /* they're gone; force raw_print()ing */
568 raw_print(program_state.gameover
569 ? "Postgame wrapup disrupted."
570 : !program_state.something_worth_saving
571 ? "Program initialization has failed."
572 : "Suddenly, the dungeon collapses.");
573 #ifndef MICRO
574 #if defined(NOTIFY_NETHACK_BUGS)
575 if (!wizard)
576 raw_printf("Report the following error to \"%s\" or at \"%s\".",
577 DEVTEAM_EMAIL, DEVTEAM_URL);
578 else if (program_state.something_worth_saving)
579 raw_print("\nError save file being written.\n");
580 #else
581 if (!wizard) {
582 const char *maybe_rebuild = !program_state.something_worth_saving
583 ? "."
584 : "\nand it may be possible to rebuild.";
586 if (sysopt.support)
587 raw_printf("To report this error, %s%s", sysopt.support,
588 maybe_rebuild);
589 else if (sysopt.fmtd_wizard_list) /* formatted SYSCF WIZARDS */
590 raw_printf("To report this error, contact %s%s",
591 sysopt.fmtd_wizard_list, maybe_rebuild);
592 else
593 raw_printf("Report error to \"%s\"%s", WIZARD_NAME,
594 maybe_rebuild);
596 #endif
597 /* XXX can we move this above the prints? Then we'd be able to
598 * suppress "it may be possible to rebuild" based on dosave0()
599 * or say it's NOT possible to rebuild. */
600 if (program_state.something_worth_saving) {
601 set_error_savefile();
602 if (dosave0()) {
603 /* os/win port specific recover instructions */
604 if (sysopt.recover)
605 raw_printf("%s", sysopt.recover);
608 #endif
610 char buf[BUFSZ];
612 Vsprintf(buf, str, VA_ARGS);
613 raw_print(buf);
614 paniclog("panic", buf);
616 #ifdef WIN32
617 interject(INTERJECT_PANIC);
618 #endif
619 #if defined(UNIX) || defined(VMS) || defined(LATTICE) || defined(WIN32)
620 if (wizard)
621 NH_abort(); /* generate core dump */
622 #endif
623 VA_END();
624 really_done(PANICKED);
627 STATIC_OVL boolean
628 should_query_disclose_option(category, defquery)
629 int category;
630 char *defquery;
632 int idx;
633 char disclose, *dop;
635 *defquery = 'n';
636 if ((dop = index(disclosure_options, category)) != 0) {
637 idx = (int) (dop - disclosure_options);
638 if (idx < 0 || idx >= NUM_DISCLOSURE_OPTIONS) {
639 impossible(
640 "should_query_disclose_option: bad disclosure index %d %c",
641 idx, category);
642 *defquery = DISCLOSE_PROMPT_DEFAULT_YES;
643 return TRUE;
645 disclose = flags.end_disclose[idx];
646 if (disclose == DISCLOSE_YES_WITHOUT_PROMPT) {
647 *defquery = 'y';
648 return FALSE;
649 } else if (disclose == DISCLOSE_SPECIAL_WITHOUT_PROMPT) {
650 *defquery = 'a';
651 return FALSE;
652 } else if (disclose == DISCLOSE_NO_WITHOUT_PROMPT) {
653 *defquery = 'n';
654 return FALSE;
655 } else if (disclose == DISCLOSE_PROMPT_DEFAULT_YES) {
656 *defquery = 'y';
657 return TRUE;
658 } else if (disclose == DISCLOSE_PROMPT_DEFAULT_SPECIAL) {
659 *defquery = 'a';
660 return TRUE;
661 } else {
662 *defquery = 'n';
663 return TRUE;
666 impossible("should_query_disclose_option: bad category %c", category);
667 return TRUE;
670 STATIC_OVL void
671 disclose(how, taken)
672 int how;
673 boolean taken;
675 char c = '\0', defquery;
676 char qbuf[QBUFSZ];
677 boolean ask = FALSE;
679 if (invent && !done_stopprint) {
680 if (taken)
681 Sprintf(qbuf, "Do you want to see what you had when you %s?",
682 (how == QUIT) ? "quit" : "died");
683 else
684 Strcpy(qbuf, "Do you want your possessions identified?");
686 ask = should_query_disclose_option('i', &defquery);
687 c = ask ? yn_function(qbuf, ynqchars, defquery) : defquery;
688 if (c == 'y') {
689 struct obj *obj;
691 for (obj = invent; obj; obj = obj->nobj) {
692 makeknown(obj->otyp);
693 obj->known = obj->bknown = obj->dknown = obj->rknown = 1;
694 if (Is_container(obj) || obj->otyp == STATUE)
695 obj->cknown = obj->lknown = 1;
697 (void) display_inventory((char *) 0, TRUE);
698 container_contents(invent, TRUE, TRUE, FALSE);
700 if (c == 'q')
701 done_stopprint++;
704 if (!done_stopprint) {
705 ask = should_query_disclose_option('a', &defquery);
706 c = ask ? yn_function("Do you want to see your attributes?", ynqchars,
707 defquery)
708 : defquery;
709 if (c == 'y')
710 enlightenment((BASICENLIGHTENMENT | MAGICENLIGHTENMENT),
711 (how >= PANICKED) ? ENL_GAMEOVERALIVE
712 : ENL_GAMEOVERDEAD);
713 if (c == 'q')
714 done_stopprint++;
717 if (!done_stopprint) {
718 ask = should_query_disclose_option('v', &defquery);
719 list_vanquished(defquery, ask);
722 if (!done_stopprint) {
723 ask = should_query_disclose_option('g', &defquery);
724 list_genocided(defquery, ask);
727 if (!done_stopprint) {
728 ask = should_query_disclose_option('c', &defquery);
729 c = ask ? yn_function("Do you want to see your conduct?", ynqchars,
730 defquery)
731 : defquery;
732 if (c == 'y')
733 show_conduct((how >= PANICKED) ? 1 : 2);
734 if (c == 'q')
735 done_stopprint++;
738 if (!done_stopprint) {
739 ask = should_query_disclose_option('o', &defquery);
740 c = ask ? yn_function("Do you want to see the dungeon overview?",
741 ynqchars, defquery)
742 : defquery;
743 if (c == 'y')
744 show_overview((how >= PANICKED) ? 1 : 2, how);
745 if (c == 'q')
746 done_stopprint++;
750 /* try to get the player back in a viable state after being killed */
751 STATIC_OVL void
752 savelife(how)
753 int how;
755 int uhpmin = max(2 * u.ulevel, 10);
757 if (u.uhpmax < uhpmin)
758 u.uhpmax = uhpmin;
759 u.uhp = u.uhpmax;
760 u.uswldtim = 0;
761 if (u.uhunger < 500) {
762 u.uhunger = 500;
763 newuhs(FALSE);
765 /* cure impending doom of sickness hero won't have time to fix */
766 if ((Sick & TIMEOUT) == 1L) {
767 u.usick_type = 0;
768 set_itimeout(&Sick, 0L);
770 if (how == CHOKING)
771 init_uhunger();
772 nomovemsg = "You survived that attempt on your life.";
773 context.move = 0;
774 if (multi > 0)
775 multi = 0;
776 else
777 multi = -1;
778 if (u.utrap && u.utraptype == TT_LAVA)
779 u.utrap = 0;
780 context.botl = 1;
781 u.ugrave_arise = NON_PM;
782 HUnchanging = 0L;
783 curs_on_u();
787 * Get valuables from the given list. Revised code: the list always remains
788 * intact.
790 STATIC_OVL void
791 get_valuables(list)
792 struct obj *list; /* inventory or container contents */
794 register struct obj *obj;
795 register int i;
797 /* find amulets and gems, ignoring all artifacts */
798 for (obj = list; obj; obj = obj->nobj)
799 if (Has_contents(obj)) {
800 get_valuables(obj->cobj);
801 } else if (obj->oartifact) {
802 continue;
803 } else if (obj->oclass == AMULET_CLASS) {
804 i = obj->otyp - FIRST_AMULET;
805 if (!amulets[i].count) {
806 amulets[i].count = obj->quan;
807 amulets[i].typ = obj->otyp;
808 } else
809 amulets[i].count += obj->quan; /* always adds one */
810 } else if (obj->oclass == GEM_CLASS && obj->otyp < LUCKSTONE) {
811 i = min(obj->otyp, LAST_GEM + 1) - FIRST_GEM;
812 if (!gems[i].count) {
813 gems[i].count = obj->quan;
814 gems[i].typ = obj->otyp;
815 } else
816 gems[i].count += obj->quan;
818 return;
822 * Sort collected valuables, most frequent to least. We could just
823 * as easily use qsort, but we don't care about efficiency here.
825 STATIC_OVL void
826 sort_valuables(list, size)
827 struct valuable_data list[];
828 int size; /* max value is less than 20 */
830 register int i, j;
831 struct valuable_data ltmp;
833 /* move greater quantities to the front of the list */
834 for (i = 1; i < size; i++) {
835 if (list[i].count == 0)
836 continue; /* empty slot */
837 ltmp = list[i]; /* structure copy */
838 for (j = i; j > 0; --j)
839 if (list[j - 1].count >= ltmp.count)
840 break;
841 else {
842 list[j] = list[j - 1];
844 list[j] = ltmp;
846 return;
849 #define CAT_CHECK 2
851 STATIC_OVL boolean
852 odds_and_ends(list, what)
853 struct obj *list;
854 int what;
856 struct obj *otmp;
857 for (otmp = list; otmp; otmp = otmp->nobj) {
858 switch (what) {
859 case CAT_CHECK: /* Schroedinger's Cat */
860 /* Ascending is deterministic */
861 if (SchroedingersBox(otmp))
862 return rn2(2);
863 break;
865 if (Has_contents(otmp))
866 return odds_and_ends(otmp->cobj, what);
868 return FALSE;
871 /* called twice; first to calculate total, then to list relevant items */
872 STATIC_OVL void
873 artifact_score(list, counting, endwin)
874 struct obj *list;
875 boolean counting; /* true => add up points; false => display them */
876 winid endwin;
878 char pbuf[BUFSZ];
879 struct obj *otmp;
880 long value, points;
881 short dummy; /* object type returned by artifact_name() */
883 for (otmp = list; otmp; otmp = otmp->nobj) {
884 if (otmp->oartifact || otmp->otyp == BELL_OF_OPENING
885 || otmp->otyp == SPE_BOOK_OF_THE_DEAD
886 || otmp->otyp == CANDELABRUM_OF_INVOCATION) {
887 value = arti_cost(otmp); /* zorkmid value */
888 points = value * 5 / 2; /* score value */
889 if (counting) {
890 nowrap_add(u.urexp, points);
891 } else {
892 makeknown(otmp->otyp);
893 otmp->known = otmp->dknown = otmp->bknown = otmp->rknown = 1;
894 /* assumes artifacts don't have quan > 1 */
895 Sprintf(pbuf, "%s%s (worth %ld %s and %ld points)",
896 the_unique_obj(otmp) ? "The " : "",
897 otmp->oartifact ? artifact_name(xname(otmp), &dummy)
898 : OBJ_NAME(objects[otmp->otyp]),
899 value, currency(value), points);
900 putstr(endwin, 0, pbuf);
903 if (Has_contents(otmp))
904 artifact_score(otmp->cobj, counting, endwin);
908 /* Be careful not to call panic from here! */
909 void
910 done(how)
911 int how;
913 if (how == TRICKED) {
914 if (killer.name[0]) {
915 paniclog("trickery", killer.name);
916 killer.name[0] = 0;
918 if (wizard) {
919 You("are a very tricky wizard, it seems.");
920 return;
924 if (how == ASCENDED || (!killer.name[0] && how == GENOCIDED))
925 killer.format = NO_KILLER_PREFIX;
926 /* Avoid killed by "a" burning or "a" starvation */
927 if (!killer.name[0] && (how == STARVING || how == BURNING))
928 killer.format = KILLED_BY;
929 if (!killer.name[0] || how >= PANICKED)
930 Strcpy(killer.name, deaths[how]);
932 if (how < PANICKED)
933 u.umortality++;
934 if (Lifesaved && (how <= GENOCIDED)) {
935 pline("But wait...");
936 makeknown(AMULET_OF_LIFE_SAVING);
937 Your("medallion %s!", !Blind ? "begins to glow" : "feels warm");
938 if (how == CHOKING)
939 You("vomit ...");
940 You_feel("much better!");
941 pline_The("medallion crumbles to dust!");
942 if (uamul)
943 useup(uamul);
945 (void) adjattrib(A_CON, -1, TRUE);
946 savelife(how);
947 if (how == GENOCIDED) {
948 pline("Unfortunately you are still genocided...");
949 } else {
950 killer.name[0] = 0;
951 killer.format = 0;
952 return;
955 if ((wizard || discover) && (how <= GENOCIDED)
956 && !paranoid_query(ParanoidDie, "Die?")) {
957 pline("OK, so you don't %s.", (how == CHOKING) ? "choke" : "die");
958 savelife(how);
959 killer.name[0] = 0;
960 killer.format = 0;
961 return;
963 really_done(how);
966 /* separated from done() in order to specify the __noreturn__ attribute */
967 STATIC_OVL void
968 really_done(how)
969 int how;
971 boolean taken;
972 char pbuf[BUFSZ];
973 winid endwin = WIN_ERR;
974 boolean bones_ok, have_windows = iflags.window_inited;
975 struct obj *corpse = (struct obj *) 0;
976 time_t endtime;
977 long umoney;
978 long tmp;
981 * The game is now over...
983 program_state.gameover = 1;
984 /* in case of a subsequent panic(), there's no point trying to save */
985 program_state.something_worth_saving = 0;
986 /* render vision subsystem inoperative */
987 iflags.vision_inited = 0;
989 /* might have been killed while using a disposable item, so make sure
990 it's gone prior to inventory disclosure and creation of bones data */
991 inven_inuse(TRUE);
992 /* maybe not on object lists; if an active light source, would cause
993 big trouble (`obj_is_local' panic) for savebones() -> savelev() */
994 if (thrownobj && thrownobj->where == OBJ_FREE)
995 dealloc_obj(thrownobj);
996 if (kickedobj && kickedobj->where == OBJ_FREE)
997 dealloc_obj(kickedobj);
999 /* remember time of death here instead of having bones, rip, and
1000 topten figure it out separately and possibly getting different
1001 time or even day if player is slow responding to --More-- */
1002 urealtime.finish_time = endtime = getnow();
1003 urealtime.realtime += (long) (endtime - urealtime.start_timing);
1005 /* Sometimes you die on the first move. Life's not fair.
1006 * On those rare occasions you get hosed immediately, go out
1007 * smiling... :-) -3.
1009 if (moves <= 1 && how < PANICKED) /* You die... --More-- */
1010 pline("Do not pass go. Do not collect 200 %s.", currency(200L));
1012 if (have_windows)
1013 wait_synch(); /* flush screen output */
1014 #ifndef NO_SIGNAL
1015 (void) signal(SIGINT, (SIG_RET_TYPE) done_intr);
1016 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
1017 (void) signal(SIGQUIT, (SIG_RET_TYPE) done_intr);
1018 sethanguphandler(done_hangup);
1019 #endif
1020 #endif /* NO_SIGNAL */
1022 bones_ok = (how < GENOCIDED) && can_make_bones();
1024 if (bones_ok && launch_in_progress())
1025 force_launch_placement();
1027 /* maintain ugrave_arise even for !bones_ok */
1028 if (how == PANICKED)
1029 u.ugrave_arise = (NON_PM - 3); /* no corpse, no grave */
1030 else if (how == BURNING || how == DISSOLVED) /* corpse burns up too */
1031 u.ugrave_arise = (NON_PM - 2); /* leave no corpse */
1032 else if (how == STONING)
1033 u.ugrave_arise = (NON_PM - 1); /* statue instead of corpse */
1034 else if (how == TURNED_SLIME)
1035 u.ugrave_arise = PM_GREEN_SLIME;
1037 /* if pets will contribute to score, populate mydogs list now
1038 (bones creation isn't a factor, but pline() messaging is) */
1039 if (how == ESCAPED || how == ASCENDED)
1040 keepdogs(TRUE);
1042 if (how == QUIT) {
1043 killer.format = NO_KILLER_PREFIX;
1044 if (u.uhp < 1) {
1045 how = DIED;
1046 u.umortality++; /* skipped above when how==QUIT */
1047 Strcpy(killer.name, "quit while already on Charon's boat");
1050 if (how == ESCAPED || how == PANICKED)
1051 killer.format = NO_KILLER_PREFIX;
1053 fixup_death(how); /* actually, fixup multi_reason */
1055 if (how != PANICKED) {
1056 /* these affect score and/or bones, but avoid them during panic */
1057 taken = paybill((how == ESCAPED) ? -1 : (how != QUIT));
1058 paygd();
1059 clearpriests();
1060 } else
1061 taken = FALSE; /* lint; assert( !bones_ok ); */
1063 clearlocks();
1065 if (have_windows)
1066 display_nhwindow(WIN_MESSAGE, FALSE);
1068 if (strcmp(flags.end_disclose, "none") && how != PANICKED)
1069 disclose(how, taken);
1071 /* finish_paybill should be called after disclosure but before bones */
1072 if (bones_ok && taken)
1073 finish_paybill();
1075 /* grave creation should be after disclosure so it doesn't have
1076 this grave in the current level's features for #overview */
1077 if (bones_ok && u.ugrave_arise == NON_PM
1078 && !(mvitals[u.umonnum].mvflags & G_NOCORPSE)) {
1079 int mnum = u.umonnum;
1081 if (!Upolyd) {
1082 /* Base corpse on race when not poly'd since original
1083 * u.umonnum is based on role, and all role monsters
1084 * are human.
1086 mnum = (flags.female && urace.femalenum != NON_PM)
1087 ? urace.femalenum
1088 : urace.malenum;
1090 corpse = mk_named_object(CORPSE, &mons[mnum], u.ux, u.uy, plname);
1091 Sprintf(pbuf, "%s, ", plname);
1092 formatkiller(eos(pbuf), sizeof pbuf - strlen(pbuf), how, TRUE);
1093 make_grave(u.ux, u.uy, pbuf);
1095 pbuf[0] = '\0'; /* clear grave text; also lint suppression */
1097 /* calculate score, before creating bones [container gold] */
1099 int deepest = deepest_lev_reached(FALSE);
1101 umoney = money_cnt(invent);
1102 tmp = u.umoney0;
1103 umoney += hidden_gold(); /* accumulate gold from containers */
1104 tmp = umoney - tmp; /* net gain */
1106 if (tmp < 0L)
1107 tmp = 0L;
1108 if (how < PANICKED)
1109 tmp -= tmp / 10L;
1110 tmp += 50L * (long) (deepest - 1);
1111 if (deepest > 20)
1112 tmp += 1000L * (long) ((deepest > 30) ? 10 : deepest - 20);
1113 nowrap_add(u.urexp, tmp);
1115 /* ascension gives a score bonus iff offering to original deity */
1116 if (how == ASCENDED && u.ualign.type == u.ualignbase[A_ORIGINAL]) {
1117 /* retaining original alignment: score *= 2;
1118 converting, then using helm-of-OA to switch back: *= 1.5 */
1119 tmp = (u.ualignbase[A_CURRENT] == u.ualignbase[A_ORIGINAL])
1120 ? u.urexp
1121 : (u.urexp / 2L);
1122 nowrap_add(u.urexp, tmp);
1126 if (u.ugrave_arise >= LOW_PM && u.ugrave_arise != PM_GREEN_SLIME) {
1127 /* give this feedback even if bones aren't going to be created,
1128 so that its presence or absence doesn't tip off the player to
1129 new bones or their lack; it might be a lie if makemon fails */
1130 Your("body rises from the dead as %s...",
1131 an(mons[u.ugrave_arise].mname));
1132 display_nhwindow(WIN_MESSAGE, FALSE);
1135 if (bones_ok) {
1136 if (!wizard || paranoid_query(ParanoidBones, "Save bones?"))
1137 savebones(how, endtime, corpse);
1138 /* corpse may be invalid pointer now so
1139 ensure that it isn't used again */
1140 corpse = (struct obj *) 0;
1143 /* update gold for the rip output, which can't use hidden_gold()
1144 (containers will be gone by then if bones just got saved...) */
1145 done_money = umoney;
1147 /* clean up unneeded windows */
1148 if (have_windows) {
1149 wait_synch();
1150 free_pickinv_cache(); /* extra persistent window if perm_invent */
1151 if (WIN_INVEN != WIN_ERR)
1152 destroy_nhwindow(WIN_INVEN), WIN_INVEN = WIN_ERR;
1153 display_nhwindow(WIN_MESSAGE, TRUE);
1154 destroy_nhwindow(WIN_MAP), WIN_MAP = WIN_ERR;
1155 #ifndef STATUS_VIA_WINDOWPORT
1156 destroy_nhwindow(WIN_STATUS), WIN_STATUS = WIN_ERR;
1157 #endif
1158 destroy_nhwindow(WIN_MESSAGE), WIN_MESSAGE = WIN_ERR;
1160 if (!done_stopprint || flags.tombstone)
1161 endwin = create_nhwindow(NHW_TEXT);
1163 if (how < GENOCIDED && flags.tombstone && endwin != WIN_ERR)
1164 outrip(endwin, how, endtime);
1165 } else
1166 done_stopprint = 1; /* just avoid any more output */
1168 if (u.uhave.amulet) {
1169 Strcat(killer.name, " (with the Amulet)");
1170 } else if (how == ESCAPED) {
1171 if (Is_astralevel(&u.uz)) /* offered Amulet to wrong deity */
1172 Strcat(killer.name, " (in celestial disgrace)");
1173 else if (carrying(FAKE_AMULET_OF_YENDOR))
1174 Strcat(killer.name, " (with a fake Amulet)");
1175 /* don't bother counting to see whether it should be plural */
1178 if (!done_stopprint) {
1179 Sprintf(pbuf, "%s %s the %s...", Goodbye(), plname,
1180 (how != ASCENDED)
1181 ? (const char *) ((flags.female && urole.name.f)
1182 ? urole.name.f
1183 : urole.name.m)
1184 : (const char *) (flags.female ? "Demigoddess" : "Demigod"));
1185 putstr(endwin, 0, pbuf);
1186 putstr(endwin, 0, "");
1189 if (how == ESCAPED || how == ASCENDED) {
1190 struct monst *mtmp;
1191 struct obj *otmp;
1192 register struct val_list *val;
1193 register int i;
1195 for (val = valuables; val->list; val++)
1196 for (i = 0; i < val->size; i++) {
1197 val->list[i].count = 0L;
1199 get_valuables(invent);
1201 /* add points for collected valuables */
1202 for (val = valuables; val->list; val++)
1203 for (i = 0; i < val->size; i++)
1204 if (val->list[i].count != 0L) {
1205 tmp = val->list[i].count
1206 * (long) objects[val->list[i].typ].oc_cost;
1207 nowrap_add(u.urexp, tmp);
1210 /* count the points for artifacts */
1211 artifact_score(invent, TRUE, endwin);
1213 viz_array[0][0] |= IN_SIGHT; /* need visibility for naming */
1214 mtmp = mydogs;
1215 if (!done_stopprint)
1216 Strcpy(pbuf, "You");
1217 if (!Schroedingers_cat) /* check here in case disclosure was off */
1218 Schroedingers_cat = odds_and_ends(invent, CAT_CHECK);
1219 if (Schroedingers_cat) {
1220 int mhp, m_lev = adj_lev(&mons[PM_HOUSECAT]);
1221 mhp = d(m_lev, 8);
1222 nowrap_add(u.urexp, mhp);
1223 if (!done_stopprint)
1224 Strcat(eos(pbuf), " and Schroedinger's cat");
1226 if (mtmp) {
1227 while (mtmp) {
1228 if (!done_stopprint)
1229 Sprintf(eos(pbuf), " and %s", mon_nam(mtmp));
1230 if (mtmp->mtame)
1231 nowrap_add(u.urexp, mtmp->mhp);
1232 mtmp = mtmp->nmon;
1234 if (!done_stopprint)
1235 putstr(endwin, 0, pbuf);
1236 pbuf[0] = '\0';
1237 } else {
1238 if (!done_stopprint)
1239 Strcat(pbuf, " ");
1241 if (!done_stopprint) {
1242 Sprintf(eos(pbuf), "%s with %ld point%s,",
1243 how == ASCENDED ? "went to your reward"
1244 : "escaped from the dungeon",
1245 u.urexp, plur(u.urexp));
1246 putstr(endwin, 0, pbuf);
1249 if (!done_stopprint)
1250 artifact_score(invent, FALSE, endwin); /* list artifacts */
1252 /* list valuables here */
1253 for (val = valuables; val->list; val++) {
1254 sort_valuables(val->list, val->size);
1255 for (i = 0; i < val->size && !done_stopprint; i++) {
1256 int typ = val->list[i].typ;
1257 long count = val->list[i].count;
1259 if (count == 0L)
1260 continue;
1261 if (objects[typ].oc_class != GEM_CLASS || typ <= LAST_GEM) {
1262 otmp = mksobj(typ, FALSE, FALSE);
1263 makeknown(otmp->otyp);
1264 otmp->known = 1; /* for fake amulets */
1265 otmp->dknown = 1; /* seen it (blindness fix) */
1266 if (has_oname(otmp))
1267 free_oname(otmp);
1268 otmp->quan = count;
1269 Sprintf(pbuf, "%8ld %s (worth %ld %s),", count,
1270 xname(otmp), count * (long) objects[typ].oc_cost,
1271 currency(2L));
1272 obfree(otmp, (struct obj *) 0);
1273 } else {
1274 Sprintf(pbuf, "%8ld worthless piece%s of colored glass,",
1275 count, plur(count));
1277 putstr(endwin, 0, pbuf);
1281 } else if (!done_stopprint) {
1282 /* did not escape or ascend */
1283 if (u.uz.dnum == 0 && u.uz.dlevel <= 0) {
1284 /* level teleported out of the dungeon; `how' is DIED,
1285 due to falling or to "arriving at heaven prematurely" */
1286 Sprintf(pbuf, "You %s beyond the confines of the dungeon",
1287 (u.uz.dlevel < 0) ? "passed away" : ends[how]);
1288 } else {
1289 /* more conventional demise */
1290 const char *where = dungeons[u.uz.dnum].dname;
1292 if (Is_astralevel(&u.uz))
1293 where = "The Astral Plane";
1294 Sprintf(pbuf, "You %s in %s", ends[how], where);
1295 if (!In_endgame(&u.uz) && !Is_knox(&u.uz))
1296 Sprintf(eos(pbuf), " on dungeon level %d",
1297 In_quest(&u.uz) ? dunlev(&u.uz) : depth(&u.uz));
1300 Sprintf(eos(pbuf), " with %ld point%s,", u.urexp, plur(u.urexp));
1301 putstr(endwin, 0, pbuf);
1304 if (!done_stopprint) {
1305 Sprintf(pbuf, "and %ld piece%s of gold, after %ld move%s.", umoney,
1306 plur(umoney), moves, plur(moves));
1307 putstr(endwin, 0, pbuf);
1309 if (!done_stopprint) {
1310 Sprintf(pbuf,
1311 "You were level %d with a maximum of %d hit point%s when you %s.",
1312 u.ulevel, u.uhpmax, plur(u.uhpmax), ends[how]);
1313 putstr(endwin, 0, pbuf);
1314 putstr(endwin, 0, "");
1316 if (!done_stopprint)
1317 display_nhwindow(endwin, TRUE);
1318 if (endwin != WIN_ERR)
1319 destroy_nhwindow(endwin);
1321 /* "So when I die, the first thing I will see in Heaven is a
1322 * score list?" */
1323 if (have_windows && !iflags.toptenwin)
1324 exit_nhwindows((char *) 0), have_windows = FALSE;
1325 topten(how, endtime);
1326 if (have_windows)
1327 exit_nhwindows((char *) 0);
1329 if (done_stopprint) {
1330 raw_print("");
1331 raw_print("");
1333 terminate(EXIT_SUCCESS);
1336 void
1337 container_contents(list, identified, all_containers, reportempty)
1338 struct obj *list;
1339 boolean identified, all_containers, reportempty;
1341 register struct obj *box, *obj;
1342 char buf[BUFSZ];
1343 boolean cat, deadcat;
1345 for (box = list; box; box = box->nobj) {
1346 if (Is_container(box) || box->otyp == STATUE) {
1347 box->cknown = 1; /* we're looking at the contents now */
1348 if (identified)
1349 box->lknown = 1;
1350 cat = deadcat = FALSE;
1351 if (SchroedingersBox(box) && !Schroedingers_cat) {
1352 /* Schroedinger's Cat? */
1353 cat = odds_and_ends(box, CAT_CHECK);
1354 if (cat)
1355 Schroedingers_cat = TRUE;
1356 else
1357 deadcat = TRUE;
1358 box->spe = 0;
1360 if (box->otyp == BAG_OF_TRICKS) {
1361 continue; /* wrong type of container */
1362 } else if (box->cobj) {
1363 winid tmpwin = create_nhwindow(NHW_MENU);
1365 sortloot(&box->cobj,
1366 (((flags.sortloot == 'l' || flags.sortloot == 'f')
1367 ? SORTLOOT_LOOT : 0)
1368 | (flags.sortpack ? SORTLOOT_PACK : 0)),
1369 FALSE);
1370 Sprintf(buf, "Contents of %s:", the(xname(box)));
1371 putstr(tmpwin, 0, buf);
1372 putstr(tmpwin, 0, "");
1373 for (obj = box->cobj; obj; obj = obj->nobj) {
1374 if (identified) {
1375 makeknown(obj->otyp);
1376 obj->known = obj->bknown = obj->dknown
1377 = obj->rknown = 1;
1378 if (Is_container(obj) || obj->otyp == STATUE)
1379 obj->cknown = obj->lknown = 1;
1381 putstr(tmpwin, 0, doname(obj));
1383 if (cat)
1384 putstr(tmpwin, 0, "Schroedinger's cat");
1385 else if (deadcat)
1386 putstr(tmpwin, 0, "Schroedinger's dead cat");
1387 display_nhwindow(tmpwin, TRUE);
1388 destroy_nhwindow(tmpwin);
1389 if (all_containers)
1390 container_contents(box->cobj, identified, TRUE,
1391 reportempty);
1392 } else if (cat || deadcat) {
1393 pline("%s Schroedinger's %scat!", Tobjnam(box, "contain"),
1394 deadcat ? "dead " : "");
1395 display_nhwindow(WIN_MESSAGE, FALSE);
1396 } else if (reportempty) {
1397 pline("%s is empty.", upstart(thesimpleoname(box)));
1398 display_nhwindow(WIN_MESSAGE, FALSE);
1401 if (!all_containers)
1402 break;
1406 /* should be called with either EXIT_SUCCESS or EXIT_FAILURE */
1407 void
1408 terminate(status)
1409 int status;
1411 program_state.in_moveloop = 0; /* won't be returning to normal play */
1412 #ifdef MAC
1413 getreturn("to exit");
1414 #endif
1415 /* don't bother to try to release memory if we're in panic mode, to
1416 avoid trouble in case that happens to be due to memory problems */
1417 if (!program_state.panicking) {
1418 freedynamicdata();
1419 dlb_cleanup();
1422 #ifdef VMS
1424 * This is liable to draw a warning if compiled with gcc, but it's
1425 * more important to flag panic() -> really_done() -> terminate()
1426 * as __noreturn__ then to avoid the warning.
1428 /* don't call exit() if already executing within an exit handler;
1429 that would cancel any other pending user-mode handlers */
1430 if (program_state.exiting)
1431 return;
1432 #endif
1433 program_state.exiting = 1;
1434 nethack_exit(status);
1437 extern const int monstr[];
1439 static const char *vanqorders[] = {
1440 "traditional: by monster level, by internal monster index",
1441 #define VANQ_MLVL_MNDX 0
1442 "by monster toughness, by internal monster index",
1443 #define VANQ_MSTR_MNDX 1
1444 "alphabetically, first unique monsters, then others",
1445 #define VANQ_ALPHA_SEP 2
1446 "alphabetically, unique monsters and others intermixed",
1447 #define VANQ_ALPHA_MIX 3
1448 "by monster class, high to low level within class",
1449 #define VANQ_MCLS_HTOL 4
1450 "by monster class, low to high level within class",
1451 #define VANQ_MCLS_LTOH 5
1452 "by count, high to low, by internal index within tied count",
1453 #define VANQ_COUNT_H_L 6
1454 "by count, low to high, by internal index within tied count",
1455 #define VANQ_COUNT_L_H 7
1457 static int vanq_sortmode = VANQ_MLVL_MNDX;
1459 STATIC_PTR int CFDECLSPEC
1460 vanqsort_cmp(vptr1, vptr2)
1461 const genericptr vptr1;
1462 const genericptr vptr2;
1464 int indx1 = *(short *) vptr1, indx2 = *(short *) vptr2,
1465 mlev1, mlev2, mstr1, mstr2, uniq1, uniq2, died1, died2, res;
1466 const char *name1, *name2, *punct;
1467 schar mcls1, mcls2;
1469 switch (vanq_sortmode) {
1470 default:
1471 case VANQ_MLVL_MNDX:
1472 /* sort by monster level */
1473 mlev1 = mons[indx1].mlevel, mlev2 = mons[indx2].mlevel;
1474 res = mlev2 - mlev1; /* mlevel high to low */
1475 break;
1476 case VANQ_MSTR_MNDX:
1477 /* sort by monster toughness */
1478 mstr1 = monstr[indx1], mstr2 = monstr[indx2];
1479 res = mstr2 - mstr1; /* monstr high to low */
1480 break;
1481 case VANQ_ALPHA_SEP:
1482 uniq1 = ((mons[indx1].geno & G_UNIQ) && indx1 != PM_HIGH_PRIEST);
1483 uniq2 = ((mons[indx2].geno & G_UNIQ) && indx2 != PM_HIGH_PRIEST);
1484 if (uniq1 ^ uniq2) { /* one or other uniq, but not both */
1485 res = uniq2 - uniq1;
1486 break;
1487 } /* else both unique or neither unique */
1488 /*FALLTHRU*/
1489 case VANQ_ALPHA_MIX:
1490 name1 = mons[indx1].mname, name2 = mons[indx2].mname;
1491 res = strcmpi(name1, name2); /* caseblind alhpa, low to high */
1492 break;
1493 case VANQ_MCLS_HTOL:
1494 case VANQ_MCLS_LTOH:
1495 /* mons[].mlet is a small integer, 1..N, of type plain char;
1496 if 'char' happens to be unsigned, (mlet1 - mlet2) would yield
1497 an inappropriate result when mlet2 is greater than mlet1,
1498 so force our copies (mcls1, mcls2) to be signed */
1499 mcls1 = (schar) mons[indx1].mlet, mcls2 = (schar) mons[indx2].mlet;
1500 /* S_ANT through S_ZRUTY correspond to lowercase monster classes,
1501 S_ANGEL through S_ZOMBIE correspond to uppercase, and various
1502 punctuation characters are used for classes beyond those */
1503 if (mcls1 > S_ZOMBIE && mcls2 > S_ZOMBIE) {
1504 /* force a specific order to the punctuation classes that's
1505 different from the internal order;
1506 internal order is ok if neither or just one is punctuation
1507 since letters have lower values so come out before punct */
1508 static const char punctclasses[] = {
1509 S_LIZARD, S_EEL, S_GOLEM, S_GHOST, S_DEMON, S_HUMAN, '\0'
1512 if ((punct = index(punctclasses, mcls1)) != 0)
1513 mcls1 = (schar) (S_ZOMBIE + 1 + (int) (punct - punctclasses));
1514 if ((punct = index(punctclasses, mcls2)) != 0)
1515 mcls2 = (schar) (S_ZOMBIE + 1 + (int) (punct - punctclasses));
1517 res = mcls1 - mcls2; /* class */
1518 if (res == 0) {
1519 mlev1 = mons[indx1].mlevel, mlev2 = mons[indx2].mlevel;
1520 res = mlev1 - mlev2; /* mlevel low to high */
1521 if (vanq_sortmode == VANQ_MCLS_HTOL)
1522 res = -res; /* mlevel high to low */
1524 break;
1525 case VANQ_COUNT_H_L:
1526 case VANQ_COUNT_L_H:
1527 died1 = mvitals[indx1].died, died2 = mvitals[indx2].died;
1528 res = died2 - died1; /* dead count high to low */
1529 if (vanq_sortmode == VANQ_COUNT_L_H)
1530 res = -res; /* dead count low to high */
1531 break;
1533 /* tiebreaker: internal mons[] index */
1534 if (res == 0)
1535 res = indx1 - indx2; /* mndx low to high */
1536 return res;
1539 /* returns -1 if cancelled via ESC */
1540 STATIC_OVL int
1541 set_vanq_order()
1543 winid tmpwin;
1544 menu_item *selected;
1545 anything any;
1546 int i, n, choice;
1548 tmpwin = create_nhwindow(NHW_MENU);
1549 start_menu(tmpwin);
1550 any = zeroany; /* zero out all bits */
1551 for (i = 0; i < SIZE(vanqorders); i++) {
1552 if (i == VANQ_ALPHA_MIX || i == VANQ_MCLS_HTOL) /* skip these */
1553 continue;
1554 any.a_int = i + 1;
1555 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, vanqorders[i],
1556 (i == vanq_sortmode) ? MENU_SELECTED : MENU_UNSELECTED);
1558 end_menu(tmpwin, "Sort order for vanquished monster counts");
1560 n = select_menu(tmpwin, PICK_ONE, &selected);
1561 destroy_nhwindow(tmpwin);
1562 if (n > 0) {
1563 choice = selected[0].item.a_int - 1;
1564 /* skip preselected entry if we have more than one item chosen */
1565 if (n > 1 && choice == vanq_sortmode)
1566 choice = selected[1].item.a_int - 1;
1567 free((genericptr_t) selected);
1568 vanq_sortmode = choice;
1570 return (n < 0) ? -1 : vanq_sortmode;
1573 /* #vanquished command */
1575 dovanquished()
1577 list_vanquished('a', FALSE);
1578 return 0;
1581 /* high priests aren't unique but are flagged as such to simplify something */
1582 #define UniqCritterIndx(mndx) ((mons[mndx].geno & G_UNIQ) \
1583 && mndx != PM_HIGH_PRIEST)
1585 STATIC_OVL void
1586 list_vanquished(defquery, ask)
1587 char defquery;
1588 boolean ask;
1590 register int i;
1591 int pfx, nkilled;
1592 unsigned ntypes, ni;
1593 long total_killed = 0L;
1594 winid klwin;
1595 short mindx[NUMMONS];
1596 char c, buf[BUFSZ], buftoo[BUFSZ];
1598 /* get totals first */
1599 ntypes = 0;
1600 for (i = LOW_PM; i < NUMMONS; i++) {
1601 if ((nkilled = (int) mvitals[i].died) == 0)
1602 continue;
1603 mindx[ntypes++] = i;
1604 total_killed += (long) nkilled;
1607 /* vanquished creatures list;
1608 * includes all dead monsters, not just those killed by the player
1610 if (ntypes != 0) {
1611 char mlet, prev_mlet = 0; /* used as small integer, not character */
1612 boolean class_header, uniq_header, was_uniq = FALSE;
1614 c = ask ? yn_function(
1615 "Do you want an account of creatures vanquished?",
1616 ynaqchars, defquery)
1617 : defquery;
1618 if (c == 'q')
1619 done_stopprint++;
1620 if (c == 'y' || c == 'a') {
1621 if (c == 'a') { /* ask player to choose sort order */
1622 /* choose value for vanq_sortmode via menu; ESC cancels list
1623 of vanquished monsters but does not set 'done_stopprint' */
1624 if (set_vanq_order() < 0)
1625 return;
1627 uniq_header = (vanq_sortmode == VANQ_ALPHA_SEP);
1628 class_header = (vanq_sortmode == VANQ_MCLS_LTOH
1629 || vanq_sortmode == VANQ_MCLS_HTOL);
1631 klwin = create_nhwindow(NHW_MENU);
1632 putstr(klwin, 0, "Vanquished creatures:");
1633 putstr(klwin, 0, "");
1635 qsort((genericptr_t) mindx, ntypes, sizeof *mindx, vanqsort_cmp);
1636 for (ni = 0; ni < ntypes; ni++) {
1637 i = mindx[ni];
1638 nkilled = mvitals[i].died;
1639 mlet = mons[i].mlet;
1640 if (class_header && mlet != prev_mlet) {
1641 Strcpy(buf, def_monsyms[(int) mlet].explain);
1642 putstr(klwin, ask ? 0 : iflags.menu_headings,
1643 upstart(buf));
1644 prev_mlet = mlet;
1646 if (UniqCritterIndx(i)) {
1647 Sprintf(buf, "%s%s",
1648 !type_is_pname(&mons[i]) ? "the " : "",
1649 mons[i].mname);
1650 if (nkilled > 1) {
1651 switch (nkilled) {
1652 case 2:
1653 Sprintf(eos(buf), " (twice)");
1654 break;
1655 case 3:
1656 Sprintf(eos(buf), " (thrice)");
1657 break;
1658 default:
1659 Sprintf(eos(buf), " (%d times)", nkilled);
1660 break;
1663 was_uniq = TRUE;
1664 } else {
1665 if (uniq_header && was_uniq) {
1666 putstr(klwin, 0, "");
1667 was_uniq = FALSE;
1669 /* trolls or undead might have come back,
1670 but we don't keep track of that */
1671 if (nkilled == 1)
1672 Strcpy(buf, an(mons[i].mname));
1673 else
1674 Sprintf(buf, "%3d %s", nkilled,
1675 makeplural(mons[i].mname));
1677 /* number of leading spaces to match 3 digit prefix */
1678 pfx = !strncmpi(buf, "the ", 3) ? 0
1679 : !strncmpi(buf, "an ", 3) ? 1
1680 : !strncmpi(buf, "a ", 2) ? 2
1681 : !isdigit(buf[2]) ? 4 : 0;
1682 if (class_header)
1683 ++pfx;
1684 Sprintf(buftoo, "%*s%s", pfx, "", buf);
1685 putstr(klwin, 0, buftoo);
1688 * if (Hallucination)
1689 * putstr(klwin, 0, "and a partridge in a pear tree");
1691 if (ntypes > 1) {
1692 putstr(klwin, 0, "");
1693 Sprintf(buf, "%ld creatures vanquished.", total_killed);
1694 putstr(klwin, 0, buf);
1696 display_nhwindow(klwin, TRUE);
1697 destroy_nhwindow(klwin);
1699 } else if (defquery == 'a') {
1700 /* #dovanquished rather than final disclosure, so pline() is ok */
1701 pline("No monsters have been vanquished.");
1705 /* number of monster species which have been genocided */
1707 num_genocides()
1709 int i, n = 0;
1711 for (i = LOW_PM; i < NUMMONS; ++i) {
1712 if (mvitals[i].mvflags & G_GENOD) {
1713 ++n;
1714 if (UniqCritterIndx(i))
1715 impossible("unique creature '%d: %s' genocided?",
1716 i, mons[i].mname);
1719 return n;
1723 num_extinct()
1725 int i, n = 0;
1727 for (i = LOW_PM; i < NUMMONS; ++i) {
1728 if (UniqCritterIndx(i))
1729 continue;
1730 if ((mvitals[i].mvflags & G_GONE) == G_EXTINCT)
1731 ++n;
1733 return n;
1736 STATIC_OVL void
1737 list_genocided(defquery, ask)
1738 char defquery;
1739 boolean ask;
1741 register int i;
1742 int ngenocided, nextinct;
1743 char c;
1744 winid klwin;
1745 char buf[BUFSZ];
1747 ngenocided = num_genocides();
1748 nextinct = num_extinct();
1750 /* genocided or extinct species list */
1751 if (ngenocided != 0 || nextinct != 0) {
1752 Sprintf(buf, "Do you want a list of %sspecies%s%s?",
1753 (nextinct && !ngenocided) ? "extinct " : "",
1754 (ngenocided) ? " genocided" : "",
1755 (nextinct && ngenocided) ? " and extinct" : "");
1756 c = ask ? yn_function(buf, ynqchars, defquery) : defquery;
1757 if (c == 'q')
1758 done_stopprint++;
1759 if (c == 'y') {
1760 klwin = create_nhwindow(NHW_MENU);
1761 Sprintf(buf, "%s%s species:",
1762 (ngenocided) ? "Genocided" : "Extinct",
1763 (nextinct && ngenocided) ? " or extinct" : "");
1764 putstr(klwin, 0, buf);
1765 putstr(klwin, 0, "");
1767 for (i = LOW_PM; i < NUMMONS; i++) {
1768 /* uniques can't be genocided but can become extinct;
1769 however, they're never reported as extinct, so skip them */
1770 if (UniqCritterIndx(i))
1771 continue;
1772 if (mvitals[i].mvflags & G_GONE) {
1773 Strcpy(buf, makeplural(mons[i].mname));
1775 * "Extinct" is unfortunate terminology. A species
1776 * is marked extinct when its birth limit is reached,
1777 * but there might be members of the species still
1778 * alive, contradicting the meaning of the word.
1780 if ((mvitals[i].mvflags & G_GONE) == G_EXTINCT)
1781 Strcat(buf, " (extinct)");
1782 putstr(klwin, 0, buf);
1785 putstr(klwin, 0, "");
1786 if (ngenocided > 0) {
1787 Sprintf(buf, "%d species genocided.", ngenocided);
1788 putstr(klwin, 0, buf);
1790 if (nextinct > 0) {
1791 Sprintf(buf, "%d species extinct.", nextinct);
1792 putstr(klwin, 0, buf);
1795 display_nhwindow(klwin, TRUE);
1796 destroy_nhwindow(klwin);
1801 /* set a delayed killer, ensure non-delayed killer is cleared out */
1802 void
1803 delayed_killer(id, format, killername)
1804 int id;
1805 int format;
1806 const char *killername;
1808 struct kinfo *k = find_delayed_killer(id);
1810 if (k == (struct kinfo *) 0) {
1811 /* no match, add a new delayed killer to the list */
1812 k = (struct kinfo *) alloc(sizeof(struct kinfo));
1813 k->id = id;
1814 k->next = killer.next;
1815 killer.next = k;
1818 k->format = format;
1819 Strcpy(k->name, killername ? killername : "");
1820 killer.name[0] = 0;
1823 struct kinfo *
1824 find_delayed_killer(id)
1825 int id;
1827 struct kinfo *k;
1829 for (k = killer.next; k != (struct kinfo *) 0; k = k->next) {
1830 if (k->id == id)
1831 break;
1833 return k;
1836 void
1837 dealloc_killer(kptr)
1838 struct kinfo *kptr;
1840 struct kinfo *prev = &killer, *k;
1842 if (kptr == (struct kinfo *) 0)
1843 return;
1844 for (k = killer.next; k != (struct kinfo *) 0; k = k->next) {
1845 if (k == kptr)
1846 break;
1847 prev = k;
1850 if (k == (struct kinfo *) 0) {
1851 impossible("dealloc_killer not on list");
1852 } else {
1853 prev->next = k->next;
1854 free((genericptr_t) k);
1858 void
1859 save_killers(fd, mode)
1860 int fd;
1861 int mode;
1863 struct kinfo *kptr;
1865 if (perform_bwrite(mode)) {
1866 for (kptr = &killer; kptr != (struct kinfo *) 0; kptr = kptr->next) {
1867 bwrite(fd, (genericptr_t) kptr, sizeof(struct kinfo));
1870 if (release_data(mode)) {
1871 while (killer.next) {
1872 kptr = killer.next->next;
1873 free((genericptr_t) killer.next);
1874 killer.next = kptr;
1879 void
1880 restore_killers(fd)
1881 int fd;
1883 struct kinfo *kptr;
1885 for (kptr = &killer; kptr != (struct kinfo *) 0; kptr = kptr->next) {
1886 mread(fd, (genericptr_t) kptr, sizeof(struct kinfo));
1887 if (kptr->next) {
1888 kptr->next = (struct kinfo *) alloc(sizeof(struct kinfo));
1893 static int
1894 wordcount(p)
1895 char *p;
1897 int words = 0;
1899 while (*p) {
1900 while (*p && isspace((uchar) *p))
1901 p++;
1902 if (*p)
1903 words++;
1904 while (*p && !isspace((uchar) *p))
1905 p++;
1907 return words;
1910 static void
1911 bel_copy1(inp, out)
1912 char **inp, *out;
1914 char *in = *inp;
1916 out += strlen(out); /* eos() */
1917 while (*in && isspace((uchar) *in))
1918 in++;
1919 while (*in && !isspace((uchar) *in))
1920 *out++ = *in++;
1921 *out = '\0';
1922 *inp = in;
1925 char *
1926 build_english_list(in)
1927 char *in;
1929 char *out, *p = in;
1930 int len = (int) strlen(p), words = wordcount(p);
1932 /* +3: " or " - " "; +(words - 1): (N-1)*(", " - " ") */
1933 if (words > 1)
1934 len += 3 + (words - 1);
1935 out = (char *) alloc(len + 1);
1936 *out = '\0'; /* bel_copy1() appends */
1938 switch (words) {
1939 case 0:
1940 impossible("no words in list");
1941 break;
1942 case 1:
1943 /* "single" */
1944 bel_copy1(&p, out);
1945 break;
1946 default:
1947 if (words == 2) {
1948 /* "first or second" */
1949 bel_copy1(&p, out);
1950 Strcat(out, " ");
1951 } else {
1952 /* "first, second, or third */
1953 do {
1954 bel_copy1(&p, out);
1955 Strcat(out, ", ");
1956 } while (--words > 1);
1958 Strcat(out, "or ");
1959 bel_copy1(&p, out);
1960 break;
1962 return out;
1965 /*end.c*/