Move getpos return values to header
[aNetHack.git] / src / end.c
blobd097292fbf6a0eece058c111e722f260578cac6e
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();
784 if (!context.mon_moving)
785 endmultishot(FALSE);
789 * Get valuables from the given list. Revised code: the list always remains
790 * intact.
792 STATIC_OVL void
793 get_valuables(list)
794 struct obj *list; /* inventory or container contents */
796 register struct obj *obj;
797 register int i;
799 /* find amulets and gems, ignoring all artifacts */
800 for (obj = list; obj; obj = obj->nobj)
801 if (Has_contents(obj)) {
802 get_valuables(obj->cobj);
803 } else if (obj->oartifact) {
804 continue;
805 } else if (obj->oclass == AMULET_CLASS) {
806 i = obj->otyp - FIRST_AMULET;
807 if (!amulets[i].count) {
808 amulets[i].count = obj->quan;
809 amulets[i].typ = obj->otyp;
810 } else
811 amulets[i].count += obj->quan; /* always adds one */
812 } else if (obj->oclass == GEM_CLASS && obj->otyp < LUCKSTONE) {
813 i = min(obj->otyp, LAST_GEM + 1) - FIRST_GEM;
814 if (!gems[i].count) {
815 gems[i].count = obj->quan;
816 gems[i].typ = obj->otyp;
817 } else
818 gems[i].count += obj->quan;
820 return;
824 * Sort collected valuables, most frequent to least. We could just
825 * as easily use qsort, but we don't care about efficiency here.
827 STATIC_OVL void
828 sort_valuables(list, size)
829 struct valuable_data list[];
830 int size; /* max value is less than 20 */
832 register int i, j;
833 struct valuable_data ltmp;
835 /* move greater quantities to the front of the list */
836 for (i = 1; i < size; i++) {
837 if (list[i].count == 0)
838 continue; /* empty slot */
839 ltmp = list[i]; /* structure copy */
840 for (j = i; j > 0; --j)
841 if (list[j - 1].count >= ltmp.count)
842 break;
843 else {
844 list[j] = list[j - 1];
846 list[j] = ltmp;
848 return;
851 #define CAT_CHECK 2
853 STATIC_OVL boolean
854 odds_and_ends(list, what)
855 struct obj *list;
856 int what;
858 struct obj *otmp;
859 for (otmp = list; otmp; otmp = otmp->nobj) {
860 switch (what) {
861 case CAT_CHECK: /* Schroedinger's Cat */
862 /* Ascending is deterministic */
863 if (SchroedingersBox(otmp))
864 return rn2(2);
865 break;
867 if (Has_contents(otmp))
868 return odds_and_ends(otmp->cobj, what);
870 return FALSE;
873 /* called twice; first to calculate total, then to list relevant items */
874 STATIC_OVL void
875 artifact_score(list, counting, endwin)
876 struct obj *list;
877 boolean counting; /* true => add up points; false => display them */
878 winid endwin;
880 char pbuf[BUFSZ];
881 struct obj *otmp;
882 long value, points;
883 short dummy; /* object type returned by artifact_name() */
885 for (otmp = list; otmp; otmp = otmp->nobj) {
886 if (otmp->oartifact || otmp->otyp == BELL_OF_OPENING
887 || otmp->otyp == SPE_BOOK_OF_THE_DEAD
888 || otmp->otyp == CANDELABRUM_OF_INVOCATION) {
889 value = arti_cost(otmp); /* zorkmid value */
890 points = value * 5 / 2; /* score value */
891 if (counting) {
892 nowrap_add(u.urexp, points);
893 } else {
894 makeknown(otmp->otyp);
895 otmp->known = otmp->dknown = otmp->bknown = otmp->rknown = 1;
896 /* assumes artifacts don't have quan > 1 */
897 Sprintf(pbuf, "%s%s (worth %ld %s and %ld points)",
898 the_unique_obj(otmp) ? "The " : "",
899 otmp->oartifact ? artifact_name(xname(otmp), &dummy)
900 : OBJ_NAME(objects[otmp->otyp]),
901 value, currency(value), points);
902 putstr(endwin, 0, pbuf);
905 if (Has_contents(otmp))
906 artifact_score(otmp->cobj, counting, endwin);
910 /* Be careful not to call panic from here! */
911 void
912 done(how)
913 int how;
915 if (how == TRICKED) {
916 if (killer.name[0]) {
917 paniclog("trickery", killer.name);
918 killer.name[0] = 0;
920 if (wizard) {
921 You("are a very tricky wizard, it seems.");
922 return;
926 if (how == ASCENDED || (!killer.name[0] && how == GENOCIDED))
927 killer.format = NO_KILLER_PREFIX;
928 /* Avoid killed by "a" burning or "a" starvation */
929 if (!killer.name[0] && (how == STARVING || how == BURNING))
930 killer.format = KILLED_BY;
931 if (!killer.name[0] || how >= PANICKED)
932 Strcpy(killer.name, deaths[how]);
934 if (how < PANICKED)
935 u.umortality++;
936 if (Lifesaved && (how <= GENOCIDED)) {
937 pline("But wait...");
938 makeknown(AMULET_OF_LIFE_SAVING);
939 Your("medallion %s!", !Blind ? "begins to glow" : "feels warm");
940 if (how == CHOKING)
941 You("vomit ...");
942 You_feel("much better!");
943 pline_The("medallion crumbles to dust!");
944 if (uamul)
945 useup(uamul);
947 (void) adjattrib(A_CON, -1, TRUE);
948 savelife(how);
949 if (how == GENOCIDED) {
950 pline("Unfortunately you are still genocided...");
951 } else {
952 killer.name[0] = 0;
953 killer.format = 0;
954 return;
957 if ((wizard || discover) && (how <= GENOCIDED)
958 && !paranoid_query(ParanoidDie, "Die?")) {
959 pline("OK, so you don't %s.", (how == CHOKING) ? "choke" : "die");
960 savelife(how);
961 killer.name[0] = 0;
962 killer.format = 0;
963 return;
965 really_done(how);
968 /* separated from done() in order to specify the __noreturn__ attribute */
969 STATIC_OVL void
970 really_done(how)
971 int how;
973 boolean taken;
974 char pbuf[BUFSZ];
975 winid endwin = WIN_ERR;
976 boolean bones_ok, have_windows = iflags.window_inited;
977 struct obj *corpse = (struct obj *) 0;
978 time_t endtime;
979 long umoney;
980 long tmp;
983 * The game is now over...
985 program_state.gameover = 1;
986 /* in case of a subsequent panic(), there's no point trying to save */
987 program_state.something_worth_saving = 0;
988 /* render vision subsystem inoperative */
989 iflags.vision_inited = 0;
991 /* might have been killed while using a disposable item, so make sure
992 it's gone prior to inventory disclosure and creation of bones data */
993 inven_inuse(TRUE);
994 /* maybe not on object lists; if an active light source, would cause
995 big trouble (`obj_is_local' panic) for savebones() -> savelev() */
996 if (thrownobj && thrownobj->where == OBJ_FREE)
997 dealloc_obj(thrownobj);
998 if (kickedobj && kickedobj->where == OBJ_FREE)
999 dealloc_obj(kickedobj);
1001 /* remember time of death here instead of having bones, rip, and
1002 topten figure it out separately and possibly getting different
1003 time or even day if player is slow responding to --More-- */
1004 urealtime.finish_time = endtime = getnow();
1005 urealtime.realtime += (long) (endtime - urealtime.start_timing);
1007 /* Sometimes you die on the first move. Life's not fair.
1008 * On those rare occasions you get hosed immediately, go out
1009 * smiling... :-) -3.
1011 if (moves <= 1 && how < PANICKED) /* You die... --More-- */
1012 pline("Do not pass go. Do not collect 200 %s.", currency(200L));
1014 if (have_windows)
1015 wait_synch(); /* flush screen output */
1016 #ifndef NO_SIGNAL
1017 (void) signal(SIGINT, (SIG_RET_TYPE) done_intr);
1018 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
1019 (void) signal(SIGQUIT, (SIG_RET_TYPE) done_intr);
1020 sethanguphandler(done_hangup);
1021 #endif
1022 #endif /* NO_SIGNAL */
1024 bones_ok = (how < GENOCIDED) && can_make_bones();
1026 if (bones_ok && launch_in_progress())
1027 force_launch_placement();
1029 /* maintain ugrave_arise even for !bones_ok */
1030 if (how == PANICKED)
1031 u.ugrave_arise = (NON_PM - 3); /* no corpse, no grave */
1032 else if (how == BURNING || how == DISSOLVED) /* corpse burns up too */
1033 u.ugrave_arise = (NON_PM - 2); /* leave no corpse */
1034 else if (how == STONING)
1035 u.ugrave_arise = (NON_PM - 1); /* statue instead of corpse */
1036 else if (how == TURNED_SLIME)
1037 u.ugrave_arise = PM_GREEN_SLIME;
1039 /* if pets will contribute to score, populate mydogs list now
1040 (bones creation isn't a factor, but pline() messaging is) */
1041 if (how == ESCAPED || how == ASCENDED)
1042 keepdogs(TRUE);
1044 if (how == QUIT) {
1045 killer.format = NO_KILLER_PREFIX;
1046 if (u.uhp < 1) {
1047 how = DIED;
1048 u.umortality++; /* skipped above when how==QUIT */
1049 Strcpy(killer.name, "quit while already on Charon's boat");
1052 if (how == ESCAPED || how == PANICKED)
1053 killer.format = NO_KILLER_PREFIX;
1055 fixup_death(how); /* actually, fixup multi_reason */
1057 if (how != PANICKED) {
1058 /* these affect score and/or bones, but avoid them during panic */
1059 taken = paybill((how == ESCAPED) ? -1 : (how != QUIT));
1060 paygd();
1061 clearpriests();
1062 } else
1063 taken = FALSE; /* lint; assert( !bones_ok ); */
1065 clearlocks();
1067 if (have_windows)
1068 display_nhwindow(WIN_MESSAGE, FALSE);
1070 if (strcmp(flags.end_disclose, "none") && how != PANICKED)
1071 disclose(how, taken);
1073 /* finish_paybill should be called after disclosure but before bones */
1074 if (bones_ok && taken)
1075 finish_paybill();
1077 /* grave creation should be after disclosure so it doesn't have
1078 this grave in the current level's features for #overview */
1079 if (bones_ok && u.ugrave_arise == NON_PM
1080 && !(mvitals[u.umonnum].mvflags & G_NOCORPSE)) {
1081 int mnum = u.umonnum;
1083 if (!Upolyd) {
1084 /* Base corpse on race when not poly'd since original
1085 * u.umonnum is based on role, and all role monsters
1086 * are human.
1088 mnum = (flags.female && urace.femalenum != NON_PM)
1089 ? urace.femalenum
1090 : urace.malenum;
1092 corpse = mk_named_object(CORPSE, &mons[mnum], u.ux, u.uy, plname);
1093 Sprintf(pbuf, "%s, ", plname);
1094 formatkiller(eos(pbuf), sizeof pbuf - strlen(pbuf), how, TRUE);
1095 make_grave(u.ux, u.uy, pbuf);
1097 pbuf[0] = '\0'; /* clear grave text; also lint suppression */
1099 /* calculate score, before creating bones [container gold] */
1101 int deepest = deepest_lev_reached(FALSE);
1103 umoney = money_cnt(invent);
1104 tmp = u.umoney0;
1105 umoney += hidden_gold(); /* accumulate gold from containers */
1106 tmp = umoney - tmp; /* net gain */
1108 if (tmp < 0L)
1109 tmp = 0L;
1110 if (how < PANICKED)
1111 tmp -= tmp / 10L;
1112 tmp += 50L * (long) (deepest - 1);
1113 if (deepest > 20)
1114 tmp += 1000L * (long) ((deepest > 30) ? 10 : deepest - 20);
1115 nowrap_add(u.urexp, tmp);
1117 /* ascension gives a score bonus iff offering to original deity */
1118 if (how == ASCENDED && u.ualign.type == u.ualignbase[A_ORIGINAL]) {
1119 /* retaining original alignment: score *= 2;
1120 converting, then using helm-of-OA to switch back: *= 1.5 */
1121 tmp = (u.ualignbase[A_CURRENT] == u.ualignbase[A_ORIGINAL])
1122 ? u.urexp
1123 : (u.urexp / 2L);
1124 nowrap_add(u.urexp, tmp);
1128 if (u.ugrave_arise >= LOW_PM && u.ugrave_arise != PM_GREEN_SLIME) {
1129 /* give this feedback even if bones aren't going to be created,
1130 so that its presence or absence doesn't tip off the player to
1131 new bones or their lack; it might be a lie if makemon fails */
1132 Your("body rises from the dead as %s...",
1133 an(mons[u.ugrave_arise].mname));
1134 display_nhwindow(WIN_MESSAGE, FALSE);
1137 if (bones_ok) {
1138 if (!wizard || paranoid_query(ParanoidBones, "Save bones?"))
1139 savebones(how, endtime, corpse);
1140 /* corpse may be invalid pointer now so
1141 ensure that it isn't used again */
1142 corpse = (struct obj *) 0;
1145 /* update gold for the rip output, which can't use hidden_gold()
1146 (containers will be gone by then if bones just got saved...) */
1147 done_money = umoney;
1149 /* clean up unneeded windows */
1150 if (have_windows) {
1151 wait_synch();
1152 free_pickinv_cache(); /* extra persistent window if perm_invent */
1153 if (WIN_INVEN != WIN_ERR)
1154 destroy_nhwindow(WIN_INVEN), WIN_INVEN = WIN_ERR;
1155 display_nhwindow(WIN_MESSAGE, TRUE);
1156 destroy_nhwindow(WIN_MAP), WIN_MAP = WIN_ERR;
1157 #ifndef STATUS_VIA_WINDOWPORT
1158 destroy_nhwindow(WIN_STATUS), WIN_STATUS = WIN_ERR;
1159 #endif
1160 destroy_nhwindow(WIN_MESSAGE), WIN_MESSAGE = WIN_ERR;
1162 if (!done_stopprint || flags.tombstone)
1163 endwin = create_nhwindow(NHW_TEXT);
1165 if (how < GENOCIDED && flags.tombstone && endwin != WIN_ERR)
1166 outrip(endwin, how, endtime);
1167 } else
1168 done_stopprint = 1; /* just avoid any more output */
1170 if (u.uhave.amulet) {
1171 Strcat(killer.name, " (with the Amulet)");
1172 } else if (how == ESCAPED) {
1173 if (Is_astralevel(&u.uz)) /* offered Amulet to wrong deity */
1174 Strcat(killer.name, " (in celestial disgrace)");
1175 else if (carrying(FAKE_AMULET_OF_YENDOR))
1176 Strcat(killer.name, " (with a fake Amulet)");
1177 /* don't bother counting to see whether it should be plural */
1180 if (!done_stopprint) {
1181 Sprintf(pbuf, "%s %s the %s...", Goodbye(), plname,
1182 (how != ASCENDED)
1183 ? (const char *) ((flags.female && urole.name.f)
1184 ? urole.name.f
1185 : urole.name.m)
1186 : (const char *) (flags.female ? "Demigoddess" : "Demigod"));
1187 putstr(endwin, 0, pbuf);
1188 putstr(endwin, 0, "");
1191 if (how == ESCAPED || how == ASCENDED) {
1192 struct monst *mtmp;
1193 struct obj *otmp;
1194 register struct val_list *val;
1195 register int i;
1197 for (val = valuables; val->list; val++)
1198 for (i = 0; i < val->size; i++) {
1199 val->list[i].count = 0L;
1201 get_valuables(invent);
1203 /* add points for collected valuables */
1204 for (val = valuables; val->list; val++)
1205 for (i = 0; i < val->size; i++)
1206 if (val->list[i].count != 0L) {
1207 tmp = val->list[i].count
1208 * (long) objects[val->list[i].typ].oc_cost;
1209 nowrap_add(u.urexp, tmp);
1212 /* count the points for artifacts */
1213 artifact_score(invent, TRUE, endwin);
1215 viz_array[0][0] |= IN_SIGHT; /* need visibility for naming */
1216 mtmp = mydogs;
1217 if (!done_stopprint)
1218 Strcpy(pbuf, "You");
1219 if (!Schroedingers_cat) /* check here in case disclosure was off */
1220 Schroedingers_cat = odds_and_ends(invent, CAT_CHECK);
1221 if (Schroedingers_cat) {
1222 int mhp, m_lev = adj_lev(&mons[PM_HOUSECAT]);
1223 mhp = d(m_lev, 8);
1224 nowrap_add(u.urexp, mhp);
1225 if (!done_stopprint)
1226 Strcat(eos(pbuf), " and Schroedinger's cat");
1228 if (mtmp) {
1229 while (mtmp) {
1230 if (!done_stopprint)
1231 Sprintf(eos(pbuf), " and %s", mon_nam(mtmp));
1232 if (mtmp->mtame)
1233 nowrap_add(u.urexp, mtmp->mhp);
1234 mtmp = mtmp->nmon;
1236 if (!done_stopprint)
1237 putstr(endwin, 0, pbuf);
1238 pbuf[0] = '\0';
1239 } else {
1240 if (!done_stopprint)
1241 Strcat(pbuf, " ");
1243 if (!done_stopprint) {
1244 Sprintf(eos(pbuf), "%s with %ld point%s,",
1245 how == ASCENDED ? "went to your reward"
1246 : "escaped from the dungeon",
1247 u.urexp, plur(u.urexp));
1248 putstr(endwin, 0, pbuf);
1251 if (!done_stopprint)
1252 artifact_score(invent, FALSE, endwin); /* list artifacts */
1254 /* list valuables here */
1255 for (val = valuables; val->list; val++) {
1256 sort_valuables(val->list, val->size);
1257 for (i = 0; i < val->size && !done_stopprint; i++) {
1258 int typ = val->list[i].typ;
1259 long count = val->list[i].count;
1261 if (count == 0L)
1262 continue;
1263 if (objects[typ].oc_class != GEM_CLASS || typ <= LAST_GEM) {
1264 otmp = mksobj(typ, FALSE, FALSE);
1265 makeknown(otmp->otyp);
1266 otmp->known = 1; /* for fake amulets */
1267 otmp->dknown = 1; /* seen it (blindness fix) */
1268 if (has_oname(otmp))
1269 free_oname(otmp);
1270 otmp->quan = count;
1271 Sprintf(pbuf, "%8ld %s (worth %ld %s),", count,
1272 xname(otmp), count * (long) objects[typ].oc_cost,
1273 currency(2L));
1274 obfree(otmp, (struct obj *) 0);
1275 } else {
1276 Sprintf(pbuf, "%8ld worthless piece%s of colored glass,",
1277 count, plur(count));
1279 putstr(endwin, 0, pbuf);
1283 } else if (!done_stopprint) {
1284 /* did not escape or ascend */
1285 if (u.uz.dnum == 0 && u.uz.dlevel <= 0) {
1286 /* level teleported out of the dungeon; `how' is DIED,
1287 due to falling or to "arriving at heaven prematurely" */
1288 Sprintf(pbuf, "You %s beyond the confines of the dungeon",
1289 (u.uz.dlevel < 0) ? "passed away" : ends[how]);
1290 } else {
1291 /* more conventional demise */
1292 const char *where = dungeons[u.uz.dnum].dname;
1294 if (Is_astralevel(&u.uz))
1295 where = "The Astral Plane";
1296 Sprintf(pbuf, "You %s in %s", ends[how], where);
1297 if (!In_endgame(&u.uz) && !Is_knox(&u.uz))
1298 Sprintf(eos(pbuf), " on dungeon level %d",
1299 In_quest(&u.uz) ? dunlev(&u.uz) : depth(&u.uz));
1302 Sprintf(eos(pbuf), " with %ld point%s,", u.urexp, plur(u.urexp));
1303 putstr(endwin, 0, pbuf);
1306 if (!done_stopprint) {
1307 Sprintf(pbuf, "and %ld piece%s of gold, after %ld move%s.", umoney,
1308 plur(umoney), moves, plur(moves));
1309 putstr(endwin, 0, pbuf);
1311 if (!done_stopprint) {
1312 Sprintf(pbuf,
1313 "You were level %d with a maximum of %d hit point%s when you %s.",
1314 u.ulevel, u.uhpmax, plur(u.uhpmax), ends[how]);
1315 putstr(endwin, 0, pbuf);
1316 putstr(endwin, 0, "");
1318 if (!done_stopprint)
1319 display_nhwindow(endwin, TRUE);
1320 if (endwin != WIN_ERR)
1321 destroy_nhwindow(endwin);
1323 /* "So when I die, the first thing I will see in Heaven is a
1324 * score list?" */
1325 if (have_windows && !iflags.toptenwin)
1326 exit_nhwindows((char *) 0), have_windows = FALSE;
1327 topten(how, endtime);
1328 if (have_windows)
1329 exit_nhwindows((char *) 0);
1331 if (done_stopprint) {
1332 raw_print("");
1333 raw_print("");
1335 terminate(EXIT_SUCCESS);
1338 void
1339 container_contents(list, identified, all_containers, reportempty)
1340 struct obj *list;
1341 boolean identified, all_containers, reportempty;
1343 register struct obj *box, *obj;
1344 char buf[BUFSZ];
1345 boolean cat, deadcat;
1347 for (box = list; box; box = box->nobj) {
1348 if (Is_container(box) || box->otyp == STATUE) {
1349 box->cknown = 1; /* we're looking at the contents now */
1350 if (identified)
1351 box->lknown = 1;
1352 cat = deadcat = FALSE;
1353 if (SchroedingersBox(box) && !Schroedingers_cat) {
1354 /* Schroedinger's Cat? */
1355 cat = odds_and_ends(box, CAT_CHECK);
1356 if (cat)
1357 Schroedingers_cat = TRUE;
1358 else
1359 deadcat = TRUE;
1360 box->spe = 0;
1362 if (box->otyp == BAG_OF_TRICKS) {
1363 continue; /* wrong type of container */
1364 } else if (box->cobj) {
1365 winid tmpwin = create_nhwindow(NHW_MENU);
1367 sortloot(&box->cobj,
1368 (((flags.sortloot == 'l' || flags.sortloot == 'f')
1369 ? SORTLOOT_LOOT : 0)
1370 | (flags.sortpack ? SORTLOOT_PACK : 0)),
1371 FALSE);
1372 Sprintf(buf, "Contents of %s:", the(xname(box)));
1373 putstr(tmpwin, 0, buf);
1374 putstr(tmpwin, 0, "");
1375 for (obj = box->cobj; obj; obj = obj->nobj) {
1376 if (identified) {
1377 makeknown(obj->otyp);
1378 obj->known = obj->bknown = obj->dknown
1379 = obj->rknown = 1;
1380 if (Is_container(obj) || obj->otyp == STATUE)
1381 obj->cknown = obj->lknown = 1;
1383 putstr(tmpwin, 0, doname(obj));
1385 if (cat)
1386 putstr(tmpwin, 0, "Schroedinger's cat");
1387 else if (deadcat)
1388 putstr(tmpwin, 0, "Schroedinger's dead cat");
1389 display_nhwindow(tmpwin, TRUE);
1390 destroy_nhwindow(tmpwin);
1391 if (all_containers)
1392 container_contents(box->cobj, identified, TRUE,
1393 reportempty);
1394 } else if (cat || deadcat) {
1395 pline("%s Schroedinger's %scat!", Tobjnam(box, "contain"),
1396 deadcat ? "dead " : "");
1397 display_nhwindow(WIN_MESSAGE, FALSE);
1398 } else if (reportempty) {
1399 pline("%s is empty.", upstart(thesimpleoname(box)));
1400 display_nhwindow(WIN_MESSAGE, FALSE);
1403 if (!all_containers)
1404 break;
1408 /* should be called with either EXIT_SUCCESS or EXIT_FAILURE */
1409 void
1410 terminate(status)
1411 int status;
1413 program_state.in_moveloop = 0; /* won't be returning to normal play */
1414 #ifdef MAC
1415 getreturn("to exit");
1416 #endif
1417 /* don't bother to try to release memory if we're in panic mode, to
1418 avoid trouble in case that happens to be due to memory problems */
1419 if (!program_state.panicking) {
1420 freedynamicdata();
1421 dlb_cleanup();
1424 #ifdef VMS
1426 * This is liable to draw a warning if compiled with gcc, but it's
1427 * more important to flag panic() -> really_done() -> terminate()
1428 * as __noreturn__ then to avoid the warning.
1430 /* don't call exit() if already executing within an exit handler;
1431 that would cancel any other pending user-mode handlers */
1432 if (program_state.exiting)
1433 return;
1434 #endif
1435 program_state.exiting = 1;
1436 nethack_exit(status);
1439 extern const int monstr[];
1441 static const char *vanqorders[] = {
1442 "traditional: by monster level, by internal monster index",
1443 #define VANQ_MLVL_MNDX 0
1444 "by monster toughness, by internal monster index",
1445 #define VANQ_MSTR_MNDX 1
1446 "alphabetically, first unique monsters, then others",
1447 #define VANQ_ALPHA_SEP 2
1448 "alphabetically, unique monsters and others intermixed",
1449 #define VANQ_ALPHA_MIX 3
1450 "by monster class, high to low level within class",
1451 #define VANQ_MCLS_HTOL 4
1452 "by monster class, low to high level within class",
1453 #define VANQ_MCLS_LTOH 5
1454 "by count, high to low, by internal index within tied count",
1455 #define VANQ_COUNT_H_L 6
1456 "by count, low to high, by internal index within tied count",
1457 #define VANQ_COUNT_L_H 7
1459 static int vanq_sortmode = VANQ_MLVL_MNDX;
1461 STATIC_PTR int CFDECLSPEC
1462 vanqsort_cmp(vptr1, vptr2)
1463 const genericptr vptr1;
1464 const genericptr vptr2;
1466 int indx1 = *(short *) vptr1, indx2 = *(short *) vptr2,
1467 mlev1, mlev2, mstr1, mstr2, uniq1, uniq2, died1, died2, res;
1468 const char *name1, *name2, *punct;
1469 schar mcls1, mcls2;
1471 switch (vanq_sortmode) {
1472 default:
1473 case VANQ_MLVL_MNDX:
1474 /* sort by monster level */
1475 mlev1 = mons[indx1].mlevel, mlev2 = mons[indx2].mlevel;
1476 res = mlev2 - mlev1; /* mlevel high to low */
1477 break;
1478 case VANQ_MSTR_MNDX:
1479 /* sort by monster toughness */
1480 mstr1 = monstr[indx1], mstr2 = monstr[indx2];
1481 res = mstr2 - mstr1; /* monstr high to low */
1482 break;
1483 case VANQ_ALPHA_SEP:
1484 uniq1 = ((mons[indx1].geno & G_UNIQ) && indx1 != PM_HIGH_PRIEST);
1485 uniq2 = ((mons[indx2].geno & G_UNIQ) && indx2 != PM_HIGH_PRIEST);
1486 if (uniq1 ^ uniq2) { /* one or other uniq, but not both */
1487 res = uniq2 - uniq1;
1488 break;
1489 } /* else both unique or neither unique */
1490 /*FALLTHRU*/
1491 case VANQ_ALPHA_MIX:
1492 name1 = mons[indx1].mname, name2 = mons[indx2].mname;
1493 res = strcmpi(name1, name2); /* caseblind alhpa, low to high */
1494 break;
1495 case VANQ_MCLS_HTOL:
1496 case VANQ_MCLS_LTOH:
1497 /* mons[].mlet is a small integer, 1..N, of type plain char;
1498 if 'char' happens to be unsigned, (mlet1 - mlet2) would yield
1499 an inappropriate result when mlet2 is greater than mlet1,
1500 so force our copies (mcls1, mcls2) to be signed */
1501 mcls1 = (schar) mons[indx1].mlet, mcls2 = (schar) mons[indx2].mlet;
1502 /* S_ANT through S_ZRUTY correspond to lowercase monster classes,
1503 S_ANGEL through S_ZOMBIE correspond to uppercase, and various
1504 punctuation characters are used for classes beyond those */
1505 if (mcls1 > S_ZOMBIE && mcls2 > S_ZOMBIE) {
1506 /* force a specific order to the punctuation classes that's
1507 different from the internal order;
1508 internal order is ok if neither or just one is punctuation
1509 since letters have lower values so come out before punct */
1510 static const char punctclasses[] = {
1511 S_LIZARD, S_EEL, S_GOLEM, S_GHOST, S_DEMON, S_HUMAN, '\0'
1514 if ((punct = index(punctclasses, mcls1)) != 0)
1515 mcls1 = (schar) (S_ZOMBIE + 1 + (int) (punct - punctclasses));
1516 if ((punct = index(punctclasses, mcls2)) != 0)
1517 mcls2 = (schar) (S_ZOMBIE + 1 + (int) (punct - punctclasses));
1519 res = mcls1 - mcls2; /* class */
1520 if (res == 0) {
1521 mlev1 = mons[indx1].mlevel, mlev2 = mons[indx2].mlevel;
1522 res = mlev1 - mlev2; /* mlevel low to high */
1523 if (vanq_sortmode == VANQ_MCLS_HTOL)
1524 res = -res; /* mlevel high to low */
1526 break;
1527 case VANQ_COUNT_H_L:
1528 case VANQ_COUNT_L_H:
1529 died1 = mvitals[indx1].died, died2 = mvitals[indx2].died;
1530 res = died2 - died1; /* dead count high to low */
1531 if (vanq_sortmode == VANQ_COUNT_L_H)
1532 res = -res; /* dead count low to high */
1533 break;
1535 /* tiebreaker: internal mons[] index */
1536 if (res == 0)
1537 res = indx1 - indx2; /* mndx low to high */
1538 return res;
1541 /* returns -1 if cancelled via ESC */
1542 STATIC_OVL int
1543 set_vanq_order()
1545 winid tmpwin;
1546 menu_item *selected;
1547 anything any;
1548 int i, n, choice;
1550 tmpwin = create_nhwindow(NHW_MENU);
1551 start_menu(tmpwin);
1552 any = zeroany; /* zero out all bits */
1553 for (i = 0; i < SIZE(vanqorders); i++) {
1554 if (i == VANQ_ALPHA_MIX || i == VANQ_MCLS_HTOL) /* skip these */
1555 continue;
1556 any.a_int = i + 1;
1557 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, vanqorders[i],
1558 (i == vanq_sortmode) ? MENU_SELECTED : MENU_UNSELECTED);
1560 end_menu(tmpwin, "Sort order for vanquished monster counts");
1562 n = select_menu(tmpwin, PICK_ONE, &selected);
1563 destroy_nhwindow(tmpwin);
1564 if (n > 0) {
1565 choice = selected[0].item.a_int - 1;
1566 /* skip preselected entry if we have more than one item chosen */
1567 if (n > 1 && choice == vanq_sortmode)
1568 choice = selected[1].item.a_int - 1;
1569 free((genericptr_t) selected);
1570 vanq_sortmode = choice;
1572 return (n < 0) ? -1 : vanq_sortmode;
1575 /* #vanquished command */
1577 dovanquished()
1579 list_vanquished('a', FALSE);
1580 return 0;
1583 /* high priests aren't unique but are flagged as such to simplify something */
1584 #define UniqCritterIndx(mndx) ((mons[mndx].geno & G_UNIQ) \
1585 && mndx != PM_HIGH_PRIEST)
1587 STATIC_OVL void
1588 list_vanquished(defquery, ask)
1589 char defquery;
1590 boolean ask;
1592 register int i;
1593 int pfx, nkilled;
1594 unsigned ntypes, ni;
1595 long total_killed = 0L;
1596 winid klwin;
1597 short mindx[NUMMONS];
1598 char c, buf[BUFSZ], buftoo[BUFSZ];
1600 /* get totals first */
1601 ntypes = 0;
1602 for (i = LOW_PM; i < NUMMONS; i++) {
1603 if ((nkilled = (int) mvitals[i].died) == 0)
1604 continue;
1605 mindx[ntypes++] = i;
1606 total_killed += (long) nkilled;
1609 /* vanquished creatures list;
1610 * includes all dead monsters, not just those killed by the player
1612 if (ntypes != 0) {
1613 char mlet, prev_mlet = 0; /* used as small integer, not character */
1614 boolean class_header, uniq_header, was_uniq = FALSE;
1616 c = ask ? yn_function(
1617 "Do you want an account of creatures vanquished?",
1618 ynaqchars, defquery)
1619 : defquery;
1620 if (c == 'q')
1621 done_stopprint++;
1622 if (c == 'y' || c == 'a') {
1623 if (c == 'a') { /* ask player to choose sort order */
1624 /* choose value for vanq_sortmode via menu; ESC cancels list
1625 of vanquished monsters but does not set 'done_stopprint' */
1626 if (set_vanq_order() < 0)
1627 return;
1629 uniq_header = (vanq_sortmode == VANQ_ALPHA_SEP);
1630 class_header = (vanq_sortmode == VANQ_MCLS_LTOH
1631 || vanq_sortmode == VANQ_MCLS_HTOL);
1633 klwin = create_nhwindow(NHW_MENU);
1634 putstr(klwin, 0, "Vanquished creatures:");
1635 putstr(klwin, 0, "");
1637 qsort((genericptr_t) mindx, ntypes, sizeof *mindx, vanqsort_cmp);
1638 for (ni = 0; ni < ntypes; ni++) {
1639 i = mindx[ni];
1640 nkilled = mvitals[i].died;
1641 mlet = mons[i].mlet;
1642 if (class_header && mlet != prev_mlet) {
1643 Strcpy(buf, def_monsyms[(int) mlet].explain);
1644 putstr(klwin, ask ? 0 : iflags.menu_headings,
1645 upstart(buf));
1646 prev_mlet = mlet;
1648 if (UniqCritterIndx(i)) {
1649 Sprintf(buf, "%s%s",
1650 !type_is_pname(&mons[i]) ? "the " : "",
1651 mons[i].mname);
1652 if (nkilled > 1) {
1653 switch (nkilled) {
1654 case 2:
1655 Sprintf(eos(buf), " (twice)");
1656 break;
1657 case 3:
1658 Sprintf(eos(buf), " (thrice)");
1659 break;
1660 default:
1661 Sprintf(eos(buf), " (%d times)", nkilled);
1662 break;
1665 was_uniq = TRUE;
1666 } else {
1667 if (uniq_header && was_uniq) {
1668 putstr(klwin, 0, "");
1669 was_uniq = FALSE;
1671 /* trolls or undead might have come back,
1672 but we don't keep track of that */
1673 if (nkilled == 1)
1674 Strcpy(buf, an(mons[i].mname));
1675 else
1676 Sprintf(buf, "%3d %s", nkilled,
1677 makeplural(mons[i].mname));
1679 /* number of leading spaces to match 3 digit prefix */
1680 pfx = !strncmpi(buf, "the ", 3) ? 0
1681 : !strncmpi(buf, "an ", 3) ? 1
1682 : !strncmpi(buf, "a ", 2) ? 2
1683 : !digit(buf[2]) ? 4 : 0;
1684 if (class_header)
1685 ++pfx;
1686 Sprintf(buftoo, "%*s%s", pfx, "", buf);
1687 putstr(klwin, 0, buftoo);
1690 * if (Hallucination)
1691 * putstr(klwin, 0, "and a partridge in a pear tree");
1693 if (ntypes > 1) {
1694 putstr(klwin, 0, "");
1695 Sprintf(buf, "%ld creatures vanquished.", total_killed);
1696 putstr(klwin, 0, buf);
1698 display_nhwindow(klwin, TRUE);
1699 destroy_nhwindow(klwin);
1701 } else if (defquery == 'a') {
1702 /* #dovanquished rather than final disclosure, so pline() is ok */
1703 pline("No monsters have been vanquished.");
1707 /* number of monster species which have been genocided */
1709 num_genocides()
1711 int i, n = 0;
1713 for (i = LOW_PM; i < NUMMONS; ++i) {
1714 if (mvitals[i].mvflags & G_GENOD) {
1715 ++n;
1716 if (UniqCritterIndx(i))
1717 impossible("unique creature '%d: %s' genocided?",
1718 i, mons[i].mname);
1721 return n;
1725 num_extinct()
1727 int i, n = 0;
1729 for (i = LOW_PM; i < NUMMONS; ++i) {
1730 if (UniqCritterIndx(i))
1731 continue;
1732 if ((mvitals[i].mvflags & G_GONE) == G_EXTINCT)
1733 ++n;
1735 return n;
1738 STATIC_OVL void
1739 list_genocided(defquery, ask)
1740 char defquery;
1741 boolean ask;
1743 register int i;
1744 int ngenocided, nextinct;
1745 char c;
1746 winid klwin;
1747 char buf[BUFSZ];
1749 ngenocided = num_genocides();
1750 nextinct = num_extinct();
1752 /* genocided or extinct species list */
1753 if (ngenocided != 0 || nextinct != 0) {
1754 Sprintf(buf, "Do you want a list of %sspecies%s%s?",
1755 (nextinct && !ngenocided) ? "extinct " : "",
1756 (ngenocided) ? " genocided" : "",
1757 (nextinct && ngenocided) ? " and extinct" : "");
1758 c = ask ? yn_function(buf, ynqchars, defquery) : defquery;
1759 if (c == 'q')
1760 done_stopprint++;
1761 if (c == 'y') {
1762 klwin = create_nhwindow(NHW_MENU);
1763 Sprintf(buf, "%s%s species:",
1764 (ngenocided) ? "Genocided" : "Extinct",
1765 (nextinct && ngenocided) ? " or extinct" : "");
1766 putstr(klwin, 0, buf);
1767 putstr(klwin, 0, "");
1769 for (i = LOW_PM; i < NUMMONS; i++) {
1770 /* uniques can't be genocided but can become extinct;
1771 however, they're never reported as extinct, so skip them */
1772 if (UniqCritterIndx(i))
1773 continue;
1774 if (mvitals[i].mvflags & G_GONE) {
1775 Strcpy(buf, makeplural(mons[i].mname));
1777 * "Extinct" is unfortunate terminology. A species
1778 * is marked extinct when its birth limit is reached,
1779 * but there might be members of the species still
1780 * alive, contradicting the meaning of the word.
1782 if ((mvitals[i].mvflags & G_GONE) == G_EXTINCT)
1783 Strcat(buf, " (extinct)");
1784 putstr(klwin, 0, buf);
1787 putstr(klwin, 0, "");
1788 if (ngenocided > 0) {
1789 Sprintf(buf, "%d species genocided.", ngenocided);
1790 putstr(klwin, 0, buf);
1792 if (nextinct > 0) {
1793 Sprintf(buf, "%d species extinct.", nextinct);
1794 putstr(klwin, 0, buf);
1797 display_nhwindow(klwin, TRUE);
1798 destroy_nhwindow(klwin);
1803 /* set a delayed killer, ensure non-delayed killer is cleared out */
1804 void
1805 delayed_killer(id, format, killername)
1806 int id;
1807 int format;
1808 const char *killername;
1810 struct kinfo *k = find_delayed_killer(id);
1812 if (k == (struct kinfo *) 0) {
1813 /* no match, add a new delayed killer to the list */
1814 k = (struct kinfo *) alloc(sizeof(struct kinfo));
1815 k->id = id;
1816 k->next = killer.next;
1817 killer.next = k;
1820 k->format = format;
1821 Strcpy(k->name, killername ? killername : "");
1822 killer.name[0] = 0;
1825 struct kinfo *
1826 find_delayed_killer(id)
1827 int id;
1829 struct kinfo *k;
1831 for (k = killer.next; k != (struct kinfo *) 0; k = k->next) {
1832 if (k->id == id)
1833 break;
1835 return k;
1838 void
1839 dealloc_killer(kptr)
1840 struct kinfo *kptr;
1842 struct kinfo *prev = &killer, *k;
1844 if (kptr == (struct kinfo *) 0)
1845 return;
1846 for (k = killer.next; k != (struct kinfo *) 0; k = k->next) {
1847 if (k == kptr)
1848 break;
1849 prev = k;
1852 if (k == (struct kinfo *) 0) {
1853 impossible("dealloc_killer not on list");
1854 } else {
1855 prev->next = k->next;
1856 free((genericptr_t) k);
1860 void
1861 save_killers(fd, mode)
1862 int fd;
1863 int mode;
1865 struct kinfo *kptr;
1867 if (perform_bwrite(mode)) {
1868 for (kptr = &killer; kptr != (struct kinfo *) 0; kptr = kptr->next) {
1869 bwrite(fd, (genericptr_t) kptr, sizeof(struct kinfo));
1872 if (release_data(mode)) {
1873 while (killer.next) {
1874 kptr = killer.next->next;
1875 free((genericptr_t) killer.next);
1876 killer.next = kptr;
1881 void
1882 restore_killers(fd)
1883 int fd;
1885 struct kinfo *kptr;
1887 for (kptr = &killer; kptr != (struct kinfo *) 0; kptr = kptr->next) {
1888 mread(fd, (genericptr_t) kptr, sizeof(struct kinfo));
1889 if (kptr->next) {
1890 kptr->next = (struct kinfo *) alloc(sizeof(struct kinfo));
1895 static int
1896 wordcount(p)
1897 char *p;
1899 int words = 0;
1901 while (*p) {
1902 while (*p && isspace((uchar) *p))
1903 p++;
1904 if (*p)
1905 words++;
1906 while (*p && !isspace((uchar) *p))
1907 p++;
1909 return words;
1912 static void
1913 bel_copy1(inp, out)
1914 char **inp, *out;
1916 char *in = *inp;
1918 out += strlen(out); /* eos() */
1919 while (*in && isspace((uchar) *in))
1920 in++;
1921 while (*in && !isspace((uchar) *in))
1922 *out++ = *in++;
1923 *out = '\0';
1924 *inp = in;
1927 char *
1928 build_english_list(in)
1929 char *in;
1931 char *out, *p = in;
1932 int len = (int) strlen(p), words = wordcount(p);
1934 /* +3: " or " - " "; +(words - 1): (N-1)*(", " - " ") */
1935 if (words > 1)
1936 len += 3 + (words - 1);
1937 out = (char *) alloc(len + 1);
1938 *out = '\0'; /* bel_copy1() appends */
1940 switch (words) {
1941 case 0:
1942 impossible("no words in list");
1943 break;
1944 case 1:
1945 /* "single" */
1946 bel_copy1(&p, out);
1947 break;
1948 default:
1949 if (words == 2) {
1950 /* "first or second" */
1951 bel_copy1(&p, out);
1952 Strcat(out, " ");
1953 } else {
1954 /* "first, second, or third */
1955 do {
1956 bel_copy1(&p, out);
1957 Strcat(out, ", ");
1958 } while (--words > 1);
1960 Strcat(out, "or ");
1961 bel_copy1(&p, out);
1962 break;
1964 return out;
1967 /*end.c*/