Add end-of-game dumplogs
[aNetHack.git] / src / end.c
blobe2e8a13afcfb001415414a51478a621d85cdbb68
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 #ifdef DUMPLOG
671 STATIC_OVL void
672 dump_plines()
674 int i;
675 char* str;
676 extern char* saved_plines[];
678 putstr(0, 0, "");
679 putstr(0, 0, "Latest messages:");
680 for (i = 0; i < DUMPLOG_MSG_COUNT; ++i)
682 str = saved_plines[DUMPLOG_MSG_COUNT - 1 - i];
683 if (str) {
684 char buf[BUFSZ];
685 Sprintf(buf, " %s", str);
686 putstr(0, 0, buf);
688 #ifdef FREE_ALL_MEMORY
689 free(str);
690 #endif
693 #endif
695 STATIC_OVL void
696 dump_everything(how, taken)
697 int how;
698 boolean taken;
700 #ifdef DUMPLOG
701 struct obj* obj;
702 struct topl* topl;
703 char pbuf[BUFSZ];
705 dump_redirect(TRUE);
706 if (!iflags.in_dumplog)
707 return;
709 init_symbols();
711 for (obj = invent; obj; obj = obj->nobj) {
712 makeknown(obj->otyp);
713 obj->known = obj->bknown = obj->dknown = obj->rknown = 1;
714 if (Is_container(obj) || obj->otyp == STATUE)
715 obj->cknown = obj->lknown = 1;
718 Sprintf(pbuf, "%s, %s %s %s %s", plname,
719 aligns[1 - u.ualign.type].adj,
720 genders[flags.female].adj,
721 urace.adj,
722 (flags.female && urole.name.f) ? urole.name.f : urole.name.m);
723 putstr(0, 0, pbuf);
724 putstr(0, 0, "");
726 dump_map();
727 putstr(0, 0, do_statusline1());
728 putstr(0, 0, do_statusline2());
729 putstr(0, 0, "");
731 dump_plines();
732 putstr(0, 0, "");
733 putstr(0, 0, "Inventory:");
734 display_inventory((char *) 0, TRUE);
735 container_contents(invent, TRUE, TRUE, FALSE);
736 enlightenment((BASICENLIGHTENMENT | MAGICENLIGHTENMENT),
737 (how >= PANICKED) ? ENL_GAMEOVERALIVE
738 : ENL_GAMEOVERDEAD);
739 putstr(0, 0, "");
740 list_vanquished('y', FALSE);
741 putstr(0, 0, "");
742 list_genocided('a', FALSE);
743 putstr(0, 0, "");
744 show_conduct((how >= PANICKED) ? 1 : 2);
745 putstr(0, 0, "");
746 show_overview((how >= PANICKED) ? 1 : 2, how);
747 putstr(0, 0, "");
748 dump_redirect(FALSE);
749 #endif
752 STATIC_OVL void
753 disclose(how, taken)
754 int how;
755 boolean taken;
757 char c = '\0', defquery;
758 char qbuf[QBUFSZ];
759 boolean ask = FALSE;
761 if (invent && !done_stopprint) {
762 if (taken)
763 Sprintf(qbuf, "Do you want to see what you had when you %s?",
764 (how == QUIT) ? "quit" : "died");
765 else
766 Strcpy(qbuf, "Do you want your possessions identified?");
768 ask = should_query_disclose_option('i', &defquery);
769 c = ask ? yn_function(qbuf, ynqchars, defquery) : defquery;
770 if (c == 'y') {
771 struct obj *obj;
773 for (obj = invent; obj; obj = obj->nobj) {
774 makeknown(obj->otyp);
775 obj->known = obj->bknown = obj->dknown = obj->rknown = 1;
776 if (Is_container(obj) || obj->otyp == STATUE)
777 obj->cknown = obj->lknown = 1;
779 (void) display_inventory((char *) 0, TRUE);
780 container_contents(invent, TRUE, TRUE, FALSE);
782 if (c == 'q')
783 done_stopprint++;
786 if (!done_stopprint) {
787 ask = should_query_disclose_option('a', &defquery);
788 c = ask ? yn_function("Do you want to see your attributes?", ynqchars,
789 defquery)
790 : defquery;
791 if (c == 'y')
792 enlightenment((BASICENLIGHTENMENT | MAGICENLIGHTENMENT),
793 (how >= PANICKED) ? ENL_GAMEOVERALIVE
794 : ENL_GAMEOVERDEAD);
795 if (c == 'q')
796 done_stopprint++;
799 if (!done_stopprint) {
800 ask = should_query_disclose_option('v', &defquery);
801 list_vanquished(defquery, ask);
804 if (!done_stopprint) {
805 ask = should_query_disclose_option('g', &defquery);
806 list_genocided(defquery, ask);
809 if (!done_stopprint) {
810 ask = should_query_disclose_option('c', &defquery);
811 c = ask ? yn_function("Do you want to see your conduct?", ynqchars,
812 defquery)
813 : defquery;
814 if (c == 'y')
815 show_conduct((how >= PANICKED) ? 1 : 2);
816 if (c == 'q')
817 done_stopprint++;
820 if (!done_stopprint) {
821 ask = should_query_disclose_option('o', &defquery);
822 c = ask ? yn_function("Do you want to see the dungeon overview?",
823 ynqchars, defquery)
824 : defquery;
825 if (c == 'y')
826 show_overview((how >= PANICKED) ? 1 : 2, how);
827 if (c == 'q')
828 done_stopprint++;
832 /* try to get the player back in a viable state after being killed */
833 STATIC_OVL void
834 savelife(how)
835 int how;
837 int uhpmin = max(2 * u.ulevel, 10);
839 if (u.uhpmax < uhpmin)
840 u.uhpmax = uhpmin;
841 u.uhp = u.uhpmax;
842 u.uswldtim = 0;
843 if (u.uhunger < 500) {
844 u.uhunger = 500;
845 newuhs(FALSE);
847 /* cure impending doom of sickness hero won't have time to fix */
848 if ((Sick & TIMEOUT) == 1L) {
849 u.usick_type = 0;
850 set_itimeout(&Sick, 0L);
852 if (how == CHOKING)
853 init_uhunger();
854 nomovemsg = "You survived that attempt on your life.";
855 context.move = 0;
856 if (multi > 0)
857 multi = 0;
858 else
859 multi = -1;
860 if (u.utrap && u.utraptype == TT_LAVA)
861 u.utrap = 0;
862 context.botl = 1;
863 u.ugrave_arise = NON_PM;
864 HUnchanging = 0L;
865 curs_on_u();
866 if (!context.mon_moving)
867 endmultishot(FALSE);
871 * Get valuables from the given list. Revised code: the list always remains
872 * intact.
874 STATIC_OVL void
875 get_valuables(list)
876 struct obj *list; /* inventory or container contents */
878 register struct obj *obj;
879 register int i;
881 /* find amulets and gems, ignoring all artifacts */
882 for (obj = list; obj; obj = obj->nobj)
883 if (Has_contents(obj)) {
884 get_valuables(obj->cobj);
885 } else if (obj->oartifact) {
886 continue;
887 } else if (obj->oclass == AMULET_CLASS) {
888 i = obj->otyp - FIRST_AMULET;
889 if (!amulets[i].count) {
890 amulets[i].count = obj->quan;
891 amulets[i].typ = obj->otyp;
892 } else
893 amulets[i].count += obj->quan; /* always adds one */
894 } else if (obj->oclass == GEM_CLASS && obj->otyp < LUCKSTONE) {
895 i = min(obj->otyp, LAST_GEM + 1) - FIRST_GEM;
896 if (!gems[i].count) {
897 gems[i].count = obj->quan;
898 gems[i].typ = obj->otyp;
899 } else
900 gems[i].count += obj->quan;
902 return;
906 * Sort collected valuables, most frequent to least. We could just
907 * as easily use qsort, but we don't care about efficiency here.
909 STATIC_OVL void
910 sort_valuables(list, size)
911 struct valuable_data list[];
912 int size; /* max value is less than 20 */
914 register int i, j;
915 struct valuable_data ltmp;
917 /* move greater quantities to the front of the list */
918 for (i = 1; i < size; i++) {
919 if (list[i].count == 0)
920 continue; /* empty slot */
921 ltmp = list[i]; /* structure copy */
922 for (j = i; j > 0; --j)
923 if (list[j - 1].count >= ltmp.count)
924 break;
925 else {
926 list[j] = list[j - 1];
928 list[j] = ltmp;
930 return;
933 #define CAT_CHECK 2
935 STATIC_OVL boolean
936 odds_and_ends(list, what)
937 struct obj *list;
938 int what;
940 struct obj *otmp;
941 for (otmp = list; otmp; otmp = otmp->nobj) {
942 switch (what) {
943 case CAT_CHECK: /* Schroedinger's Cat */
944 /* Ascending is deterministic */
945 if (SchroedingersBox(otmp))
946 return rn2(2);
947 break;
949 if (Has_contents(otmp))
950 return odds_and_ends(otmp->cobj, what);
952 return FALSE;
955 /* called twice; first to calculate total, then to list relevant items */
956 STATIC_OVL void
957 artifact_score(list, counting, endwin)
958 struct obj *list;
959 boolean counting; /* true => add up points; false => display them */
960 winid endwin;
962 char pbuf[BUFSZ];
963 struct obj *otmp;
964 long value, points;
965 short dummy; /* object type returned by artifact_name() */
967 for (otmp = list; otmp; otmp = otmp->nobj) {
968 if (otmp->oartifact || otmp->otyp == BELL_OF_OPENING
969 || otmp->otyp == SPE_BOOK_OF_THE_DEAD
970 || otmp->otyp == CANDELABRUM_OF_INVOCATION) {
971 value = arti_cost(otmp); /* zorkmid value */
972 points = value * 5 / 2; /* score value */
973 if (counting) {
974 nowrap_add(u.urexp, points);
975 } else {
976 makeknown(otmp->otyp);
977 otmp->known = otmp->dknown = otmp->bknown = otmp->rknown = 1;
978 /* assumes artifacts don't have quan > 1 */
979 Sprintf(pbuf, "%s%s (worth %ld %s and %ld points)",
980 the_unique_obj(otmp) ? "The " : "",
981 otmp->oartifact ? artifact_name(xname(otmp), &dummy)
982 : OBJ_NAME(objects[otmp->otyp]),
983 value, currency(value), points);
984 putstr(endwin, 0, pbuf);
987 if (Has_contents(otmp))
988 artifact_score(otmp->cobj, counting, endwin);
992 /* Be careful not to call panic from here! */
993 void
994 done(how)
995 int how;
997 if (how == TRICKED) {
998 if (killer.name[0]) {
999 paniclog("trickery", killer.name);
1000 killer.name[0] = 0;
1002 if (wizard) {
1003 You("are a very tricky wizard, it seems.");
1004 return;
1007 if (program_state.panicking
1008 #ifdef HANGUPHANDLING
1009 || program_state.done_hup
1010 #endif
1012 /* skip status update if panicking or disconnected */
1013 context.botl = context.botlx = FALSE;
1014 } else {
1015 /* otherwise force full status update */
1016 context.botlx = TRUE;
1017 bot();
1020 if (how == ASCENDED || (!killer.name[0] && how == GENOCIDED))
1021 killer.format = NO_KILLER_PREFIX;
1022 /* Avoid killed by "a" burning or "a" starvation */
1023 if (!killer.name[0] && (how == STARVING || how == BURNING))
1024 killer.format = KILLED_BY;
1025 if (!killer.name[0] || how >= PANICKED)
1026 Strcpy(killer.name, deaths[how]);
1028 if (how < PANICKED)
1029 u.umortality++;
1030 if (Lifesaved && (how <= GENOCIDED)) {
1031 pline("But wait...");
1032 makeknown(AMULET_OF_LIFE_SAVING);
1033 Your("medallion %s!", !Blind ? "begins to glow" : "feels warm");
1034 if (how == CHOKING)
1035 You("vomit ...");
1036 You_feel("much better!");
1037 pline_The("medallion crumbles to dust!");
1038 if (uamul)
1039 useup(uamul);
1041 (void) adjattrib(A_CON, -1, TRUE);
1042 savelife(how);
1043 if (how == GENOCIDED) {
1044 pline("Unfortunately you are still genocided...");
1045 } else {
1046 killer.name[0] = 0;
1047 killer.format = 0;
1048 return;
1051 if ((wizard || discover) && (how <= GENOCIDED)
1052 && !paranoid_query(ParanoidDie, "Die?")) {
1053 pline("OK, so you don't %s.", (how == CHOKING) ? "choke" : "die");
1054 savelife(how);
1055 killer.name[0] = 0;
1056 killer.format = 0;
1057 return;
1059 really_done(how);
1062 /* separated from done() in order to specify the __noreturn__ attribute */
1063 STATIC_OVL void
1064 really_done(how)
1065 int how;
1067 boolean taken;
1068 char pbuf[BUFSZ];
1069 winid endwin = WIN_ERR;
1070 boolean bones_ok, have_windows = iflags.window_inited;
1071 struct obj *corpse = (struct obj *) 0;
1072 time_t endtime;
1073 long umoney;
1074 long tmp;
1077 * The game is now over...
1079 program_state.gameover = 1;
1080 /* in case of a subsequent panic(), there's no point trying to save */
1081 program_state.something_worth_saving = 0;
1082 /* render vision subsystem inoperative */
1083 iflags.vision_inited = 0;
1085 /* might have been killed while using a disposable item, so make sure
1086 it's gone prior to inventory disclosure and creation of bones data */
1087 inven_inuse(TRUE);
1088 /* maybe not on object lists; if an active light source, would cause
1089 big trouble (`obj_is_local' panic) for savebones() -> savelev() */
1090 if (thrownobj && thrownobj->where == OBJ_FREE)
1091 dealloc_obj(thrownobj);
1092 if (kickedobj && kickedobj->where == OBJ_FREE)
1093 dealloc_obj(kickedobj);
1095 /* remember time of death here instead of having bones, rip, and
1096 topten figure it out separately and possibly getting different
1097 time or even day if player is slow responding to --More-- */
1098 urealtime.finish_time = endtime = getnow();
1099 urealtime.realtime += (long) (endtime - urealtime.start_timing);
1101 dump_open_log(endtime);
1102 /* Sometimes you die on the first move. Life's not fair.
1103 * On those rare occasions you get hosed immediately, go out
1104 * smiling... :-) -3.
1106 if (moves <= 1 && how < PANICKED) /* You die... --More-- */
1107 pline("Do not pass go. Do not collect 200 %s.", currency(200L));
1109 if (have_windows)
1110 wait_synch(); /* flush screen output */
1111 #ifndef NO_SIGNAL
1112 (void) signal(SIGINT, (SIG_RET_TYPE) done_intr);
1113 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
1114 (void) signal(SIGQUIT, (SIG_RET_TYPE) done_intr);
1115 sethanguphandler(done_hangup);
1116 #endif
1117 #endif /* NO_SIGNAL */
1119 bones_ok = (how < GENOCIDED) && can_make_bones();
1121 if (bones_ok && launch_in_progress())
1122 force_launch_placement();
1124 /* maintain ugrave_arise even for !bones_ok */
1125 if (how == PANICKED)
1126 u.ugrave_arise = (NON_PM - 3); /* no corpse, no grave */
1127 else if (how == BURNING || how == DISSOLVED) /* corpse burns up too */
1128 u.ugrave_arise = (NON_PM - 2); /* leave no corpse */
1129 else if (how == STONING)
1130 u.ugrave_arise = (NON_PM - 1); /* statue instead of corpse */
1131 else if (how == TURNED_SLIME)
1132 u.ugrave_arise = PM_GREEN_SLIME;
1134 /* if pets will contribute to score, populate mydogs list now
1135 (bones creation isn't a factor, but pline() messaging is) */
1136 if (how == ESCAPED || how == ASCENDED)
1137 keepdogs(TRUE);
1139 if (how == QUIT) {
1140 killer.format = NO_KILLER_PREFIX;
1141 if (u.uhp < 1) {
1142 how = DIED;
1143 u.umortality++; /* skipped above when how==QUIT */
1144 Strcpy(killer.name, "quit while already on Charon's boat");
1147 if (how == ESCAPED || how == PANICKED)
1148 killer.format = NO_KILLER_PREFIX;
1150 fixup_death(how); /* actually, fixup multi_reason */
1152 if (how != PANICKED) {
1153 /* these affect score and/or bones, but avoid them during panic */
1154 taken = paybill((how == ESCAPED) ? -1 : (how != QUIT));
1155 paygd();
1156 clearpriests();
1157 } else
1158 taken = FALSE; /* lint; assert( !bones_ok ); */
1160 clearlocks();
1162 if (have_windows)
1163 display_nhwindow(WIN_MESSAGE, FALSE);
1165 if (strcmp(flags.end_disclose, "none") && how != PANICKED)
1166 disclose(how, taken);
1167 dump_everything(how, taken);
1169 /* finish_paybill should be called after disclosure but before bones */
1170 if (bones_ok && taken)
1171 finish_paybill();
1173 /* grave creation should be after disclosure so it doesn't have
1174 this grave in the current level's features for #overview */
1175 if (bones_ok && u.ugrave_arise == NON_PM
1176 && !(mvitals[u.umonnum].mvflags & G_NOCORPSE)) {
1177 int mnum = u.umonnum;
1179 if (!Upolyd) {
1180 /* Base corpse on race when not poly'd since original
1181 * u.umonnum is based on role, and all role monsters
1182 * are human.
1184 mnum = (flags.female && urace.femalenum != NON_PM)
1185 ? urace.femalenum
1186 : urace.malenum;
1188 corpse = mk_named_object(CORPSE, &mons[mnum], u.ux, u.uy, plname);
1189 Sprintf(pbuf, "%s, ", plname);
1190 formatkiller(eos(pbuf), sizeof pbuf - strlen(pbuf), how, TRUE);
1191 make_grave(u.ux, u.uy, pbuf);
1193 pbuf[0] = '\0'; /* clear grave text; also lint suppression */
1195 /* calculate score, before creating bones [container gold] */
1197 int deepest = deepest_lev_reached(FALSE);
1199 umoney = money_cnt(invent);
1200 tmp = u.umoney0;
1201 umoney += hidden_gold(); /* accumulate gold from containers */
1202 tmp = umoney - tmp; /* net gain */
1204 if (tmp < 0L)
1205 tmp = 0L;
1206 if (how < PANICKED)
1207 tmp -= tmp / 10L;
1208 tmp += 50L * (long) (deepest - 1);
1209 if (deepest > 20)
1210 tmp += 1000L * (long) ((deepest > 30) ? 10 : deepest - 20);
1211 nowrap_add(u.urexp, tmp);
1213 /* ascension gives a score bonus iff offering to original deity */
1214 if (how == ASCENDED && u.ualign.type == u.ualignbase[A_ORIGINAL]) {
1215 /* retaining original alignment: score *= 2;
1216 converting, then using helm-of-OA to switch back: *= 1.5 */
1217 tmp = (u.ualignbase[A_CURRENT] == u.ualignbase[A_ORIGINAL])
1218 ? u.urexp
1219 : (u.urexp / 2L);
1220 nowrap_add(u.urexp, tmp);
1224 if (u.ugrave_arise >= LOW_PM && u.ugrave_arise != PM_GREEN_SLIME) {
1225 /* give this feedback even if bones aren't going to be created,
1226 so that its presence or absence doesn't tip off the player to
1227 new bones or their lack; it might be a lie if makemon fails */
1228 Your("body rises from the dead as %s...",
1229 an(mons[u.ugrave_arise].mname));
1230 display_nhwindow(WIN_MESSAGE, FALSE);
1233 if (bones_ok) {
1234 if (!wizard || paranoid_query(ParanoidBones, "Save bones?"))
1235 savebones(how, endtime, corpse);
1236 /* corpse may be invalid pointer now so
1237 ensure that it isn't used again */
1238 corpse = (struct obj *) 0;
1241 /* update gold for the rip output, which can't use hidden_gold()
1242 (containers will be gone by then if bones just got saved...) */
1243 done_money = umoney;
1245 /* clean up unneeded windows */
1246 if (have_windows) {
1247 wait_synch();
1248 free_pickinv_cache(); /* extra persistent window if perm_invent */
1249 if (WIN_INVEN != WIN_ERR)
1250 destroy_nhwindow(WIN_INVEN), WIN_INVEN = WIN_ERR;
1251 display_nhwindow(WIN_MESSAGE, TRUE);
1252 destroy_nhwindow(WIN_MAP), WIN_MAP = WIN_ERR;
1253 #ifndef STATUS_VIA_WINDOWPORT
1254 destroy_nhwindow(WIN_STATUS), WIN_STATUS = WIN_ERR;
1255 #endif
1256 destroy_nhwindow(WIN_MESSAGE), WIN_MESSAGE = WIN_ERR;
1258 if (!done_stopprint || flags.tombstone)
1259 endwin = create_nhwindow(NHW_TEXT);
1261 if (how < GENOCIDED && flags.tombstone && endwin != WIN_ERR)
1262 outrip(endwin, how, endtime);
1263 } else
1264 done_stopprint = 1; /* just avoid any more output */
1266 #ifdef DUMPLOG
1267 dump_redirect(TRUE);
1268 genl_outrip(0, how, endtime);
1269 dump_redirect(FALSE);
1270 #endif
1271 if (u.uhave.amulet) {
1272 Strcat(killer.name, " (with the Amulet)");
1273 } else if (how == ESCAPED) {
1274 if (Is_astralevel(&u.uz)) /* offered Amulet to wrong deity */
1275 Strcat(killer.name, " (in celestial disgrace)");
1276 else if (carrying(FAKE_AMULET_OF_YENDOR))
1277 Strcat(killer.name, " (with a fake Amulet)");
1278 /* don't bother counting to see whether it should be plural */
1281 Sprintf(pbuf, "%s %s the %s...", Goodbye(), plname,
1282 (how != ASCENDED)
1283 ? (const char *) ((flags.female && urole.name.f)
1284 ? urole.name.f
1285 : urole.name.m)
1286 : (const char *) (flags.female ? "Demigoddess" : "Demigod"));
1287 dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
1288 dump_forward_putstr(endwin, 0, "", done_stopprint);
1290 if (how == ESCAPED || how == ASCENDED) {
1291 struct monst *mtmp;
1292 struct obj *otmp;
1293 register struct val_list *val;
1294 register int i;
1296 for (val = valuables; val->list; val++)
1297 for (i = 0; i < val->size; i++) {
1298 val->list[i].count = 0L;
1300 get_valuables(invent);
1302 /* add points for collected valuables */
1303 for (val = valuables; val->list; val++)
1304 for (i = 0; i < val->size; i++)
1305 if (val->list[i].count != 0L) {
1306 tmp = val->list[i].count
1307 * (long) objects[val->list[i].typ].oc_cost;
1308 nowrap_add(u.urexp, tmp);
1311 /* count the points for artifacts */
1312 artifact_score(invent, TRUE, endwin);
1313 #ifdef DUMPLOG
1314 dump_redirect(TRUE);
1315 artifact_score(invent, TRUE, endwin);
1316 dump_redirect(FALSE);
1317 #endif
1319 viz_array[0][0] |= IN_SIGHT; /* need visibility for naming */
1320 mtmp = mydogs;
1321 Strcpy(pbuf, "You");
1322 if (!Schroedingers_cat) /* check here in case disclosure was off */
1323 Schroedingers_cat = odds_and_ends(invent, CAT_CHECK);
1324 if (Schroedingers_cat) {
1325 int mhp, m_lev = adj_lev(&mons[PM_HOUSECAT]);
1326 mhp = d(m_lev, 8);
1327 nowrap_add(u.urexp, mhp);
1328 Strcat(eos(pbuf), " and Schroedinger's cat");
1330 if (mtmp) {
1331 while (mtmp) {
1332 Sprintf(eos(pbuf), " and %s", mon_nam(mtmp));
1333 if (mtmp->mtame)
1334 nowrap_add(u.urexp, mtmp->mhp);
1335 mtmp = mtmp->nmon;
1337 dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
1338 pbuf[0] = '\0';
1339 } else {
1340 Strcat(pbuf, " ");
1342 Sprintf(eos(pbuf), "%s with %ld point%s,",
1343 how == ASCENDED ? "went to your reward"
1344 : "escaped from the dungeon",
1345 u.urexp, plur(u.urexp));
1346 dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
1348 if (!done_stopprint)
1349 artifact_score(invent, FALSE, endwin); /* list artifacts */
1350 #if DUMPLOG
1351 dump_redirect(TRUE);
1352 artifact_score(invent, FALSE, 0);
1353 dump_redirect(FALSE);
1354 #endif
1356 /* list valuables here */
1357 for (val = valuables; val->list; val++) {
1358 sort_valuables(val->list, val->size);
1359 for (i = 0; i < val->size && !done_stopprint; i++) {
1360 int typ = val->list[i].typ;
1361 long count = val->list[i].count;
1363 if (count == 0L)
1364 continue;
1365 if (objects[typ].oc_class != GEM_CLASS || typ <= LAST_GEM) {
1366 otmp = mksobj(typ, FALSE, FALSE);
1367 makeknown(otmp->otyp);
1368 otmp->known = 1; /* for fake amulets */
1369 otmp->dknown = 1; /* seen it (blindness fix) */
1370 if (has_oname(otmp))
1371 free_oname(otmp);
1372 otmp->quan = count;
1373 Sprintf(pbuf, "%8ld %s (worth %ld %s),", count,
1374 xname(otmp), count * (long) objects[typ].oc_cost,
1375 currency(2L));
1376 obfree(otmp, (struct obj *) 0);
1377 } else {
1378 Sprintf(pbuf, "%8ld worthless piece%s of colored glass,",
1379 count, plur(count));
1381 dump_forward_putstr(endwin, 0, pbuf, 0);
1385 } else {
1386 /* did not escape or ascend */
1387 if (u.uz.dnum == 0 && u.uz.dlevel <= 0) {
1388 /* level teleported out of the dungeon; `how' is DIED,
1389 due to falling or to "arriving at heaven prematurely" */
1390 Sprintf(pbuf, "You %s beyond the confines of the dungeon",
1391 (u.uz.dlevel < 0) ? "passed away" : ends[how]);
1392 } else {
1393 /* more conventional demise */
1394 const char *where = dungeons[u.uz.dnum].dname;
1396 if (Is_astralevel(&u.uz))
1397 where = "The Astral Plane";
1398 Sprintf(pbuf, "You %s in %s", ends[how], where);
1399 if (!In_endgame(&u.uz) && !Is_knox(&u.uz))
1400 Sprintf(eos(pbuf), " on dungeon level %d",
1401 In_quest(&u.uz) ? dunlev(&u.uz) : depth(&u.uz));
1404 Sprintf(eos(pbuf), " with %ld point%s,", u.urexp, plur(u.urexp));
1405 dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
1408 Sprintf(pbuf, "and %ld piece%s of gold, after %ld move%s.", umoney,
1409 plur(umoney), moves, plur(moves));
1410 dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
1411 Sprintf(pbuf,
1412 "You were level %d with a maximum of %d hit point%s when you %s.",
1413 u.ulevel, u.uhpmax, plur(u.uhpmax), ends[how]);
1414 dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
1415 dump_forward_putstr(endwin, 0, "", done_stopprint);
1416 if (!done_stopprint)
1417 display_nhwindow(endwin, TRUE);
1418 if (endwin != WIN_ERR)
1419 destroy_nhwindow(endwin);
1421 dump_close_log();
1422 /* "So when I die, the first thing I will see in Heaven is a
1423 * score list?" */
1424 if (have_windows && !iflags.toptenwin)
1425 exit_nhwindows((char *) 0), have_windows = FALSE;
1426 topten(how, endtime);
1427 if (have_windows)
1428 exit_nhwindows((char *) 0);
1430 if (done_stopprint) {
1431 raw_print("");
1432 raw_print("");
1434 terminate(EXIT_SUCCESS);
1437 void
1438 container_contents(list, identified, all_containers, reportempty)
1439 struct obj *list;
1440 boolean identified, all_containers, reportempty;
1442 register struct obj *box, *obj;
1443 char buf[BUFSZ];
1444 boolean cat, deadcat;
1446 for (box = list; box; box = box->nobj) {
1447 if (Is_container(box) || box->otyp == STATUE) {
1448 box->cknown = 1; /* we're looking at the contents now */
1449 if (identified)
1450 box->lknown = 1;
1451 cat = deadcat = FALSE;
1452 if (SchroedingersBox(box) && !Schroedingers_cat) {
1453 /* Schroedinger's Cat? */
1454 cat = odds_and_ends(box, CAT_CHECK);
1455 if (cat)
1456 Schroedingers_cat = TRUE;
1457 else
1458 deadcat = TRUE;
1459 box->spe = 0;
1461 if (box->otyp == BAG_OF_TRICKS) {
1462 continue; /* wrong type of container */
1463 } else if (box->cobj) {
1464 winid tmpwin = create_nhwindow(NHW_MENU);
1466 sortloot(&box->cobj,
1467 (((flags.sortloot == 'l' || flags.sortloot == 'f')
1468 ? SORTLOOT_LOOT : 0)
1469 | (flags.sortpack ? SORTLOOT_PACK : 0)),
1470 FALSE);
1471 Sprintf(buf, "Contents of %s:", the(xname(box)));
1472 putstr(tmpwin, 0, buf);
1473 putstr(tmpwin, 0, "");
1474 for (obj = box->cobj; obj; obj = obj->nobj) {
1475 if (identified) {
1476 makeknown(obj->otyp);
1477 obj->known = obj->bknown = obj->dknown
1478 = obj->rknown = 1;
1479 if (Is_container(obj) || obj->otyp == STATUE)
1480 obj->cknown = obj->lknown = 1;
1482 putstr(tmpwin, 0, doname(obj));
1484 if (cat)
1485 putstr(tmpwin, 0, "Schroedinger's cat");
1486 else if (deadcat)
1487 putstr(tmpwin, 0, "Schroedinger's dead cat");
1488 display_nhwindow(tmpwin, TRUE);
1489 destroy_nhwindow(tmpwin);
1490 if (all_containers)
1491 container_contents(box->cobj, identified, TRUE,
1492 reportempty);
1493 } else if (cat || deadcat) {
1494 pline("%s Schroedinger's %scat!", Tobjnam(box, "contain"),
1495 deadcat ? "dead " : "");
1496 display_nhwindow(WIN_MESSAGE, FALSE);
1497 } else if (reportempty) {
1498 pline("%s is empty.", upstart(thesimpleoname(box)));
1499 display_nhwindow(WIN_MESSAGE, FALSE);
1502 if (!all_containers)
1503 break;
1507 /* should be called with either EXIT_SUCCESS or EXIT_FAILURE */
1508 void
1509 terminate(status)
1510 int status;
1512 program_state.in_moveloop = 0; /* won't be returning to normal play */
1513 #ifdef MAC
1514 getreturn("to exit");
1515 #endif
1516 /* don't bother to try to release memory if we're in panic mode, to
1517 avoid trouble in case that happens to be due to memory problems */
1518 if (!program_state.panicking) {
1519 freedynamicdata();
1520 dlb_cleanup();
1523 #ifdef VMS
1525 * This is liable to draw a warning if compiled with gcc, but it's
1526 * more important to flag panic() -> really_done() -> terminate()
1527 * as __noreturn__ then to avoid the warning.
1529 /* don't call exit() if already executing within an exit handler;
1530 that would cancel any other pending user-mode handlers */
1531 if (program_state.exiting)
1532 return;
1533 #endif
1534 program_state.exiting = 1;
1535 nethack_exit(status);
1538 extern const int monstr[];
1540 static const char *vanqorders[] = {
1541 "traditional: by monster level, by internal monster index",
1542 #define VANQ_MLVL_MNDX 0
1543 "by monster toughness, by internal monster index",
1544 #define VANQ_MSTR_MNDX 1
1545 "alphabetically, first unique monsters, then others",
1546 #define VANQ_ALPHA_SEP 2
1547 "alphabetically, unique monsters and others intermixed",
1548 #define VANQ_ALPHA_MIX 3
1549 "by monster class, high to low level within class",
1550 #define VANQ_MCLS_HTOL 4
1551 "by monster class, low to high level within class",
1552 #define VANQ_MCLS_LTOH 5
1553 "by count, high to low, by internal index within tied count",
1554 #define VANQ_COUNT_H_L 6
1555 "by count, low to high, by internal index within tied count",
1556 #define VANQ_COUNT_L_H 7
1558 static int vanq_sortmode = VANQ_MLVL_MNDX;
1560 STATIC_PTR int CFDECLSPEC
1561 vanqsort_cmp(vptr1, vptr2)
1562 const genericptr vptr1;
1563 const genericptr vptr2;
1565 int indx1 = *(short *) vptr1, indx2 = *(short *) vptr2,
1566 mlev1, mlev2, mstr1, mstr2, uniq1, uniq2, died1, died2, res;
1567 const char *name1, *name2, *punct;
1568 schar mcls1, mcls2;
1570 switch (vanq_sortmode) {
1571 default:
1572 case VANQ_MLVL_MNDX:
1573 /* sort by monster level */
1574 mlev1 = mons[indx1].mlevel, mlev2 = mons[indx2].mlevel;
1575 res = mlev2 - mlev1; /* mlevel high to low */
1576 break;
1577 case VANQ_MSTR_MNDX:
1578 /* sort by monster toughness */
1579 mstr1 = monstr[indx1], mstr2 = monstr[indx2];
1580 res = mstr2 - mstr1; /* monstr high to low */
1581 break;
1582 case VANQ_ALPHA_SEP:
1583 uniq1 = ((mons[indx1].geno & G_UNIQ) && indx1 != PM_HIGH_PRIEST);
1584 uniq2 = ((mons[indx2].geno & G_UNIQ) && indx2 != PM_HIGH_PRIEST);
1585 if (uniq1 ^ uniq2) { /* one or other uniq, but not both */
1586 res = uniq2 - uniq1;
1587 break;
1588 } /* else both unique or neither unique */
1589 /*FALLTHRU*/
1590 case VANQ_ALPHA_MIX:
1591 name1 = mons[indx1].mname, name2 = mons[indx2].mname;
1592 res = strcmpi(name1, name2); /* caseblind alhpa, low to high */
1593 break;
1594 case VANQ_MCLS_HTOL:
1595 case VANQ_MCLS_LTOH:
1596 /* mons[].mlet is a small integer, 1..N, of type plain char;
1597 if 'char' happens to be unsigned, (mlet1 - mlet2) would yield
1598 an inappropriate result when mlet2 is greater than mlet1,
1599 so force our copies (mcls1, mcls2) to be signed */
1600 mcls1 = (schar) mons[indx1].mlet, mcls2 = (schar) mons[indx2].mlet;
1601 /* S_ANT through S_ZRUTY correspond to lowercase monster classes,
1602 S_ANGEL through S_ZOMBIE correspond to uppercase, and various
1603 punctuation characters are used for classes beyond those */
1604 if (mcls1 > S_ZOMBIE && mcls2 > S_ZOMBIE) {
1605 /* force a specific order to the punctuation classes that's
1606 different from the internal order;
1607 internal order is ok if neither or just one is punctuation
1608 since letters have lower values so come out before punct */
1609 static const char punctclasses[] = {
1610 S_LIZARD, S_EEL, S_GOLEM, S_GHOST, S_DEMON, S_HUMAN, '\0'
1613 if ((punct = index(punctclasses, mcls1)) != 0)
1614 mcls1 = (schar) (S_ZOMBIE + 1 + (int) (punct - punctclasses));
1615 if ((punct = index(punctclasses, mcls2)) != 0)
1616 mcls2 = (schar) (S_ZOMBIE + 1 + (int) (punct - punctclasses));
1618 res = mcls1 - mcls2; /* class */
1619 if (res == 0) {
1620 mlev1 = mons[indx1].mlevel, mlev2 = mons[indx2].mlevel;
1621 res = mlev1 - mlev2; /* mlevel low to high */
1622 if (vanq_sortmode == VANQ_MCLS_HTOL)
1623 res = -res; /* mlevel high to low */
1625 break;
1626 case VANQ_COUNT_H_L:
1627 case VANQ_COUNT_L_H:
1628 died1 = mvitals[indx1].died, died2 = mvitals[indx2].died;
1629 res = died2 - died1; /* dead count high to low */
1630 if (vanq_sortmode == VANQ_COUNT_L_H)
1631 res = -res; /* dead count low to high */
1632 break;
1634 /* tiebreaker: internal mons[] index */
1635 if (res == 0)
1636 res = indx1 - indx2; /* mndx low to high */
1637 return res;
1640 /* returns -1 if cancelled via ESC */
1641 STATIC_OVL int
1642 set_vanq_order()
1644 winid tmpwin;
1645 menu_item *selected;
1646 anything any;
1647 int i, n, choice;
1649 tmpwin = create_nhwindow(NHW_MENU);
1650 start_menu(tmpwin);
1651 any = zeroany; /* zero out all bits */
1652 for (i = 0; i < SIZE(vanqorders); i++) {
1653 if (i == VANQ_ALPHA_MIX || i == VANQ_MCLS_HTOL) /* skip these */
1654 continue;
1655 any.a_int = i + 1;
1656 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, vanqorders[i],
1657 (i == vanq_sortmode) ? MENU_SELECTED : MENU_UNSELECTED);
1659 end_menu(tmpwin, "Sort order for vanquished monster counts");
1661 n = select_menu(tmpwin, PICK_ONE, &selected);
1662 destroy_nhwindow(tmpwin);
1663 if (n > 0) {
1664 choice = selected[0].item.a_int - 1;
1665 /* skip preselected entry if we have more than one item chosen */
1666 if (n > 1 && choice == vanq_sortmode)
1667 choice = selected[1].item.a_int - 1;
1668 free((genericptr_t) selected);
1669 vanq_sortmode = choice;
1671 return (n < 0) ? -1 : vanq_sortmode;
1674 /* #vanquished command */
1676 dovanquished()
1678 list_vanquished('a', FALSE);
1679 return 0;
1682 /* high priests aren't unique but are flagged as such to simplify something */
1683 #define UniqCritterIndx(mndx) ((mons[mndx].geno & G_UNIQ) \
1684 && mndx != PM_HIGH_PRIEST)
1686 STATIC_OVL void
1687 list_vanquished(defquery, ask)
1688 char defquery;
1689 boolean ask;
1691 register int i;
1692 int pfx, nkilled;
1693 unsigned ntypes, ni;
1694 long total_killed = 0L;
1695 winid klwin;
1696 short mindx[NUMMONS];
1697 char c, buf[BUFSZ], buftoo[BUFSZ];
1699 /* get totals first */
1700 ntypes = 0;
1701 for (i = LOW_PM; i < NUMMONS; i++) {
1702 if ((nkilled = (int) mvitals[i].died) == 0)
1703 continue;
1704 mindx[ntypes++] = i;
1705 total_killed += (long) nkilled;
1708 /* vanquished creatures list;
1709 * includes all dead monsters, not just those killed by the player
1711 if (ntypes != 0) {
1712 char mlet, prev_mlet = 0; /* used as small integer, not character */
1713 boolean class_header, uniq_header, was_uniq = FALSE;
1715 c = ask ? yn_function(
1716 "Do you want an account of creatures vanquished?",
1717 ynaqchars, defquery)
1718 : defquery;
1719 if (c == 'q')
1720 done_stopprint++;
1721 if (c == 'y' || c == 'a') {
1722 if (c == 'a') { /* ask player to choose sort order */
1723 /* choose value for vanq_sortmode via menu; ESC cancels list
1724 of vanquished monsters but does not set 'done_stopprint' */
1725 if (set_vanq_order() < 0)
1726 return;
1728 uniq_header = (vanq_sortmode == VANQ_ALPHA_SEP);
1729 class_header = (vanq_sortmode == VANQ_MCLS_LTOH
1730 || vanq_sortmode == VANQ_MCLS_HTOL);
1732 klwin = create_nhwindow(NHW_MENU);
1733 putstr(klwin, 0, "Vanquished creatures:");
1734 putstr(klwin, 0, "");
1736 qsort((genericptr_t) mindx, ntypes, sizeof *mindx, vanqsort_cmp);
1737 for (ni = 0; ni < ntypes; ni++) {
1738 i = mindx[ni];
1739 nkilled = mvitals[i].died;
1740 mlet = mons[i].mlet;
1741 if (class_header && mlet != prev_mlet) {
1742 Strcpy(buf, def_monsyms[(int) mlet].explain);
1743 putstr(klwin, ask ? 0 : iflags.menu_headings,
1744 upstart(buf));
1745 prev_mlet = mlet;
1747 if (UniqCritterIndx(i)) {
1748 Sprintf(buf, "%s%s",
1749 !type_is_pname(&mons[i]) ? "the " : "",
1750 mons[i].mname);
1751 if (nkilled > 1) {
1752 switch (nkilled) {
1753 case 2:
1754 Sprintf(eos(buf), " (twice)");
1755 break;
1756 case 3:
1757 Sprintf(eos(buf), " (thrice)");
1758 break;
1759 default:
1760 Sprintf(eos(buf), " (%d times)", nkilled);
1761 break;
1764 was_uniq = TRUE;
1765 } else {
1766 if (uniq_header && was_uniq) {
1767 putstr(klwin, 0, "");
1768 was_uniq = FALSE;
1770 /* trolls or undead might have come back,
1771 but we don't keep track of that */
1772 if (nkilled == 1)
1773 Strcpy(buf, an(mons[i].mname));
1774 else
1775 Sprintf(buf, "%3d %s", nkilled,
1776 makeplural(mons[i].mname));
1778 /* number of leading spaces to match 3 digit prefix */
1779 pfx = !strncmpi(buf, "the ", 3) ? 0
1780 : !strncmpi(buf, "an ", 3) ? 1
1781 : !strncmpi(buf, "a ", 2) ? 2
1782 : !digit(buf[2]) ? 4 : 0;
1783 if (class_header)
1784 ++pfx;
1785 Sprintf(buftoo, "%*s%s", pfx, "", buf);
1786 putstr(klwin, 0, buftoo);
1789 * if (Hallucination)
1790 * putstr(klwin, 0, "and a partridge in a pear tree");
1792 if (ntypes > 1) {
1793 putstr(klwin, 0, "");
1794 Sprintf(buf, "%ld creatures vanquished.", total_killed);
1795 putstr(klwin, 0, buf);
1797 display_nhwindow(klwin, TRUE);
1798 destroy_nhwindow(klwin);
1800 } else if (defquery == 'a') {
1801 /* #dovanquished rather than final disclosure, so pline() is ok */
1802 pline("No monsters have been vanquished.");
1806 /* number of monster species which have been genocided */
1808 num_genocides()
1810 int i, n = 0;
1812 for (i = LOW_PM; i < NUMMONS; ++i) {
1813 if (mvitals[i].mvflags & G_GENOD) {
1814 ++n;
1815 if (UniqCritterIndx(i))
1816 impossible("unique creature '%d: %s' genocided?",
1817 i, mons[i].mname);
1820 return n;
1824 num_extinct()
1826 int i, n = 0;
1828 for (i = LOW_PM; i < NUMMONS; ++i) {
1829 if (UniqCritterIndx(i))
1830 continue;
1831 if ((mvitals[i].mvflags & G_GONE) == G_EXTINCT)
1832 ++n;
1834 return n;
1837 STATIC_OVL void
1838 list_genocided(defquery, ask)
1839 char defquery;
1840 boolean ask;
1842 register int i;
1843 int ngenocided, nextinct;
1844 char c;
1845 winid klwin;
1846 char buf[BUFSZ];
1848 ngenocided = num_genocides();
1849 nextinct = num_extinct();
1851 /* genocided or extinct species list */
1852 if (ngenocided != 0 || nextinct != 0) {
1853 Sprintf(buf, "Do you want a list of %sspecies%s%s?",
1854 (nextinct && !ngenocided) ? "extinct " : "",
1855 (ngenocided) ? " genocided" : "",
1856 (nextinct && ngenocided) ? " and extinct" : "");
1857 c = ask ? yn_function(buf, ynqchars, defquery) : defquery;
1858 if (c == 'q')
1859 done_stopprint++;
1860 if (c == 'y') {
1861 klwin = create_nhwindow(NHW_MENU);
1862 Sprintf(buf, "%s%s species:",
1863 (ngenocided) ? "Genocided" : "Extinct",
1864 (nextinct && ngenocided) ? " or extinct" : "");
1865 putstr(klwin, 0, buf);
1866 putstr(klwin, 0, "");
1868 for (i = LOW_PM; i < NUMMONS; i++) {
1869 /* uniques can't be genocided but can become extinct;
1870 however, they're never reported as extinct, so skip them */
1871 if (UniqCritterIndx(i))
1872 continue;
1873 if (mvitals[i].mvflags & G_GONE) {
1874 Strcpy(buf, makeplural(mons[i].mname));
1876 * "Extinct" is unfortunate terminology. A species
1877 * is marked extinct when its birth limit is reached,
1878 * but there might be members of the species still
1879 * alive, contradicting the meaning of the word.
1881 if ((mvitals[i].mvflags & G_GONE) == G_EXTINCT)
1882 Strcat(buf, " (extinct)");
1883 putstr(klwin, 0, buf);
1886 putstr(klwin, 0, "");
1887 if (ngenocided > 0) {
1888 Sprintf(buf, "%d species genocided.", ngenocided);
1889 putstr(klwin, 0, buf);
1891 if (nextinct > 0) {
1892 Sprintf(buf, "%d species extinct.", nextinct);
1893 putstr(klwin, 0, buf);
1896 display_nhwindow(klwin, TRUE);
1897 destroy_nhwindow(klwin);
1902 /* set a delayed killer, ensure non-delayed killer is cleared out */
1903 void
1904 delayed_killer(id, format, killername)
1905 int id;
1906 int format;
1907 const char *killername;
1909 struct kinfo *k = find_delayed_killer(id);
1911 if (k == (struct kinfo *) 0) {
1912 /* no match, add a new delayed killer to the list */
1913 k = (struct kinfo *) alloc(sizeof(struct kinfo));
1914 (void) memset((genericptr_t)k, 0, sizeof(struct kinfo));
1915 k->id = id;
1916 k->next = killer.next;
1917 killer.next = k;
1920 k->format = format;
1921 Strcpy(k->name, killername ? killername : "");
1922 killer.name[0] = 0;
1925 struct kinfo *
1926 find_delayed_killer(id)
1927 int id;
1929 struct kinfo *k;
1931 for (k = killer.next; k != (struct kinfo *) 0; k = k->next) {
1932 if (k->id == id)
1933 break;
1935 return k;
1938 void
1939 dealloc_killer(kptr)
1940 struct kinfo *kptr;
1942 struct kinfo *prev = &killer, *k;
1944 if (kptr == (struct kinfo *) 0)
1945 return;
1946 for (k = killer.next; k != (struct kinfo *) 0; k = k->next) {
1947 if (k == kptr)
1948 break;
1949 prev = k;
1952 if (k == (struct kinfo *) 0) {
1953 impossible("dealloc_killer not on list");
1954 } else {
1955 prev->next = k->next;
1956 free((genericptr_t) k);
1960 void
1961 save_killers(fd, mode)
1962 int fd;
1963 int mode;
1965 struct kinfo *kptr;
1967 if (perform_bwrite(mode)) {
1968 for (kptr = &killer; kptr != (struct kinfo *) 0; kptr = kptr->next) {
1969 bwrite(fd, (genericptr_t) kptr, sizeof(struct kinfo));
1972 if (release_data(mode)) {
1973 while (killer.next) {
1974 kptr = killer.next->next;
1975 free((genericptr_t) killer.next);
1976 killer.next = kptr;
1981 void
1982 restore_killers(fd)
1983 int fd;
1985 struct kinfo *kptr;
1987 for (kptr = &killer; kptr != (struct kinfo *) 0; kptr = kptr->next) {
1988 mread(fd, (genericptr_t) kptr, sizeof(struct kinfo));
1989 if (kptr->next) {
1990 kptr->next = (struct kinfo *) alloc(sizeof(struct kinfo));
1995 static int
1996 wordcount(p)
1997 char *p;
1999 int words = 0;
2001 while (*p) {
2002 while (*p && isspace((uchar) *p))
2003 p++;
2004 if (*p)
2005 words++;
2006 while (*p && !isspace((uchar) *p))
2007 p++;
2009 return words;
2012 static void
2013 bel_copy1(inp, out)
2014 char **inp, *out;
2016 char *in = *inp;
2018 out += strlen(out); /* eos() */
2019 while (*in && isspace((uchar) *in))
2020 in++;
2021 while (*in && !isspace((uchar) *in))
2022 *out++ = *in++;
2023 *out = '\0';
2024 *inp = in;
2027 char *
2028 build_english_list(in)
2029 char *in;
2031 char *out, *p = in;
2032 int len = (int) strlen(p), words = wordcount(p);
2034 /* +3: " or " - " "; +(words - 1): (N-1)*(", " - " ") */
2035 if (words > 1)
2036 len += 3 + (words - 1);
2037 out = (char *) alloc(len + 1);
2038 *out = '\0'; /* bel_copy1() appends */
2040 switch (words) {
2041 case 0:
2042 impossible("no words in list");
2043 break;
2044 case 1:
2045 /* "single" */
2046 bel_copy1(&p, out);
2047 break;
2048 default:
2049 if (words == 2) {
2050 /* "first or second" */
2051 bel_copy1(&p, out);
2052 Strcat(out, " ");
2053 } else {
2054 /* "first, second, or third */
2055 do {
2056 bel_copy1(&p, out);
2057 Strcat(out, ", ");
2058 } while (--words > 1);
2060 Strcat(out, "or ");
2061 bel_copy1(&p, out);
2062 break;
2064 return out;
2067 /*end.c*/