dumplog's saved_plines[]
[aNetHack.git] / src / end.c
blobf0d3e2f0709435bdac3e8d1229dee55090f568fd
1 /* NetHack 3.6 end.c $NHDT-Date: 1488075979 2017/02/26 02:26:19 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.127 $ */
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 #ifdef DUMPLOG
60 STATIC_DCL void NDECL(dump_plines);
61 #endif
62 STATIC_DCL void FDECL(dump_everything, (int));
63 STATIC_DCL int NDECL(num_extinct);
65 #if defined(__BEOS__) || defined(MICRO) || defined(WIN32) || defined(OS2)
66 extern void FDECL(nethack_exit, (int));
67 #else
68 #define nethack_exit exit
69 #endif
71 #define done_stopprint program_state.stopprint
73 #ifndef PANICTRACE
74 #define NH_abort NH_abort_
75 #endif
77 #ifdef AMIGA
78 #define NH_abort_() Abort(0)
79 #else
80 #ifdef SYSV
81 #define NH_abort_() (void) abort()
82 #else
83 #ifdef WIN32
84 #define NH_abort_() win32_abort()
85 #else
86 #define NH_abort_() abort()
87 #endif
88 #endif /* !SYSV */
89 #endif /* !AMIGA */
91 #ifdef PANICTRACE
92 #include <errno.h>
93 #ifdef PANICTRACE_LIBC
94 #include <execinfo.h>
95 #endif
97 /* What do we try and in what order? Tradeoffs:
98 * libc: +no external programs required
99 * -requires newish libc/glibc
100 * -requires -rdynamic
101 * gdb: +gives more detailed information
102 * +works on more OS versions
103 * -requires -g, which may preclude -O on some compilers
105 #ifdef SYSCF
106 #define SYSOPT_PANICTRACE_GDB sysopt.panictrace_gdb
107 #ifdef PANICTRACE_LIBC
108 #define SYSOPT_PANICTRACE_LIBC sysopt.panictrace_libc
109 #else
110 #define SYSOPT_PANICTRACE_LIBC 0
111 #endif
112 #else
113 #define SYSOPT_PANICTRACE_GDB (nh_getenv("NETHACK_USE_GDB") == 0 ? 0 : 2)
114 #ifdef PANICTRACE_LIBC
115 #define SYSOPT_PANICTRACE_LIBC 1
116 #else
117 #define SYSOPT_PANICTRACE_LIBC 0
118 #endif
119 #endif
121 static void NDECL(NH_abort);
122 #ifndef NO_SIGNAL
123 static void FDECL(panictrace_handler, (int));
124 #endif
125 static boolean NDECL(NH_panictrace_libc);
126 static boolean NDECL(NH_panictrace_gdb);
128 #ifndef NO_SIGNAL
129 /*ARGSUSED*/
130 void panictrace_handler(
131 sig_unused) /* called as signal() handler, so sent at least one arg */
132 int sig_unused UNUSED;
134 #define SIG_MSG "\nSignal received.\n"
135 (void) write(2, SIG_MSG, sizeof(SIG_MSG) - 1);
136 NH_abort();
139 void
140 panictrace_setsignals(set)
141 boolean set;
143 #define SETSIGNAL(sig) \
144 (void) signal(sig, set ? (SIG_RET_TYPE) panictrace_handler : SIG_DFL);
145 #ifdef SIGILL
146 SETSIGNAL(SIGILL);
147 #endif
148 #ifdef SIGTRAP
149 SETSIGNAL(SIGTRAP);
150 #endif
151 #ifdef SIGIOT
152 SETSIGNAL(SIGIOT);
153 #endif
154 #ifdef SIGBUS
155 SETSIGNAL(SIGBUS);
156 #endif
157 #ifdef SIGFPE
158 SETSIGNAL(SIGFPE);
159 #endif
160 #ifdef SIGSEGV
161 SETSIGNAL(SIGSEGV);
162 #endif
163 #ifdef SIGSTKFLT
164 SETSIGNAL(SIGSTKFLT);
165 #endif
166 #ifdef SIGSYS
167 SETSIGNAL(SIGSYS);
168 #endif
169 #ifdef SIGEMT
170 SETSIGNAL(SIGEMT);
171 #endif
172 #undef SETSIGNAL
174 #endif /* NO_SIGNAL */
176 static void
177 NH_abort()
179 int gdb_prio = SYSOPT_PANICTRACE_GDB;
180 int libc_prio = SYSOPT_PANICTRACE_LIBC;
181 static boolean aborting = FALSE;
183 if (aborting)
184 return;
185 aborting = TRUE;
187 #ifndef VMS
188 if (gdb_prio == libc_prio && gdb_prio > 0)
189 gdb_prio++;
191 if (gdb_prio > libc_prio) {
192 (void) (NH_panictrace_gdb() || (libc_prio && NH_panictrace_libc()));
193 } else {
194 (void) (NH_panictrace_libc() || (gdb_prio && NH_panictrace_gdb()));
197 #else /* VMS */
198 /* overload otherwise unused priority for debug mode: 1 = show
199 traceback and exit; 2 = show traceback and stay in debugger */
200 /* if (wizard && gdb_prio == 1) gdb_prio = 2; */
201 vms_traceback(gdb_prio);
202 (void) libc_prio; /* half-hearted attempt at lint suppression */
204 #endif /* ?VMS */
206 #ifndef NO_SIGNAL
207 panictrace_setsignals(FALSE);
208 #endif
209 NH_abort_();
212 static boolean
213 NH_panictrace_libc()
215 #ifdef PANICTRACE_LIBC
216 void *bt[20];
217 size_t count, x;
218 char **info;
220 raw_print("Generating more information you may report:\n");
221 count = backtrace(bt, SIZE(bt));
222 info = backtrace_symbols(bt, count);
223 for (x = 0; x < count; x++) {
224 raw_printf("[%lu] %s", (unsigned long) x, info[x]);
226 /* free(info); -- Don't risk it. */
227 return TRUE;
228 #else
229 return FALSE;
230 #endif /* !PANICTRACE_LIBC */
234 * fooPATH file system path for foo
235 * fooVAR (possibly const) variable containing fooPATH
237 #ifdef PANICTRACE_GDB
238 #ifdef SYSCF
239 #define GDBVAR sysopt.gdbpath
240 #define GREPVAR sysopt.greppath
241 #else /* SYSCF */
242 #define GDBVAR GDBPATH
243 #define GREPVAR GREPPATH
244 #endif /* SYSCF */
245 #endif /* PANICTRACE_GDB */
247 static boolean
248 NH_panictrace_gdb()
250 #ifdef PANICTRACE_GDB
251 /* A (more) generic method to get a stack trace - invoke
252 * gdb on ourself. */
253 char *gdbpath = GDBVAR;
254 char *greppath = GREPVAR;
255 char buf[BUFSZ];
256 FILE *gdb;
258 if (gdbpath == NULL || gdbpath[0] == 0)
259 return FALSE;
260 if (greppath == NULL || greppath[0] == 0)
261 return FALSE;
263 sprintf(buf, "%s -n -q %s %d 2>&1 | %s '^#'", gdbpath, ARGV0, getpid(),
264 greppath);
265 gdb = popen(buf, "w");
266 if (gdb) {
267 raw_print("Generating more information you may report:\n");
268 fprintf(gdb, "bt\nquit\ny");
269 fflush(gdb);
270 sleep(4); /* ugly */
271 pclose(gdb);
272 return TRUE;
273 } else {
274 return FALSE;
276 #else
277 return FALSE;
278 #endif /* !PANICTRACE_GDB */
280 #endif /* PANICTRACE */
283 * The order of these needs to match the macros in hack.h.
285 static NEARDATA const char *deaths[] = {
286 /* the array of death */
287 "died", "choked", "poisoned", "starvation", "drowning", "burning",
288 "dissolving under the heat and pressure", "crushed", "turned to stone",
289 "turned into slime", "genocided", "panic", "trickery", "quit",
290 "escaped", "ascended"
293 static NEARDATA const char *ends[] = {
294 /* "when you %s" */
295 "died", "choked", "were poisoned",
296 "starved", "drowned", "burned",
297 "dissolved in the lava",
298 "were crushed", "turned to stone",
299 "turned into slime", "were genocided",
300 "panicked", "were tricked", "quit",
301 "escaped", "ascended"
304 static boolean Schroedingers_cat = FALSE;
306 /*ARGSUSED*/
307 void
308 done1(sig_unused) /* called as signal() handler, so sent at least one arg */
309 int sig_unused UNUSED;
311 #ifndef NO_SIGNAL
312 (void) signal(SIGINT, SIG_IGN);
313 #endif
314 if (flags.ignintr) {
315 #ifndef NO_SIGNAL
316 (void) signal(SIGINT, (SIG_RET_TYPE) done1);
317 #endif
318 clear_nhwindow(WIN_MESSAGE);
319 curs_on_u();
320 wait_synch();
321 if (multi > 0)
322 nomul(0);
323 } else {
324 (void) done2();
328 /* "#quit" command or keyboard interrupt */
330 done2()
332 if (!paranoid_query(ParanoidQuit, "Really quit?")) {
333 #ifndef NO_SIGNAL
334 (void) signal(SIGINT, (SIG_RET_TYPE) done1);
335 #endif
336 clear_nhwindow(WIN_MESSAGE);
337 curs_on_u();
338 wait_synch();
339 if (multi > 0)
340 nomul(0);
341 if (multi == 0) {
342 u.uinvulnerable = FALSE; /* avoid ctrl-C bug -dlc */
343 u.usleep = 0;
345 return 0;
347 #if (defined(UNIX) || defined(VMS) || defined(LATTICE))
348 if (wizard) {
349 int c;
350 #ifdef VMS
351 extern int debuggable; /* sys/vms/vmsmisc.c, vmsunix.c */
353 c = !debuggable ? 'n' : ynq("Enter debugger?");
354 #else
355 #ifdef LATTICE
356 c = ynq("Create SnapShot?");
357 #else
358 c = ynq("Dump core?");
359 #endif
360 #endif
361 if (c == 'y') {
362 #ifndef NO_SIGNAL
363 (void) signal(SIGINT, (SIG_RET_TYPE) done1);
364 #endif
365 exit_nhwindows((char *) 0);
366 NH_abort();
367 } else if (c == 'q')
368 done_stopprint++;
370 #endif
371 #ifndef LINT
372 done(QUIT);
373 #endif
374 return 0;
377 #ifndef NO_SIGNAL
378 /*ARGSUSED*/
379 STATIC_PTR void
380 done_intr(sig_unused) /* called as signal() handler, so sent at least 1 arg */
381 int sig_unused UNUSED;
383 done_stopprint++;
384 (void) signal(SIGINT, SIG_IGN);
385 #if defined(UNIX) || defined(VMS)
386 (void) signal(SIGQUIT, SIG_IGN);
387 #endif
388 return;
391 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
392 /* signal() handler */
393 static void
394 done_hangup(sig)
395 int sig;
397 program_state.done_hup++;
398 sethanguphandler((void FDECL((*), (int) )) SIG_IGN);
399 done_intr(sig);
400 return;
402 #endif
403 #endif /* NO_SIGNAL */
405 void
406 done_in_by(mtmp, how)
407 struct monst *mtmp;
408 int how;
410 char buf[BUFSZ];
411 struct permonst *mptr = mtmp->data,
412 *champtr = ((mtmp->cham >= LOW_PM)
413 ? &mons[mtmp->cham]
414 : mptr);
415 boolean distorted = (boolean) (Hallucination && canspotmon(mtmp)),
416 mimicker = (mtmp->m_ap_type == M_AP_MONSTER),
417 imitator = (mptr != champtr || mimicker);
419 You((how == STONING) ? "turn to stone..." : "die...");
420 mark_synch(); /* flush buffered screen output */
421 buf[0] = '\0';
422 killer.format = KILLED_BY_AN;
423 /* "killed by the high priest of Crom" is okay,
424 "killed by the high priest" alone isn't */
425 if ((mptr->geno & G_UNIQ) != 0 && !(imitator && !mimicker)
426 && !(mptr == &mons[PM_HIGH_PRIEST] && !mtmp->ispriest)) {
427 if (!type_is_pname(mptr))
428 Strcat(buf, "the ");
429 killer.format = KILLED_BY;
431 /* _the_ <invisible> <distorted> ghost of Dudley */
432 if (mptr == &mons[PM_GHOST] && has_mname(mtmp)) {
433 Strcat(buf, "the ");
434 killer.format = KILLED_BY;
436 if (mtmp->minvis)
437 Strcat(buf, "invisible ");
438 if (distorted)
439 Strcat(buf, "hallucinogen-distorted ");
441 if (imitator) {
442 char shape[BUFSZ];
443 const char *realnm = champtr->mname, *fakenm = mptr->mname;
444 boolean alt = is_vampshifter(mtmp);
446 if (mimicker) {
447 /* realnm is already correct because champtr==mptr;
448 set up fake mptr for type_is_pname/the_unique_pm */
449 mptr = &mons[mtmp->mappearance];
450 fakenm = mptr->mname;
451 } else if (alt && strstri(realnm, "vampire")
452 && !strcmp(fakenm, "vampire bat")) {
453 /* special case: use "vampire in bat form" in preference
454 to redundant looking "vampire in vampire bat form" */
455 fakenm = "bat";
457 /* for the alternate format, always suppress any article;
458 pname and the_unique should also have s_suffix() applied,
459 but vampires don't take on any shapes which warrant that */
460 if (alt || type_is_pname(mptr)) /* no article */
461 Strcpy(shape, fakenm);
462 else if (the_unique_pm(mptr)) /* "the"; don't use the() here */
463 Sprintf(shape, "the %s", fakenm);
464 else /* "a"/"an" */
465 Strcpy(shape, an(fakenm));
466 /* omit "called" to avoid excessive verbosity */
467 Sprintf(eos(buf),
468 alt ? "%s in %s form"
469 : mimicker ? "%s disguised as %s"
470 : "%s imitating %s",
471 realnm, shape);
472 mptr = mtmp->data; /* reset for mimicker case */
473 } else if (mptr == &mons[PM_GHOST]) {
474 Strcat(buf, "ghost");
475 if (has_mname(mtmp))
476 Sprintf(eos(buf), " of %s", MNAME(mtmp));
477 } else if (mtmp->isshk) {
478 const char *shknm = shkname(mtmp),
479 *honorific = shkname_is_pname(mtmp) ? ""
480 : mtmp->female ? "Ms. " : "Mr. ";
482 Sprintf(eos(buf), "%s%s, the shopkeeper", honorific, shknm);
483 killer.format = KILLED_BY;
484 } else if (mtmp->ispriest || mtmp->isminion) {
485 /* m_monnam() suppresses "the" prefix plus "invisible", and
486 it overrides the effect of Hallucination on priestname() */
487 Strcat(buf, m_monnam(mtmp));
488 } else {
489 Strcat(buf, mptr->mname);
490 if (has_mname(mtmp))
491 Sprintf(eos(buf), " called %s", MNAME(mtmp));
494 Strcpy(killer.name, buf);
495 if (mptr->mlet == S_WRAITH)
496 u.ugrave_arise = PM_WRAITH;
497 else if (mptr->mlet == S_MUMMY && urace.mummynum != NON_PM)
498 u.ugrave_arise = urace.mummynum;
499 else if (mptr->mlet == S_VAMPIRE && Race_if(PM_HUMAN))
500 u.ugrave_arise = PM_VAMPIRE;
501 else if (mptr == &mons[PM_GHOUL])
502 u.ugrave_arise = PM_GHOUL;
503 /* this could happen if a high-end vampire kills the hero
504 when ordinary vampires are genocided; ditto for wraiths */
505 if (u.ugrave_arise >= LOW_PM
506 && (mvitals[u.ugrave_arise].mvflags & G_GENOD))
507 u.ugrave_arise = NON_PM;
509 done(how);
510 return;
513 /* some special cases for overriding while-helpless reason */
514 static const struct {
515 int why, unmulti;
516 const char *exclude, *include;
517 } death_fixups[] = {
518 /* "petrified by <foo>, while getting stoned" -- "while getting stoned"
519 prevented any last-second recovery, but it was not the cause of
520 "petrified by <foo>" */
521 { STONING, 1, "getting stoned", (char *) 0 },
522 /* "died of starvation, while fainted from lack of food" is accurate
523 but sounds a fairly silly (and doesn't actually appear unless you
524 splice together death and while-helpless from xlogfile) */
525 { STARVING, 0, "fainted from lack of food", "fainted" },
528 /* clear away while-helpless when the cause of death caused that
529 helplessness (ie, "petrified by <foo> while getting stoned") */
530 STATIC_DCL void
531 fixup_death(how)
532 int how;
534 int i;
536 if (multi_reason) {
537 for (i = 0; i < SIZE(death_fixups); ++i)
538 if (death_fixups[i].why == how
539 && !strcmp(death_fixups[i].exclude, multi_reason)) {
540 if (death_fixups[i].include) /* substitute alternate reason */
541 multi_reason = death_fixups[i].include;
542 else /* remove the helplessness reason */
543 multi_reason = (char *) 0;
544 if (death_fixups[i].unmulti) /* possibly hide helplessness */
545 multi = 0L;
546 break;
551 #if defined(WIN32) && !defined(SYSCF)
552 #define NOTIFY_NETHACK_BUGS
553 #endif
555 /*VARARGS1*/
556 void panic
557 VA_DECL(const char *, str)
559 VA_START(str);
560 VA_INIT(str, char *);
562 if (program_state.panicking++)
563 NH_abort(); /* avoid loops - this should never happen*/
565 if (iflags.window_inited) {
566 raw_print("\r\nOops...");
567 wait_synch(); /* make sure all pending output gets flushed */
568 exit_nhwindows((char *) 0);
569 iflags.window_inited = 0; /* they're gone; force raw_print()ing */
572 raw_print(program_state.gameover
573 ? "Postgame wrapup disrupted."
574 : !program_state.something_worth_saving
575 ? "Program initialization has failed."
576 : "Suddenly, the dungeon collapses.");
577 #ifndef MICRO
578 #if defined(NOTIFY_NETHACK_BUGS)
579 if (!wizard)
580 raw_printf("Report the following error to \"%s\" or at \"%s\".",
581 DEVTEAM_EMAIL, DEVTEAM_URL);
582 else if (program_state.something_worth_saving)
583 raw_print("\nError save file being written.\n");
584 #else
585 if (!wizard) {
586 const char *maybe_rebuild = !program_state.something_worth_saving
587 ? "."
588 : "\nand it may be possible to rebuild.";
590 if (sysopt.support)
591 raw_printf("To report this error, %s%s", sysopt.support,
592 maybe_rebuild);
593 else if (sysopt.fmtd_wizard_list) /* formatted SYSCF WIZARDS */
594 raw_printf("To report this error, contact %s%s",
595 sysopt.fmtd_wizard_list, maybe_rebuild);
596 else
597 raw_printf("Report error to \"%s\"%s", WIZARD_NAME,
598 maybe_rebuild);
600 #endif
601 /* XXX can we move this above the prints? Then we'd be able to
602 * suppress "it may be possible to rebuild" based on dosave0()
603 * or say it's NOT possible to rebuild. */
604 if (program_state.something_worth_saving) {
605 set_error_savefile();
606 if (dosave0()) {
607 /* os/win port specific recover instructions */
608 if (sysopt.recover)
609 raw_printf("%s", sysopt.recover);
612 #endif
614 char buf[BUFSZ];
616 Vsprintf(buf, str, VA_ARGS);
617 raw_print(buf);
618 paniclog("panic", buf);
620 #ifdef WIN32
621 interject(INTERJECT_PANIC);
622 #endif
623 #if defined(UNIX) || defined(VMS) || defined(LATTICE) || defined(WIN32)
624 if (wizard)
625 NH_abort(); /* generate core dump */
626 #endif
627 VA_END();
628 really_done(PANICKED);
631 STATIC_OVL boolean
632 should_query_disclose_option(category, defquery)
633 int category;
634 char *defquery;
636 int idx;
637 char disclose, *dop;
639 *defquery = 'n';
640 if ((dop = index(disclosure_options, category)) != 0) {
641 idx = (int) (dop - disclosure_options);
642 if (idx < 0 || idx >= NUM_DISCLOSURE_OPTIONS) {
643 impossible(
644 "should_query_disclose_option: bad disclosure index %d %c",
645 idx, category);
646 *defquery = DISCLOSE_PROMPT_DEFAULT_YES;
647 return TRUE;
649 disclose = flags.end_disclose[idx];
650 if (disclose == DISCLOSE_YES_WITHOUT_PROMPT) {
651 *defquery = 'y';
652 return FALSE;
653 } else if (disclose == DISCLOSE_SPECIAL_WITHOUT_PROMPT) {
654 *defquery = 'a';
655 return FALSE;
656 } else if (disclose == DISCLOSE_NO_WITHOUT_PROMPT) {
657 *defquery = 'n';
658 return FALSE;
659 } else if (disclose == DISCLOSE_PROMPT_DEFAULT_YES) {
660 *defquery = 'y';
661 return TRUE;
662 } else if (disclose == DISCLOSE_PROMPT_DEFAULT_SPECIAL) {
663 *defquery = 'a';
664 return TRUE;
665 } else {
666 *defquery = 'n';
667 return TRUE;
670 impossible("should_query_disclose_option: bad category %c", category);
671 return TRUE;
674 #ifdef DUMPLOG
675 STATIC_OVL void
676 dump_plines()
678 int i, j;
679 char buf[BUFSZ], **strp;
680 extern char *saved_plines[];
681 extern unsigned saved_pline_index;
683 Strcpy(buf, " ");
684 putstr(0, 0, "");
685 putstr(0, 0, "Latest messages:");
686 for (i = 0, j = (int) saved_pline_index; i < DUMPLOG_MSG_COUNT;
687 ++i, j = (j + 1) % DUMPLOG_MSG_COUNT) {
688 strp = &saved_plines[j];
689 if (*strp) {
690 copynchars(&buf[1], *strp, BUFSZ - 1 - 1);
691 putstr(0, 0, buf);
692 #ifdef FREE_ALL_MEMORY
693 free(*strp), *strp = 0;
694 #endif
698 #endif
700 STATIC_OVL void
701 dump_everything(how)
702 int how;
704 #ifdef DUMPLOG
705 struct obj *obj;
706 char pbuf[BUFSZ];
708 dump_redirect(TRUE);
709 if (!iflags.in_dumplog)
710 return;
712 init_symbols();
714 for (obj = invent; obj; obj = obj->nobj) {
715 makeknown(obj->otyp);
716 obj->known = obj->bknown = obj->dknown = obj->rknown = 1;
717 if (Is_container(obj) || obj->otyp == STATUE)
718 obj->cknown = obj->lknown = 1;
721 Sprintf(pbuf, "%s, %s %s %s %s", plname,
722 aligns[1 - u.ualign.type].adj,
723 genders[flags.female].adj,
724 urace.adj,
725 (flags.female && urole.name.f) ? urole.name.f : urole.name.m);
726 putstr(0, 0, pbuf);
727 putstr(0, 0, "");
729 dump_map();
730 putstr(0, 0, do_statusline1());
731 putstr(0, 0, do_statusline2());
732 putstr(0, 0, "");
734 dump_plines();
735 putstr(0, 0, "");
736 putstr(0, 0, "Inventory:");
737 display_inventory((char *) 0, TRUE);
738 container_contents(invent, TRUE, TRUE, FALSE);
739 enlightenment((BASICENLIGHTENMENT | MAGICENLIGHTENMENT),
740 (how >= PANICKED) ? ENL_GAMEOVERALIVE
741 : ENL_GAMEOVERDEAD);
742 putstr(0, 0, "");
743 list_vanquished('y', FALSE);
744 putstr(0, 0, "");
745 list_genocided('a', FALSE);
746 putstr(0, 0, "");
747 show_conduct((how >= PANICKED) ? 1 : 2);
748 putstr(0, 0, "");
749 show_overview((how >= PANICKED) ? 1 : 2, how);
750 putstr(0, 0, "");
751 dump_redirect(FALSE);
752 #else
753 nhUse(how);
754 #endif
757 STATIC_OVL void
758 disclose(how, taken)
759 int how;
760 boolean taken;
762 char c = '\0', defquery;
763 char qbuf[QBUFSZ];
764 boolean ask = FALSE;
766 if (invent && !done_stopprint) {
767 if (taken)
768 Sprintf(qbuf, "Do you want to see what you had when you %s?",
769 (how == QUIT) ? "quit" : "died");
770 else
771 Strcpy(qbuf, "Do you want your possessions identified?");
773 ask = should_query_disclose_option('i', &defquery);
774 c = ask ? yn_function(qbuf, ynqchars, defquery) : defquery;
775 if (c == 'y') {
776 struct obj *obj;
778 for (obj = invent; obj; obj = obj->nobj) {
779 makeknown(obj->otyp);
780 obj->known = obj->bknown = obj->dknown = obj->rknown = 1;
781 if (Is_container(obj) || obj->otyp == STATUE)
782 obj->cknown = obj->lknown = 1;
784 (void) display_inventory((char *) 0, TRUE);
785 container_contents(invent, TRUE, TRUE, FALSE);
787 if (c == 'q')
788 done_stopprint++;
791 if (!done_stopprint) {
792 ask = should_query_disclose_option('a', &defquery);
793 c = ask ? yn_function("Do you want to see your attributes?", ynqchars,
794 defquery)
795 : defquery;
796 if (c == 'y')
797 enlightenment((BASICENLIGHTENMENT | MAGICENLIGHTENMENT),
798 (how >= PANICKED) ? ENL_GAMEOVERALIVE
799 : ENL_GAMEOVERDEAD);
800 if (c == 'q')
801 done_stopprint++;
804 if (!done_stopprint) {
805 ask = should_query_disclose_option('v', &defquery);
806 list_vanquished(defquery, ask);
809 if (!done_stopprint) {
810 ask = should_query_disclose_option('g', &defquery);
811 list_genocided(defquery, ask);
814 if (!done_stopprint) {
815 ask = should_query_disclose_option('c', &defquery);
816 c = ask ? yn_function("Do you want to see your conduct?", ynqchars,
817 defquery)
818 : defquery;
819 if (c == 'y')
820 show_conduct((how >= PANICKED) ? 1 : 2);
821 if (c == 'q')
822 done_stopprint++;
825 if (!done_stopprint) {
826 ask = should_query_disclose_option('o', &defquery);
827 c = ask ? yn_function("Do you want to see the dungeon overview?",
828 ynqchars, defquery)
829 : defquery;
830 if (c == 'y')
831 show_overview((how >= PANICKED) ? 1 : 2, how);
832 if (c == 'q')
833 done_stopprint++;
837 /* try to get the player back in a viable state after being killed */
838 STATIC_OVL void
839 savelife(how)
840 int how;
842 int uhpmin = max(2 * u.ulevel, 10);
844 if (u.uhpmax < uhpmin)
845 u.uhpmax = uhpmin;
846 u.uhp = u.uhpmax;
847 u.uswldtim = 0;
848 if (u.uhunger < 500) {
849 u.uhunger = 500;
850 newuhs(FALSE);
852 /* cure impending doom of sickness hero won't have time to fix */
853 if ((Sick & TIMEOUT) == 1L) {
854 u.usick_type = 0;
855 set_itimeout(&Sick, 0L);
857 if (how == CHOKING)
858 init_uhunger();
859 nomovemsg = "You survived that attempt on your life.";
860 context.move = 0;
861 if (multi > 0)
862 multi = 0;
863 else
864 multi = -1;
865 if (u.utrap && u.utraptype == TT_LAVA)
866 u.utrap = 0;
867 context.botl = 1;
868 u.ugrave_arise = NON_PM;
869 HUnchanging = 0L;
870 curs_on_u();
871 if (!context.mon_moving)
872 endmultishot(FALSE);
876 * Get valuables from the given list. Revised code: the list always remains
877 * intact.
879 STATIC_OVL void
880 get_valuables(list)
881 struct obj *list; /* inventory or container contents */
883 register struct obj *obj;
884 register int i;
886 /* find amulets and gems, ignoring all artifacts */
887 for (obj = list; obj; obj = obj->nobj)
888 if (Has_contents(obj)) {
889 get_valuables(obj->cobj);
890 } else if (obj->oartifact) {
891 continue;
892 } else if (obj->oclass == AMULET_CLASS) {
893 i = obj->otyp - FIRST_AMULET;
894 if (!amulets[i].count) {
895 amulets[i].count = obj->quan;
896 amulets[i].typ = obj->otyp;
897 } else
898 amulets[i].count += obj->quan; /* always adds one */
899 } else if (obj->oclass == GEM_CLASS && obj->otyp < LUCKSTONE) {
900 i = min(obj->otyp, LAST_GEM + 1) - FIRST_GEM;
901 if (!gems[i].count) {
902 gems[i].count = obj->quan;
903 gems[i].typ = obj->otyp;
904 } else
905 gems[i].count += obj->quan;
907 return;
911 * Sort collected valuables, most frequent to least. We could just
912 * as easily use qsort, but we don't care about efficiency here.
914 STATIC_OVL void
915 sort_valuables(list, size)
916 struct valuable_data list[];
917 int size; /* max value is less than 20 */
919 register int i, j;
920 struct valuable_data ltmp;
922 /* move greater quantities to the front of the list */
923 for (i = 1; i < size; i++) {
924 if (list[i].count == 0)
925 continue; /* empty slot */
926 ltmp = list[i]; /* structure copy */
927 for (j = i; j > 0; --j)
928 if (list[j - 1].count >= ltmp.count)
929 break;
930 else {
931 list[j] = list[j - 1];
933 list[j] = ltmp;
935 return;
938 #define CAT_CHECK 2
940 STATIC_OVL boolean
941 odds_and_ends(list, what)
942 struct obj *list;
943 int what;
945 struct obj *otmp;
946 for (otmp = list; otmp; otmp = otmp->nobj) {
947 switch (what) {
948 case CAT_CHECK: /* Schroedinger's Cat */
949 /* Ascending is deterministic */
950 if (SchroedingersBox(otmp))
951 return rn2(2);
952 break;
954 if (Has_contents(otmp))
955 return odds_and_ends(otmp->cobj, what);
957 return FALSE;
960 /* called twice; first to calculate total, then to list relevant items */
961 STATIC_OVL void
962 artifact_score(list, counting, endwin)
963 struct obj *list;
964 boolean counting; /* true => add up points; false => display them */
965 winid endwin;
967 char pbuf[BUFSZ];
968 struct obj *otmp;
969 long value, points;
970 short dummy; /* object type returned by artifact_name() */
972 for (otmp = list; otmp; otmp = otmp->nobj) {
973 if (otmp->oartifact || otmp->otyp == BELL_OF_OPENING
974 || otmp->otyp == SPE_BOOK_OF_THE_DEAD
975 || otmp->otyp == CANDELABRUM_OF_INVOCATION) {
976 value = arti_cost(otmp); /* zorkmid value */
977 points = value * 5 / 2; /* score value */
978 if (counting) {
979 nowrap_add(u.urexp, points);
980 } else {
981 makeknown(otmp->otyp);
982 otmp->known = otmp->dknown = otmp->bknown = otmp->rknown = 1;
983 /* assumes artifacts don't have quan > 1 */
984 Sprintf(pbuf, "%s%s (worth %ld %s and %ld points)",
985 the_unique_obj(otmp) ? "The " : "",
986 otmp->oartifact ? artifact_name(xname(otmp), &dummy)
987 : OBJ_NAME(objects[otmp->otyp]),
988 value, currency(value), points);
989 putstr(endwin, 0, pbuf);
992 if (Has_contents(otmp))
993 artifact_score(otmp->cobj, counting, endwin);
997 /* Be careful not to call panic from here! */
998 void
999 done(how)
1000 int how;
1002 if (how == TRICKED) {
1003 if (killer.name[0]) {
1004 paniclog("trickery", killer.name);
1005 killer.name[0] = 0;
1007 if (wizard) {
1008 You("are a very tricky wizard, it seems.");
1009 return;
1012 if (program_state.panicking
1013 #ifdef HANGUPHANDLING
1014 || program_state.done_hup
1015 #endif
1017 /* skip status update if panicking or disconnected */
1018 context.botl = context.botlx = FALSE;
1019 } else {
1020 /* otherwise force full status update */
1021 context.botlx = TRUE;
1022 bot();
1025 if (how == ASCENDED || (!killer.name[0] && how == GENOCIDED))
1026 killer.format = NO_KILLER_PREFIX;
1027 /* Avoid killed by "a" burning or "a" starvation */
1028 if (!killer.name[0] && (how == STARVING || how == BURNING))
1029 killer.format = KILLED_BY;
1030 if (!killer.name[0] || how >= PANICKED)
1031 Strcpy(killer.name, deaths[how]);
1033 if (how < PANICKED)
1034 u.umortality++;
1035 if (Lifesaved && (how <= GENOCIDED)) {
1036 pline("But wait...");
1037 makeknown(AMULET_OF_LIFE_SAVING);
1038 Your("medallion %s!", !Blind ? "begins to glow" : "feels warm");
1039 if (how == CHOKING)
1040 You("vomit ...");
1041 You_feel("much better!");
1042 pline_The("medallion crumbles to dust!");
1043 if (uamul)
1044 useup(uamul);
1046 (void) adjattrib(A_CON, -1, TRUE);
1047 savelife(how);
1048 if (how == GENOCIDED) {
1049 pline("Unfortunately you are still genocided...");
1050 } else {
1051 killer.name[0] = 0;
1052 killer.format = 0;
1053 return;
1056 if ((wizard || discover) && (how <= GENOCIDED)
1057 && !paranoid_query(ParanoidDie, "Die?")) {
1058 pline("OK, so you don't %s.", (how == CHOKING) ? "choke" : "die");
1059 savelife(how);
1060 killer.name[0] = 0;
1061 killer.format = 0;
1062 return;
1064 really_done(how);
1067 /* separated from done() in order to specify the __noreturn__ attribute */
1068 STATIC_OVL void
1069 really_done(how)
1070 int how;
1072 boolean taken;
1073 char pbuf[BUFSZ];
1074 winid endwin = WIN_ERR;
1075 boolean bones_ok, have_windows = iflags.window_inited;
1076 struct obj *corpse = (struct obj *) 0;
1077 time_t endtime;
1078 long umoney;
1079 long tmp;
1082 * The game is now over...
1084 program_state.gameover = 1;
1085 /* in case of a subsequent panic(), there's no point trying to save */
1086 program_state.something_worth_saving = 0;
1087 /* render vision subsystem inoperative */
1088 iflags.vision_inited = 0;
1090 /* might have been killed while using a disposable item, so make sure
1091 it's gone prior to inventory disclosure and creation of bones data */
1092 inven_inuse(TRUE);
1093 /* maybe not on object lists; if an active light source, would cause
1094 big trouble (`obj_is_local' panic) for savebones() -> savelev() */
1095 if (thrownobj && thrownobj->where == OBJ_FREE)
1096 dealloc_obj(thrownobj);
1097 if (kickedobj && kickedobj->where == OBJ_FREE)
1098 dealloc_obj(kickedobj);
1100 /* remember time of death here instead of having bones, rip, and
1101 topten figure it out separately and possibly getting different
1102 time or even day if player is slow responding to --More-- */
1103 urealtime.finish_time = endtime = getnow();
1104 urealtime.realtime += (long) (endtime - urealtime.start_timing);
1106 dump_open_log(endtime);
1107 /* Sometimes you die on the first move. Life's not fair.
1108 * On those rare occasions you get hosed immediately, go out
1109 * smiling... :-) -3.
1111 if (moves <= 1 && how < PANICKED) /* You die... --More-- */
1112 pline("Do not pass go. Do not collect 200 %s.", currency(200L));
1114 if (have_windows)
1115 wait_synch(); /* flush screen output */
1116 #ifndef NO_SIGNAL
1117 (void) signal(SIGINT, (SIG_RET_TYPE) done_intr);
1118 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
1119 (void) signal(SIGQUIT, (SIG_RET_TYPE) done_intr);
1120 sethanguphandler(done_hangup);
1121 #endif
1122 #endif /* NO_SIGNAL */
1124 bones_ok = (how < GENOCIDED) && can_make_bones();
1126 if (bones_ok && launch_in_progress())
1127 force_launch_placement();
1129 /* maintain ugrave_arise even for !bones_ok */
1130 if (how == PANICKED)
1131 u.ugrave_arise = (NON_PM - 3); /* no corpse, no grave */
1132 else if (how == BURNING || how == DISSOLVED) /* corpse burns up too */
1133 u.ugrave_arise = (NON_PM - 2); /* leave no corpse */
1134 else if (how == STONING)
1135 u.ugrave_arise = (NON_PM - 1); /* statue instead of corpse */
1136 else if (how == TURNED_SLIME)
1137 u.ugrave_arise = PM_GREEN_SLIME;
1139 /* if pets will contribute to score, populate mydogs list now
1140 (bones creation isn't a factor, but pline() messaging is) */
1141 if (how == ESCAPED || how == ASCENDED)
1142 keepdogs(TRUE);
1144 if (how == QUIT) {
1145 killer.format = NO_KILLER_PREFIX;
1146 if (u.uhp < 1) {
1147 how = DIED;
1148 u.umortality++; /* skipped above when how==QUIT */
1149 Strcpy(killer.name, "quit while already on Charon's boat");
1152 if (how == ESCAPED || how == PANICKED)
1153 killer.format = NO_KILLER_PREFIX;
1155 fixup_death(how); /* actually, fixup multi_reason */
1157 if (how != PANICKED) {
1158 /* these affect score and/or bones, but avoid them during panic */
1159 taken = paybill((how == ESCAPED) ? -1 : (how != QUIT));
1160 paygd();
1161 clearpriests();
1162 } else
1163 taken = FALSE; /* lint; assert( !bones_ok ); */
1165 clearlocks();
1167 if (have_windows)
1168 display_nhwindow(WIN_MESSAGE, FALSE);
1170 if (strcmp(flags.end_disclose, "none") && how != PANICKED)
1171 disclose(how, taken);
1173 dump_everything(how);
1175 /* finish_paybill should be called after disclosure but before bones */
1176 if (bones_ok && taken)
1177 finish_paybill();
1179 /* grave creation should be after disclosure so it doesn't have
1180 this grave in the current level's features for #overview */
1181 if (bones_ok && u.ugrave_arise == NON_PM
1182 && !(mvitals[u.umonnum].mvflags & G_NOCORPSE)) {
1183 int mnum = u.umonnum;
1185 if (!Upolyd) {
1186 /* Base corpse on race when not poly'd since original
1187 * u.umonnum is based on role, and all role monsters
1188 * are human.
1190 mnum = (flags.female && urace.femalenum != NON_PM)
1191 ? urace.femalenum
1192 : urace.malenum;
1194 corpse = mk_named_object(CORPSE, &mons[mnum], u.ux, u.uy, plname);
1195 Sprintf(pbuf, "%s, ", plname);
1196 formatkiller(eos(pbuf), sizeof pbuf - strlen(pbuf), how, TRUE);
1197 make_grave(u.ux, u.uy, pbuf);
1199 pbuf[0] = '\0'; /* clear grave text; also lint suppression */
1201 /* calculate score, before creating bones [container gold] */
1203 int deepest = deepest_lev_reached(FALSE);
1205 umoney = money_cnt(invent);
1206 tmp = u.umoney0;
1207 umoney += hidden_gold(); /* accumulate gold from containers */
1208 tmp = umoney - tmp; /* net gain */
1210 if (tmp < 0L)
1211 tmp = 0L;
1212 if (how < PANICKED)
1213 tmp -= tmp / 10L;
1214 tmp += 50L * (long) (deepest - 1);
1215 if (deepest > 20)
1216 tmp += 1000L * (long) ((deepest > 30) ? 10 : deepest - 20);
1217 nowrap_add(u.urexp, tmp);
1219 /* ascension gives a score bonus iff offering to original deity */
1220 if (how == ASCENDED && u.ualign.type == u.ualignbase[A_ORIGINAL]) {
1221 /* retaining original alignment: score *= 2;
1222 converting, then using helm-of-OA to switch back: *= 1.5 */
1223 tmp = (u.ualignbase[A_CURRENT] == u.ualignbase[A_ORIGINAL])
1224 ? u.urexp
1225 : (u.urexp / 2L);
1226 nowrap_add(u.urexp, tmp);
1230 if (u.ugrave_arise >= LOW_PM && u.ugrave_arise != PM_GREEN_SLIME) {
1231 /* give this feedback even if bones aren't going to be created,
1232 so that its presence or absence doesn't tip off the player to
1233 new bones or their lack; it might be a lie if makemon fails */
1234 Your("body rises from the dead as %s...",
1235 an(mons[u.ugrave_arise].mname));
1236 display_nhwindow(WIN_MESSAGE, FALSE);
1239 if (bones_ok) {
1240 if (!wizard || paranoid_query(ParanoidBones, "Save bones?"))
1241 savebones(how, endtime, corpse);
1242 /* corpse may be invalid pointer now so
1243 ensure that it isn't used again */
1244 corpse = (struct obj *) 0;
1247 /* update gold for the rip output, which can't use hidden_gold()
1248 (containers will be gone by then if bones just got saved...) */
1249 done_money = umoney;
1251 /* clean up unneeded windows */
1252 if (have_windows) {
1253 wait_synch();
1254 free_pickinv_cache(); /* extra persistent window if perm_invent */
1255 if (WIN_INVEN != WIN_ERR)
1256 destroy_nhwindow(WIN_INVEN), WIN_INVEN = WIN_ERR;
1257 display_nhwindow(WIN_MESSAGE, TRUE);
1258 destroy_nhwindow(WIN_MAP), WIN_MAP = WIN_ERR;
1259 #ifndef STATUS_VIA_WINDOWPORT
1260 destroy_nhwindow(WIN_STATUS), WIN_STATUS = WIN_ERR;
1261 #endif
1262 destroy_nhwindow(WIN_MESSAGE), WIN_MESSAGE = WIN_ERR;
1264 if (!done_stopprint || flags.tombstone)
1265 endwin = create_nhwindow(NHW_TEXT);
1267 if (how < GENOCIDED && flags.tombstone && endwin != WIN_ERR)
1268 outrip(endwin, how, endtime);
1269 } else
1270 done_stopprint = 1; /* just avoid any more output */
1272 #ifdef DUMPLOG
1273 dump_redirect(TRUE);
1274 genl_outrip(0, how, endtime);
1275 dump_redirect(FALSE);
1276 #endif
1277 if (u.uhave.amulet) {
1278 Strcat(killer.name, " (with the Amulet)");
1279 } else if (how == ESCAPED) {
1280 if (Is_astralevel(&u.uz)) /* offered Amulet to wrong deity */
1281 Strcat(killer.name, " (in celestial disgrace)");
1282 else if (carrying(FAKE_AMULET_OF_YENDOR))
1283 Strcat(killer.name, " (with a fake Amulet)");
1284 /* don't bother counting to see whether it should be plural */
1287 Sprintf(pbuf, "%s %s the %s...", Goodbye(), plname,
1288 (how != ASCENDED)
1289 ? (const char *) ((flags.female && urole.name.f)
1290 ? urole.name.f
1291 : urole.name.m)
1292 : (const char *) (flags.female ? "Demigoddess" : "Demigod"));
1293 dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
1294 dump_forward_putstr(endwin, 0, "", done_stopprint);
1296 if (how == ESCAPED || how == ASCENDED) {
1297 struct monst *mtmp;
1298 struct obj *otmp;
1299 register struct val_list *val;
1300 register int i;
1302 for (val = valuables; val->list; val++)
1303 for (i = 0; i < val->size; i++) {
1304 val->list[i].count = 0L;
1306 get_valuables(invent);
1308 /* add points for collected valuables */
1309 for (val = valuables; val->list; val++)
1310 for (i = 0; i < val->size; i++)
1311 if (val->list[i].count != 0L) {
1312 tmp = val->list[i].count
1313 * (long) objects[val->list[i].typ].oc_cost;
1314 nowrap_add(u.urexp, tmp);
1317 /* count the points for artifacts */
1318 artifact_score(invent, TRUE, endwin);
1319 #ifdef DUMPLOG
1320 dump_redirect(TRUE);
1321 artifact_score(invent, TRUE, endwin);
1322 dump_redirect(FALSE);
1323 #endif
1325 viz_array[0][0] |= IN_SIGHT; /* need visibility for naming */
1326 mtmp = mydogs;
1327 Strcpy(pbuf, "You");
1328 if (!Schroedingers_cat) /* check here in case disclosure was off */
1329 Schroedingers_cat = odds_and_ends(invent, CAT_CHECK);
1330 if (Schroedingers_cat) {
1331 int mhp, m_lev = adj_lev(&mons[PM_HOUSECAT]);
1332 mhp = d(m_lev, 8);
1333 nowrap_add(u.urexp, mhp);
1334 Strcat(eos(pbuf), " and Schroedinger's cat");
1336 if (mtmp) {
1337 while (mtmp) {
1338 Sprintf(eos(pbuf), " and %s", mon_nam(mtmp));
1339 if (mtmp->mtame)
1340 nowrap_add(u.urexp, mtmp->mhp);
1341 mtmp = mtmp->nmon;
1343 dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
1344 pbuf[0] = '\0';
1345 } else {
1346 Strcat(pbuf, " ");
1348 Sprintf(eos(pbuf), "%s with %ld point%s,",
1349 how == ASCENDED ? "went to your reward"
1350 : "escaped from the dungeon",
1351 u.urexp, plur(u.urexp));
1352 dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
1354 if (!done_stopprint)
1355 artifact_score(invent, FALSE, endwin); /* list artifacts */
1356 #if DUMPLOG
1357 dump_redirect(TRUE);
1358 artifact_score(invent, FALSE, 0);
1359 dump_redirect(FALSE);
1360 #endif
1362 /* list valuables here */
1363 for (val = valuables; val->list; val++) {
1364 sort_valuables(val->list, val->size);
1365 for (i = 0; i < val->size && !done_stopprint; i++) {
1366 int typ = val->list[i].typ;
1367 long count = val->list[i].count;
1369 if (count == 0L)
1370 continue;
1371 if (objects[typ].oc_class != GEM_CLASS || typ <= LAST_GEM) {
1372 otmp = mksobj(typ, FALSE, FALSE);
1373 makeknown(otmp->otyp);
1374 otmp->known = 1; /* for fake amulets */
1375 otmp->dknown = 1; /* seen it (blindness fix) */
1376 if (has_oname(otmp))
1377 free_oname(otmp);
1378 otmp->quan = count;
1379 Sprintf(pbuf, "%8ld %s (worth %ld %s),", count,
1380 xname(otmp), count * (long) objects[typ].oc_cost,
1381 currency(2L));
1382 obfree(otmp, (struct obj *) 0);
1383 } else {
1384 Sprintf(pbuf, "%8ld worthless piece%s of colored glass,",
1385 count, plur(count));
1387 dump_forward_putstr(endwin, 0, pbuf, 0);
1391 } else {
1392 /* did not escape or ascend */
1393 if (u.uz.dnum == 0 && u.uz.dlevel <= 0) {
1394 /* level teleported out of the dungeon; `how' is DIED,
1395 due to falling or to "arriving at heaven prematurely" */
1396 Sprintf(pbuf, "You %s beyond the confines of the dungeon",
1397 (u.uz.dlevel < 0) ? "passed away" : ends[how]);
1398 } else {
1399 /* more conventional demise */
1400 const char *where = dungeons[u.uz.dnum].dname;
1402 if (Is_astralevel(&u.uz))
1403 where = "The Astral Plane";
1404 Sprintf(pbuf, "You %s in %s", ends[how], where);
1405 if (!In_endgame(&u.uz) && !Is_knox(&u.uz))
1406 Sprintf(eos(pbuf), " on dungeon level %d",
1407 In_quest(&u.uz) ? dunlev(&u.uz) : depth(&u.uz));
1410 Sprintf(eos(pbuf), " with %ld point%s,", u.urexp, plur(u.urexp));
1411 dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
1414 Sprintf(pbuf, "and %ld piece%s of gold, after %ld move%s.", umoney,
1415 plur(umoney), moves, plur(moves));
1416 dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
1417 Sprintf(pbuf,
1418 "You were level %d with a maximum of %d hit point%s when you %s.",
1419 u.ulevel, u.uhpmax, plur(u.uhpmax), ends[how]);
1420 dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
1421 dump_forward_putstr(endwin, 0, "", done_stopprint);
1422 if (!done_stopprint)
1423 display_nhwindow(endwin, TRUE);
1424 if (endwin != WIN_ERR)
1425 destroy_nhwindow(endwin);
1427 dump_close_log();
1428 /* "So when I die, the first thing I will see in Heaven is a
1429 * score list?" */
1430 if (have_windows && !iflags.toptenwin)
1431 exit_nhwindows((char *) 0), have_windows = FALSE;
1432 topten(how, endtime);
1433 if (have_windows)
1434 exit_nhwindows((char *) 0);
1436 if (done_stopprint) {
1437 raw_print("");
1438 raw_print("");
1440 terminate(EXIT_SUCCESS);
1443 void
1444 container_contents(list, identified, all_containers, reportempty)
1445 struct obj *list;
1446 boolean identified, all_containers, reportempty;
1448 register struct obj *box, *obj;
1449 char buf[BUFSZ];
1450 boolean cat, deadcat;
1452 for (box = list; box; box = box->nobj) {
1453 if (Is_container(box) || box->otyp == STATUE) {
1454 box->cknown = 1; /* we're looking at the contents now */
1455 if (identified)
1456 box->lknown = 1;
1457 cat = deadcat = FALSE;
1458 if (SchroedingersBox(box) && !Schroedingers_cat) {
1459 /* Schroedinger's Cat? */
1460 cat = odds_and_ends(box, CAT_CHECK);
1461 if (cat)
1462 Schroedingers_cat = TRUE;
1463 else
1464 deadcat = TRUE;
1465 box->spe = 0;
1467 if (box->otyp == BAG_OF_TRICKS) {
1468 continue; /* wrong type of container */
1469 } else if (box->cobj) {
1470 winid tmpwin = create_nhwindow(NHW_MENU);
1472 sortloot(&box->cobj,
1473 (((flags.sortloot == 'l' || flags.sortloot == 'f')
1474 ? SORTLOOT_LOOT : 0)
1475 | (flags.sortpack ? SORTLOOT_PACK : 0)),
1476 FALSE);
1477 Sprintf(buf, "Contents of %s:", the(xname(box)));
1478 putstr(tmpwin, 0, buf);
1479 putstr(tmpwin, 0, "");
1480 for (obj = box->cobj; obj; obj = obj->nobj) {
1481 if (identified) {
1482 makeknown(obj->otyp);
1483 obj->known = obj->bknown = obj->dknown
1484 = obj->rknown = 1;
1485 if (Is_container(obj) || obj->otyp == STATUE)
1486 obj->cknown = obj->lknown = 1;
1488 putstr(tmpwin, 0, doname(obj));
1490 if (cat)
1491 putstr(tmpwin, 0, "Schroedinger's cat");
1492 else if (deadcat)
1493 putstr(tmpwin, 0, "Schroedinger's dead cat");
1494 display_nhwindow(tmpwin, TRUE);
1495 destroy_nhwindow(tmpwin);
1496 if (all_containers)
1497 container_contents(box->cobj, identified, TRUE,
1498 reportempty);
1499 } else if (cat || deadcat) {
1500 pline("%s Schroedinger's %scat!", Tobjnam(box, "contain"),
1501 deadcat ? "dead " : "");
1502 display_nhwindow(WIN_MESSAGE, FALSE);
1503 } else if (reportempty) {
1504 pline("%s is empty.", upstart(thesimpleoname(box)));
1505 display_nhwindow(WIN_MESSAGE, FALSE);
1508 if (!all_containers)
1509 break;
1513 /* should be called with either EXIT_SUCCESS or EXIT_FAILURE */
1514 void
1515 terminate(status)
1516 int status;
1518 program_state.in_moveloop = 0; /* won't be returning to normal play */
1519 #ifdef MAC
1520 getreturn("to exit");
1521 #endif
1522 /* don't bother to try to release memory if we're in panic mode, to
1523 avoid trouble in case that happens to be due to memory problems */
1524 if (!program_state.panicking) {
1525 freedynamicdata();
1526 dlb_cleanup();
1529 #ifdef VMS
1531 * This is liable to draw a warning if compiled with gcc, but it's
1532 * more important to flag panic() -> really_done() -> terminate()
1533 * as __noreturn__ then to avoid the warning.
1535 /* don't call exit() if already executing within an exit handler;
1536 that would cancel any other pending user-mode handlers */
1537 if (program_state.exiting)
1538 return;
1539 #endif
1540 program_state.exiting = 1;
1541 nethack_exit(status);
1544 extern const int monstr[];
1546 static const char *vanqorders[] = {
1547 "traditional: by monster level, by internal monster index",
1548 #define VANQ_MLVL_MNDX 0
1549 "by monster toughness, by internal monster index",
1550 #define VANQ_MSTR_MNDX 1
1551 "alphabetically, first unique monsters, then others",
1552 #define VANQ_ALPHA_SEP 2
1553 "alphabetically, unique monsters and others intermixed",
1554 #define VANQ_ALPHA_MIX 3
1555 "by monster class, high to low level within class",
1556 #define VANQ_MCLS_HTOL 4
1557 "by monster class, low to high level within class",
1558 #define VANQ_MCLS_LTOH 5
1559 "by count, high to low, by internal index within tied count",
1560 #define VANQ_COUNT_H_L 6
1561 "by count, low to high, by internal index within tied count",
1562 #define VANQ_COUNT_L_H 7
1564 static int vanq_sortmode = VANQ_MLVL_MNDX;
1566 STATIC_PTR int CFDECLSPEC
1567 vanqsort_cmp(vptr1, vptr2)
1568 const genericptr vptr1;
1569 const genericptr vptr2;
1571 int indx1 = *(short *) vptr1, indx2 = *(short *) vptr2,
1572 mlev1, mlev2, mstr1, mstr2, uniq1, uniq2, died1, died2, res;
1573 const char *name1, *name2, *punct;
1574 schar mcls1, mcls2;
1576 switch (vanq_sortmode) {
1577 default:
1578 case VANQ_MLVL_MNDX:
1579 /* sort by monster level */
1580 mlev1 = mons[indx1].mlevel, mlev2 = mons[indx2].mlevel;
1581 res = mlev2 - mlev1; /* mlevel high to low */
1582 break;
1583 case VANQ_MSTR_MNDX:
1584 /* sort by monster toughness */
1585 mstr1 = monstr[indx1], mstr2 = monstr[indx2];
1586 res = mstr2 - mstr1; /* monstr high to low */
1587 break;
1588 case VANQ_ALPHA_SEP:
1589 uniq1 = ((mons[indx1].geno & G_UNIQ) && indx1 != PM_HIGH_PRIEST);
1590 uniq2 = ((mons[indx2].geno & G_UNIQ) && indx2 != PM_HIGH_PRIEST);
1591 if (uniq1 ^ uniq2) { /* one or other uniq, but not both */
1592 res = uniq2 - uniq1;
1593 break;
1594 } /* else both unique or neither unique */
1595 /*FALLTHRU*/
1596 case VANQ_ALPHA_MIX:
1597 name1 = mons[indx1].mname, name2 = mons[indx2].mname;
1598 res = strcmpi(name1, name2); /* caseblind alhpa, low to high */
1599 break;
1600 case VANQ_MCLS_HTOL:
1601 case VANQ_MCLS_LTOH:
1602 /* mons[].mlet is a small integer, 1..N, of type plain char;
1603 if 'char' happens to be unsigned, (mlet1 - mlet2) would yield
1604 an inappropriate result when mlet2 is greater than mlet1,
1605 so force our copies (mcls1, mcls2) to be signed */
1606 mcls1 = (schar) mons[indx1].mlet, mcls2 = (schar) mons[indx2].mlet;
1607 /* S_ANT through S_ZRUTY correspond to lowercase monster classes,
1608 S_ANGEL through S_ZOMBIE correspond to uppercase, and various
1609 punctuation characters are used for classes beyond those */
1610 if (mcls1 > S_ZOMBIE && mcls2 > S_ZOMBIE) {
1611 /* force a specific order to the punctuation classes that's
1612 different from the internal order;
1613 internal order is ok if neither or just one is punctuation
1614 since letters have lower values so come out before punct */
1615 static const char punctclasses[] = {
1616 S_LIZARD, S_EEL, S_GOLEM, S_GHOST, S_DEMON, S_HUMAN, '\0'
1619 if ((punct = index(punctclasses, mcls1)) != 0)
1620 mcls1 = (schar) (S_ZOMBIE + 1 + (int) (punct - punctclasses));
1621 if ((punct = index(punctclasses, mcls2)) != 0)
1622 mcls2 = (schar) (S_ZOMBIE + 1 + (int) (punct - punctclasses));
1624 res = mcls1 - mcls2; /* class */
1625 if (res == 0) {
1626 mlev1 = mons[indx1].mlevel, mlev2 = mons[indx2].mlevel;
1627 res = mlev1 - mlev2; /* mlevel low to high */
1628 if (vanq_sortmode == VANQ_MCLS_HTOL)
1629 res = -res; /* mlevel high to low */
1631 break;
1632 case VANQ_COUNT_H_L:
1633 case VANQ_COUNT_L_H:
1634 died1 = mvitals[indx1].died, died2 = mvitals[indx2].died;
1635 res = died2 - died1; /* dead count high to low */
1636 if (vanq_sortmode == VANQ_COUNT_L_H)
1637 res = -res; /* dead count low to high */
1638 break;
1640 /* tiebreaker: internal mons[] index */
1641 if (res == 0)
1642 res = indx1 - indx2; /* mndx low to high */
1643 return res;
1646 /* returns -1 if cancelled via ESC */
1647 STATIC_OVL int
1648 set_vanq_order()
1650 winid tmpwin;
1651 menu_item *selected;
1652 anything any;
1653 int i, n, choice;
1655 tmpwin = create_nhwindow(NHW_MENU);
1656 start_menu(tmpwin);
1657 any = zeroany; /* zero out all bits */
1658 for (i = 0; i < SIZE(vanqorders); i++) {
1659 if (i == VANQ_ALPHA_MIX || i == VANQ_MCLS_HTOL) /* skip these */
1660 continue;
1661 any.a_int = i + 1;
1662 add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, vanqorders[i],
1663 (i == vanq_sortmode) ? MENU_SELECTED : MENU_UNSELECTED);
1665 end_menu(tmpwin, "Sort order for vanquished monster counts");
1667 n = select_menu(tmpwin, PICK_ONE, &selected);
1668 destroy_nhwindow(tmpwin);
1669 if (n > 0) {
1670 choice = selected[0].item.a_int - 1;
1671 /* skip preselected entry if we have more than one item chosen */
1672 if (n > 1 && choice == vanq_sortmode)
1673 choice = selected[1].item.a_int - 1;
1674 free((genericptr_t) selected);
1675 vanq_sortmode = choice;
1677 return (n < 0) ? -1 : vanq_sortmode;
1680 /* #vanquished command */
1682 dovanquished()
1684 list_vanquished('a', FALSE);
1685 return 0;
1688 /* high priests aren't unique but are flagged as such to simplify something */
1689 #define UniqCritterIndx(mndx) ((mons[mndx].geno & G_UNIQ) \
1690 && mndx != PM_HIGH_PRIEST)
1692 STATIC_OVL void
1693 list_vanquished(defquery, ask)
1694 char defquery;
1695 boolean ask;
1697 register int i;
1698 int pfx, nkilled;
1699 unsigned ntypes, ni;
1700 long total_killed = 0L;
1701 winid klwin;
1702 short mindx[NUMMONS];
1703 char c, buf[BUFSZ], buftoo[BUFSZ];
1705 /* get totals first */
1706 ntypes = 0;
1707 for (i = LOW_PM; i < NUMMONS; i++) {
1708 if ((nkilled = (int) mvitals[i].died) == 0)
1709 continue;
1710 mindx[ntypes++] = i;
1711 total_killed += (long) nkilled;
1714 /* vanquished creatures list;
1715 * includes all dead monsters, not just those killed by the player
1717 if (ntypes != 0) {
1718 char mlet, prev_mlet = 0; /* used as small integer, not character */
1719 boolean class_header, uniq_header, was_uniq = FALSE;
1721 c = ask ? yn_function(
1722 "Do you want an account of creatures vanquished?",
1723 ynaqchars, defquery)
1724 : defquery;
1725 if (c == 'q')
1726 done_stopprint++;
1727 if (c == 'y' || c == 'a') {
1728 if (c == 'a') { /* ask player to choose sort order */
1729 /* choose value for vanq_sortmode via menu; ESC cancels list
1730 of vanquished monsters but does not set 'done_stopprint' */
1731 if (set_vanq_order() < 0)
1732 return;
1734 uniq_header = (vanq_sortmode == VANQ_ALPHA_SEP);
1735 class_header = (vanq_sortmode == VANQ_MCLS_LTOH
1736 || vanq_sortmode == VANQ_MCLS_HTOL);
1738 klwin = create_nhwindow(NHW_MENU);
1739 putstr(klwin, 0, "Vanquished creatures:");
1740 putstr(klwin, 0, "");
1742 qsort((genericptr_t) mindx, ntypes, sizeof *mindx, vanqsort_cmp);
1743 for (ni = 0; ni < ntypes; ni++) {
1744 i = mindx[ni];
1745 nkilled = mvitals[i].died;
1746 mlet = mons[i].mlet;
1747 if (class_header && mlet != prev_mlet) {
1748 Strcpy(buf, def_monsyms[(int) mlet].explain);
1749 putstr(klwin, ask ? 0 : iflags.menu_headings,
1750 upstart(buf));
1751 prev_mlet = mlet;
1753 if (UniqCritterIndx(i)) {
1754 Sprintf(buf, "%s%s",
1755 !type_is_pname(&mons[i]) ? "the " : "",
1756 mons[i].mname);
1757 if (nkilled > 1) {
1758 switch (nkilled) {
1759 case 2:
1760 Sprintf(eos(buf), " (twice)");
1761 break;
1762 case 3:
1763 Sprintf(eos(buf), " (thrice)");
1764 break;
1765 default:
1766 Sprintf(eos(buf), " (%d times)", nkilled);
1767 break;
1770 was_uniq = TRUE;
1771 } else {
1772 if (uniq_header && was_uniq) {
1773 putstr(klwin, 0, "");
1774 was_uniq = FALSE;
1776 /* trolls or undead might have come back,
1777 but we don't keep track of that */
1778 if (nkilled == 1)
1779 Strcpy(buf, an(mons[i].mname));
1780 else
1781 Sprintf(buf, "%3d %s", nkilled,
1782 makeplural(mons[i].mname));
1784 /* number of leading spaces to match 3 digit prefix */
1785 pfx = !strncmpi(buf, "the ", 3) ? 0
1786 : !strncmpi(buf, "an ", 3) ? 1
1787 : !strncmpi(buf, "a ", 2) ? 2
1788 : !digit(buf[2]) ? 4 : 0;
1789 if (class_header)
1790 ++pfx;
1791 Sprintf(buftoo, "%*s%s", pfx, "", buf);
1792 putstr(klwin, 0, buftoo);
1795 * if (Hallucination)
1796 * putstr(klwin, 0, "and a partridge in a pear tree");
1798 if (ntypes > 1) {
1799 putstr(klwin, 0, "");
1800 Sprintf(buf, "%ld creatures vanquished.", total_killed);
1801 putstr(klwin, 0, buf);
1803 display_nhwindow(klwin, TRUE);
1804 destroy_nhwindow(klwin);
1806 } else if (defquery == 'a') {
1807 /* #dovanquished rather than final disclosure, so pline() is ok */
1808 pline("No monsters have been vanquished.");
1812 /* number of monster species which have been genocided */
1814 num_genocides()
1816 int i, n = 0;
1818 for (i = LOW_PM; i < NUMMONS; ++i) {
1819 if (mvitals[i].mvflags & G_GENOD) {
1820 ++n;
1821 if (UniqCritterIndx(i))
1822 impossible("unique creature '%d: %s' genocided?",
1823 i, mons[i].mname);
1826 return n;
1829 STATIC_OVL int
1830 num_extinct()
1832 int i, n = 0;
1834 for (i = LOW_PM; i < NUMMONS; ++i) {
1835 if (UniqCritterIndx(i))
1836 continue;
1837 if ((mvitals[i].mvflags & G_GONE) == G_EXTINCT)
1838 ++n;
1840 return n;
1843 STATIC_OVL void
1844 list_genocided(defquery, ask)
1845 char defquery;
1846 boolean ask;
1848 register int i;
1849 int ngenocided, nextinct;
1850 char c;
1851 winid klwin;
1852 char buf[BUFSZ];
1854 ngenocided = num_genocides();
1855 nextinct = num_extinct();
1857 /* genocided or extinct species list */
1858 if (ngenocided != 0 || nextinct != 0) {
1859 Sprintf(buf, "Do you want a list of %sspecies%s%s?",
1860 (nextinct && !ngenocided) ? "extinct " : "",
1861 (ngenocided) ? " genocided" : "",
1862 (nextinct && ngenocided) ? " and extinct" : "");
1863 c = ask ? yn_function(buf, ynqchars, defquery) : defquery;
1864 if (c == 'q')
1865 done_stopprint++;
1866 if (c == 'y') {
1867 klwin = create_nhwindow(NHW_MENU);
1868 Sprintf(buf, "%s%s species:",
1869 (ngenocided) ? "Genocided" : "Extinct",
1870 (nextinct && ngenocided) ? " or extinct" : "");
1871 putstr(klwin, 0, buf);
1872 putstr(klwin, 0, "");
1874 for (i = LOW_PM; i < NUMMONS; i++) {
1875 /* uniques can't be genocided but can become extinct;
1876 however, they're never reported as extinct, so skip them */
1877 if (UniqCritterIndx(i))
1878 continue;
1879 if (mvitals[i].mvflags & G_GONE) {
1880 Strcpy(buf, makeplural(mons[i].mname));
1882 * "Extinct" is unfortunate terminology. A species
1883 * is marked extinct when its birth limit is reached,
1884 * but there might be members of the species still
1885 * alive, contradicting the meaning of the word.
1887 if ((mvitals[i].mvflags & G_GONE) == G_EXTINCT)
1888 Strcat(buf, " (extinct)");
1889 putstr(klwin, 0, buf);
1892 putstr(klwin, 0, "");
1893 if (ngenocided > 0) {
1894 Sprintf(buf, "%d species genocided.", ngenocided);
1895 putstr(klwin, 0, buf);
1897 if (nextinct > 0) {
1898 Sprintf(buf, "%d species extinct.", nextinct);
1899 putstr(klwin, 0, buf);
1902 display_nhwindow(klwin, TRUE);
1903 destroy_nhwindow(klwin);
1908 /* set a delayed killer, ensure non-delayed killer is cleared out */
1909 void
1910 delayed_killer(id, format, killername)
1911 int id;
1912 int format;
1913 const char *killername;
1915 struct kinfo *k = find_delayed_killer(id);
1917 if (k == (struct kinfo *) 0) {
1918 /* no match, add a new delayed killer to the list */
1919 k = (struct kinfo *) alloc(sizeof(struct kinfo));
1920 (void) memset((genericptr_t)k, 0, sizeof(struct kinfo));
1921 k->id = id;
1922 k->next = killer.next;
1923 killer.next = k;
1926 k->format = format;
1927 Strcpy(k->name, killername ? killername : "");
1928 killer.name[0] = 0;
1931 struct kinfo *
1932 find_delayed_killer(id)
1933 int id;
1935 struct kinfo *k;
1937 for (k = killer.next; k != (struct kinfo *) 0; k = k->next) {
1938 if (k->id == id)
1939 break;
1941 return k;
1944 void
1945 dealloc_killer(kptr)
1946 struct kinfo *kptr;
1948 struct kinfo *prev = &killer, *k;
1950 if (kptr == (struct kinfo *) 0)
1951 return;
1952 for (k = killer.next; k != (struct kinfo *) 0; k = k->next) {
1953 if (k == kptr)
1954 break;
1955 prev = k;
1958 if (k == (struct kinfo *) 0) {
1959 impossible("dealloc_killer not on list");
1960 } else {
1961 prev->next = k->next;
1962 free((genericptr_t) k);
1966 void
1967 save_killers(fd, mode)
1968 int fd;
1969 int mode;
1971 struct kinfo *kptr;
1973 if (perform_bwrite(mode)) {
1974 for (kptr = &killer; kptr != (struct kinfo *) 0; kptr = kptr->next) {
1975 bwrite(fd, (genericptr_t) kptr, sizeof(struct kinfo));
1978 if (release_data(mode)) {
1979 while (killer.next) {
1980 kptr = killer.next->next;
1981 free((genericptr_t) killer.next);
1982 killer.next = kptr;
1987 void
1988 restore_killers(fd)
1989 int fd;
1991 struct kinfo *kptr;
1993 for (kptr = &killer; kptr != (struct kinfo *) 0; kptr = kptr->next) {
1994 mread(fd, (genericptr_t) kptr, sizeof(struct kinfo));
1995 if (kptr->next) {
1996 kptr->next = (struct kinfo *) alloc(sizeof(struct kinfo));
2001 static int
2002 wordcount(p)
2003 char *p;
2005 int words = 0;
2007 while (*p) {
2008 while (*p && isspace((uchar) *p))
2009 p++;
2010 if (*p)
2011 words++;
2012 while (*p && !isspace((uchar) *p))
2013 p++;
2015 return words;
2018 static void
2019 bel_copy1(inp, out)
2020 char **inp, *out;
2022 char *in = *inp;
2024 out += strlen(out); /* eos() */
2025 while (*in && isspace((uchar) *in))
2026 in++;
2027 while (*in && !isspace((uchar) *in))
2028 *out++ = *in++;
2029 *out = '\0';
2030 *inp = in;
2033 char *
2034 build_english_list(in)
2035 char *in;
2037 char *out, *p = in;
2038 int len = (int) strlen(p), words = wordcount(p);
2040 /* +3: " or " - " "; +(words - 1): (N-1)*(", " - " ") */
2041 if (words > 1)
2042 len += 3 + (words - 1);
2043 out = (char *) alloc(len + 1);
2044 *out = '\0'; /* bel_copy1() appends */
2046 switch (words) {
2047 case 0:
2048 impossible("no words in list");
2049 break;
2050 case 1:
2051 /* "single" */
2052 bel_copy1(&p, out);
2053 break;
2054 default:
2055 if (words == 2) {
2056 /* "first or second" */
2057 bel_copy1(&p, out);
2058 Strcat(out, " ");
2059 } else {
2060 /* "first, second, or third */
2061 do {
2062 bel_copy1(&p, out);
2063 Strcat(out, ", ");
2064 } while (--words > 1);
2066 Strcat(out, "or ");
2067 bel_copy1(&p, out);
2068 break;
2070 return out;
2073 /*end.c*/