1 /*	SCCS Id: @(#)end.c	3.4	2003/03/10	*/
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed.  See license for details. */
4 
5 #define NEED_VARARGS	/* comment line for pre-compiled headers */
6 
7 #include "hack.h"
8 #include "eshk.h"
9 #ifndef NO_SIGNAL
10 #include <signal.h>
11 #endif
12 #include "dlb.h"
13 
14 	/* these probably ought to be generated by makedefs, like LAST_GEM */
15 #define FIRST_GEM    DILITHIUM_CRYSTAL
16 #define FIRST_AMULET AMULET_OF_ESP
17 #define LAST_AMULET  AMULET_OF_YENDOR
18 
19 struct valuable_data { long count; int typ; };
20 
21 static struct valuable_data
22 	gems[LAST_GEM+1 - FIRST_GEM + 1], /* 1 extra for glass */
23 	amulets[LAST_AMULET+1 - FIRST_AMULET];
24 
25 static struct val_list { struct valuable_data *list; int size; } valuables[] = {
26 	{ gems,    sizeof gems / sizeof *gems },
27 	{ amulets, sizeof amulets / sizeof *amulets },
28 	{ 0, 0 }
29 };
30 
31 #ifndef NO_SIGNAL
32 STATIC_PTR void FDECL(done_intr, (int));
33 # if defined(UNIX) || defined(VMS) || defined (__EMX__)
34 static void FDECL(done_hangup, (int));
35 # endif
36 #endif
37 STATIC_DCL void FDECL(disclose,(int,BOOLEAN_P));
38 STATIC_DCL void FDECL(get_valuables, (struct obj *));
39 STATIC_DCL void FDECL(sort_valuables, (struct valuable_data *,int));
40 STATIC_DCL void FDECL(artifact_score, (struct obj *,BOOLEAN_P,winid));
41 STATIC_DCL void FDECL(savelife, (int));
42 STATIC_DCL void FDECL(list_vanquished, (CHAR_P,BOOLEAN_P));
43 STATIC_DCL void FDECL(list_genocided, (CHAR_P,BOOLEAN_P));
44 STATIC_DCL boolean FDECL(should_query_disclose_option, (int,char *));
45 
46 #if defined(__BEOS__) || defined(MICRO) || defined(WIN32) || defined(OS2)
47 extern void FDECL(nethack_exit,(int));
48 #else
49 #define nethack_exit exit
50 #endif
51 
52 #define done_stopprint program_state.stopprint
53 
54 #ifdef AMIGA
55 # define NH_abort()	Abort(0)
56 #else
57 # ifdef SYSV
58 # define NH_abort()	(void) abort()
59 # else
60 #  ifdef WIN32
61 # define NH_abort()	win32_abort()
62 #  else
63 # define NH_abort()	abort()
64 #  endif
65 # endif
66 #endif
67 
68 /*
69  * The order of these needs to match the macros in hack.h.
70  */
71 static NEARDATA const char *deaths[] = {		/* the array of death */
72 	"died", "choked", "poisoned", "starvation", "drowning",
73 	"burning", "dissolving under the heat and pressure",
74 	"crushed", "turned to stone", "turned into slime",
75 	"genocided", "panic", "trickery",
76 	"quit", "escaped", "ascended"
77 };
78 
79 static NEARDATA const char *ends[] = {		/* "when you..." */
80 	"died", "choked", "were poisoned", "starved", "drowned",
81 	"burned", "dissolved in the lava",
82 	"were crushed", "turned to stone", "turned into slime",
83 	"were genocided", "panicked", "were tricked",
84 	"quit", "escaped", "ascended"
85 };
86 
87 extern const char * const killed_by_prefix[];	/* from topten.c */
88 
89 /*ARGSUSED*/
90 void
done1(sig_unused)91 done1(sig_unused)   /* called as signal() handler, so sent at least one arg */
92 int sig_unused;
93 {
94 #ifndef NO_SIGNAL
95 	(void) signal(SIGINT,SIG_IGN);
96 #endif
97 	if(flags.ignintr) {
98 #ifndef NO_SIGNAL
99 		(void) signal(SIGINT, (SIG_RET_TYPE) done1);
100 #endif
101 		clear_nhwindow(WIN_MESSAGE);
102 		curs_on_u();
103 		wait_synch();
104 		if(multi > 0) nomul(0);
105 	} else {
106 		(void)done2();
107 	}
108 }
109 
110 
111 /* "#quit" command or keyboard interrupt */
112 int
done2()113 done2()
114 {
115 	if(yn("Really quit?") == 'n') {
116 #ifndef NO_SIGNAL
117 		(void) signal(SIGINT, (SIG_RET_TYPE) done1);
118 #endif
119 		clear_nhwindow(WIN_MESSAGE);
120 		curs_on_u();
121 		wait_synch();
122 		if(multi > 0) nomul(0);
123 		if(multi == 0) {
124 		    u.uinvulnerable = FALSE;	/* avoid ctrl-C bug -dlc */
125 		    u.usleep = 0;
126 		}
127 		return 0;
128 	}
129 #if defined(WIZARD) && (defined(UNIX) || defined(VMS) || defined(LATTICE))
130 	if(wizard) {
131 	    int c;
132 # ifdef VMS
133 	    const char *tmp = "Enter debugger?";
134 # else
135 #  ifdef LATTICE
136 	    const char *tmp = "Create SnapShot?";
137 #  else
138 	    const char *tmp = "Dump core?";
139 #  endif
140 # endif
141 	    if ((c = ynq(tmp)) == 'y') {
142 		(void) signal(SIGINT, (SIG_RET_TYPE) done1);
143 		exit_nhwindows((char *)0);
144 		NH_abort();
145 	    } else if (c == 'q') done_stopprint++;
146 	}
147 #endif
148 #ifndef LINT
149 	done(QUIT);
150 #endif
151 	return 0;
152 }
153 
154 #ifndef NO_SIGNAL
155 /*ARGSUSED*/
156 STATIC_PTR void
done_intr(sig_unused)157 done_intr(sig_unused) /* called as signal() handler, so sent at least one arg */
158 int sig_unused;
159 {
160 	done_stopprint++;
161 	(void) signal(SIGINT, SIG_IGN);
162 # if defined(UNIX) || defined(VMS)
163 	(void) signal(SIGQUIT, SIG_IGN);
164 # endif
165 	return;
166 }
167 
168 # if defined(UNIX) || defined(VMS) || defined(__EMX__)
169 static void
done_hangup(sig)170 done_hangup(sig)	/* signal() handler */
171 int sig;
172 {
173 	program_state.done_hup++;
174 	(void)signal(SIGHUP, SIG_IGN);
175 	done_intr(sig);
176 	return;
177 }
178 # endif
179 #endif /* NO_SIGNAL */
180 
181 void
done_in_by(mtmp)182 done_in_by(mtmp)
183 register struct monst *mtmp;
184 {
185 	char buf[BUFSZ];
186 	boolean distorted = (boolean)(Hallucination && canspotmon(mtmp));
187 
188 	You("die...");
189 	mark_synch();	/* flush buffered screen output */
190 	buf[0] = '\0';
191 	killer_format = KILLED_BY_AN;
192 	/* "killed by the high priest of Crom" is okay, "killed by the high
193 	   priest" alone isn't */
194 	if ((mtmp->data->geno & G_UNIQ) != 0 && !(mtmp->data == &mons[PM_HIGH_PRIEST] && !mtmp->ispriest)) {
195 	    if (!type_is_pname(mtmp->data))
196 		Strcat(buf, "the ");
197 	    killer_format = KILLED_BY;
198 	}
199 	/* _the_ <invisible> <distorted> ghost of Dudley */
200 	if (mtmp->data == &mons[PM_GHOST] && mtmp->mnamelth) {
201 		Strcat(buf, "the ");
202 		killer_format = KILLED_BY;
203 	}
204 	if (mtmp->minvis)
205 		Strcat(buf, "invisible ");
206 	if (distorted)
207 		Strcat(buf, "hallucinogen-distorted ");
208 
209 	if(mtmp->data == &mons[PM_GHOST]) {
210 		Strcat(buf, "ghost");
211 		if (mtmp->mnamelth) Sprintf(eos(buf), " of %s", NAME(mtmp));
212 	} else if(mtmp->isshk) {
213 		Sprintf(eos(buf), "%s %s, the shopkeeper",
214 			(mtmp->female ? "Ms." : "Mr."), shkname(mtmp));
215 		killer_format = KILLED_BY;
216 	} else if (mtmp->ispriest || mtmp->isminion) {
217 		/* m_monnam() suppresses "the" prefix plus "invisible", and
218 		   it overrides the effect of Hallucination on priestname() */
219 		killer = m_monnam(mtmp);
220 		Strcat(buf, killer);
221 	} else {
222 		Strcat(buf, mtmp->data->mname);
223 		if (mtmp->mnamelth)
224 		    Sprintf(eos(buf), " called %s", NAME(mtmp));
225 	}
226 
227 	if (multi) Strcat(buf, ", while helpless");
228 	killer = buf;
229 	if (mtmp->data->mlet == S_WRAITH)
230 		u.ugrave_arise = PM_WRAITH;
231 	else if (mtmp->data->mlet == S_MUMMY && urace.mummynum != NON_PM)
232 		u.ugrave_arise = urace.mummynum;
233 	else if (mtmp->data->mlet == S_VAMPIRE && Race_if(PM_HUMAN))
234 		u.ugrave_arise = PM_VAMPIRE;
235 	else if (mtmp->data == &mons[PM_GHOUL])
236 		u.ugrave_arise = PM_GHOUL;
237 	if (u.ugrave_arise >= LOW_PM &&
238 				(mvitals[u.ugrave_arise].mvflags & G_GENOD))
239 		u.ugrave_arise = NON_PM;
240 	if (touch_petrifies(mtmp->data))
241 		done(STONING);
242 	else
243 		done(DIED);
244 	return;
245 }
246 
247 #if defined(WIN32)
248 #define NOTIFY_NETHACK_BUGS
249 #endif
250 
251 /*VARARGS1*/
252 void
253 panic VA_DECL(const char *, str)
254 	VA_START(str);
255 	VA_INIT(str, char *);
256 
257 	if (program_state.panicking++)
258 	    NH_abort();	/* avoid loops - this should never happen*/
259 
260 	if (iflags.window_inited) {
261 	    raw_print("\r\nOops...");
262 	    wait_synch();	/* make sure all pending output gets flushed */
263 	    exit_nhwindows((char *)0);
264 	    iflags.window_inited = 0; /* they're gone; force raw_print()ing */
265 	}
266 
267 	raw_print(program_state.gameover ?
268 		  "Postgame wrapup disrupted." :
269 		  !program_state.something_worth_saving ?
270 		  "Program initialization has failed." :
271 		  "Suddenly, the dungeon collapses.");
272 #if defined(WIZARD) && !defined(MICRO)
273 # if defined(NOTIFY_NETHACK_BUGS)
274 	if (!wizard)
275 	    raw_printf("Report the following error to \"%s\".",
276 			"nethack-bugs@nethack.org");
277 	else if (program_state.something_worth_saving)
278 	    raw_print("\nError save file being written.\n");
279 # else
280 	if (!wizard)
281 	    raw_printf("Report error to \"%s\"%s.",
282 #  ifdef WIZARD_NAME	/*(KR1ED)*/
283 			WIZARD_NAME,
284 #  else
285 			WIZARD,
286 #  endif
287 			!program_state.something_worth_saving ? "" :
288 			" and it may be possible to rebuild.");
289 # endif
290 	if (program_state.something_worth_saving) {
291 	    set_error_savefile();
292 	    (void) dosave0();
293 	}
294 #endif
295 	{
296 	    char buf[BUFSZ];
297 	    Vsprintf(buf,str,VA_ARGS);
298 	    raw_print(buf);
299 	    paniclog("panic", buf);
300 	}
301 #ifdef WIN32
302 	interject(INTERJECT_PANIC);
303 #endif
304 #if defined(WIZARD) && (defined(UNIX) || defined(VMS) || defined(LATTICE) || defined(WIN32))
305 	if (wizard)
306 	    NH_abort();	/* generate core dump */
307 #endif
308 	VA_END();
309 	done(PANICKED);
310 }
311 
312 STATIC_OVL boolean
313 should_query_disclose_option(category, defquery)
314 int category;
315 char *defquery;
316 {
317     int idx;
318     char *dop = index(disclosure_options, category);
319 
320     if (dop && defquery) {
321 	idx = dop - disclosure_options;
322 	if (idx < 0 || idx > (NUM_DISCLOSURE_OPTIONS - 1)) {
323 	    impossible(
324 		   "should_query_disclose_option: bad disclosure index %d %c",
325 		       idx, category);
326 	    *defquery = DISCLOSE_PROMPT_DEFAULT_YES;
327 	    return TRUE;
328 	}
329 	if (flags.end_disclose[idx] == DISCLOSE_YES_WITHOUT_PROMPT) {
330 	    *defquery = 'y';
331 	    return FALSE;
332 	} else if (flags.end_disclose[idx] == DISCLOSE_NO_WITHOUT_PROMPT) {
333 	    *defquery = 'n';
334 	    return FALSE;
335 	} else if (flags.end_disclose[idx] == DISCLOSE_PROMPT_DEFAULT_YES) {
336 	    *defquery = 'y';
337 	    return TRUE;
338 	} else if (flags.end_disclose[idx] == DISCLOSE_PROMPT_DEFAULT_NO) {
339 	    *defquery = 'n';
340 	    return TRUE;
341 	}
342     }
343     if (defquery)
344 	impossible("should_query_disclose_option: bad category %c", category);
345     else
346 	impossible("should_query_disclose_option: null defquery");
347     return TRUE;
348 }
349 
350 STATIC_OVL void
351 disclose(how,taken)
352 int how;
353 boolean taken;
354 {
355 	char	c = 0, defquery;
356 	char	qbuf[QBUFSZ];
357 	boolean ask;
358 
359 	if (invent) {
360 	    if(taken)
361 		Sprintf(qbuf,"Do you want to see what you had when you %s?",
362 			(how == QUIT) ? "quit" : "died");
363 	    else
364 		Strcpy(qbuf,"Do you want your possessions identified?");
365 
366 	    ask = should_query_disclose_option('i', &defquery);
367 	    if (!done_stopprint) {
368 		c = ask ? yn_function(qbuf, ynqchars, defquery) : defquery;
369 		if (c == 'y') {
370 			struct obj *obj;
371 
372 			for (obj = invent; obj; obj = obj->nobj) {
373 			    makeknown(obj->otyp);
374 			    obj->known = obj->bknown = obj->dknown = obj->rknown = 1;
375 			}
376 			(void) display_inventory((char *)0, TRUE);
377 			container_contents(invent, TRUE, TRUE);
378 		}
379 		if (c == 'q')  done_stopprint++;
380 	    }
381 	}
382 
383 	ask = should_query_disclose_option('a', &defquery);
384 	if (!done_stopprint) {
385 	    c = ask ? yn_function("Do you want to see your attributes?",
386 				  ynqchars, defquery) : defquery;
387 	    if (c == 'y')
388 		enlightenment(how >= PANICKED ? 1 : 2); /* final */
389 	    if (c == 'q') done_stopprint++;
390 	}
391 
392 	ask = should_query_disclose_option('v', &defquery);
393 	if (!done_stopprint)
394 	    list_vanquished(defquery, ask);
395 
396 	ask = should_query_disclose_option('g', &defquery);
397 	if (!done_stopprint)
398 	    list_genocided(defquery, ask);
399 
400 	ask = should_query_disclose_option('c', &defquery);
401 	if (!done_stopprint) {
402 	    c = ask ? yn_function("Do you want to see your conduct?",
403 				  ynqchars, defquery) : defquery;
404 	    if (c == 'y')
405 		show_conduct(how >= PANICKED ? 1 : 2);
406 	    if (c == 'q') done_stopprint++;
407 	}
408 }
409 
410 /* try to get the player back in a viable state after being killed */
411 STATIC_OVL void
412 savelife(how)
413 int how;
414 {
415 	u.uswldtim = 0;
416 	u.uhp = u.uhpmax;
417 	if (u.uhunger < 500) {
418 	    u.uhunger = 500;
419 	    newuhs(FALSE);
420 	}
421 	/* cure impending doom of sickness hero won't have time to fix */
422 	if ((Sick & TIMEOUT) == 1) {
423 	    u.usick_type = 0;
424 	    Sick = 0;
425 	}
426 	if (how == CHOKING) init_uhunger();
427 	nomovemsg = "You survived that attempt on your life.";
428 	flags.move = 0;
429 	if(multi > 0) multi = 0; else multi = -1;
430 	if(u.utrap && u.utraptype == TT_LAVA) u.utrap = 0;
431 	flags.botl = 1;
432 	u.ugrave_arise = NON_PM;
433 	HUnchanging = 0L;
434 	curs_on_u();
435 }
436 
437 /*
438  * Get valuables from the given list.  Revised code: the list always remains
439  * intact.
440  */
441 STATIC_OVL void
442 get_valuables(list)
443 struct obj *list;	/* inventory or container contents */
444 {
445     register struct obj *obj;
446     register int i;
447 
448     /* find amulets and gems, ignoring all artifacts */
449     for (obj = list; obj; obj = obj->nobj)
450 	if (Has_contents(obj)) {
451 	    get_valuables(obj->cobj);
452 	} else if (obj->oartifact) {
453 	    continue;
454 	} else if (obj->oclass == AMULET_CLASS) {
455 	    i = obj->otyp - FIRST_AMULET;
456 	    if (!amulets[i].count) {
457 		amulets[i].count = obj->quan;
458 		amulets[i].typ = obj->otyp;
459 	    } else amulets[i].count += obj->quan; /* always adds one */
460 	} else if (obj->oclass == GEM_CLASS && obj->otyp < LUCKSTONE) {
461 	    i = min(obj->otyp, LAST_GEM + 1) - FIRST_GEM;
462 	    if (!gems[i].count) {
463 		gems[i].count = obj->quan;
464 		gems[i].typ = obj->otyp;
465 	    } else gems[i].count += obj->quan;
466 	}
467     return;
468 }
469 
470 /*
471  *  Sort collected valuables, most frequent to least.  We could just
472  *  as easily use qsort, but we don't care about efficiency here.
473  */
474 STATIC_OVL void
475 sort_valuables(list, size)
476 struct valuable_data list[];
477 int size;		/* max value is less than 20 */
478 {
479     register int i, j;
480     struct valuable_data ltmp;
481 
482     /* move greater quantities to the front of the list */
483     for (i = 1; i < size; i++) {
484 	if (list[i].count == 0) continue;	/* empty slot */
485 	ltmp = list[i]; /* structure copy */
486 	for (j = i; j > 0; --j)
487 	    if (list[j-1].count >= ltmp.count) break;
488 	    else {
489 		list[j] = list[j-1];
490 	    }
491 	list[j] = ltmp;
492     }
493     return;
494 }
495 
496 /* called twice; first to calculate total, then to list relevant items */
497 STATIC_OVL void
498 artifact_score(list, counting, endwin)
499 struct obj *list;
500 boolean counting;	/* true => add up points; false => display them */
501 winid endwin;
502 {
503     char pbuf[BUFSZ];
504     struct obj *otmp;
505     long value, points;
506     short dummy;	/* object type returned by artifact_name() */
507 
508     for (otmp = list; otmp; otmp = otmp->nobj) {
509 	if (otmp->oartifact ||
510 			otmp->otyp == BELL_OF_OPENING ||
511 			otmp->otyp == SPE_BOOK_OF_THE_DEAD ||
512 			otmp->otyp == CANDELABRUM_OF_INVOCATION) {
513 	    value = arti_cost(otmp);	/* zorkmid value */
514 	    points = value * 5 / 2;	/* score value */
515 	    if (counting) {
516 		u.urexp += points;
517 	    } else {
518 		makeknown(otmp->otyp);
519 		otmp->known = otmp->dknown = otmp->bknown = otmp->rknown = 1;
520 		/* assumes artifacts don't have quan > 1 */
521 		Sprintf(pbuf, "%s%s (worth %ld %s and %ld points)",
522 			the_unique_obj(otmp) ? "The " : "",
523 			otmp->oartifact ? artifact_name(xname(otmp), &dummy) :
524 				OBJ_NAME(objects[otmp->otyp]),
525 			value, currency(value), points);
526 		putstr(endwin, 0, pbuf);
527 	    }
528 	}
529 	if (Has_contents(otmp))
530 	    artifact_score(otmp->cobj, counting, endwin);
531     }
532 }
533 
534 /* Be careful not to call panic from here! */
535 void
536 done(how)
537 int how;
538 {
539 	boolean taken;
540 	char kilbuf[BUFSZ], pbuf[BUFSZ];
541 	winid endwin = WIN_ERR;
542 	boolean bones_ok, have_windows = iflags.window_inited;
543 	struct obj *corpse = (struct obj *)0;
544 	long umoney;
545 
546 	if (how == TRICKED) {
547 	    if (killer) {
548 		paniclog("trickery", killer);
549 		killer = 0;
550 	    }
551 #ifdef WIZARD
552 	    if (wizard) {
553 		You("are a very tricky wizard, it seems.");
554 		return;
555 	    }
556 #endif
557 	}
558 
559 	/* kilbuf: used to copy killer in case it comes from something like
560 	 *	xname(), which would otherwise get overwritten when we call
561 	 *	xname() when listing possessions
562 	 * pbuf: holds Sprintf'd output for raw_print and putstr
563 	 */
564 	if (how == ASCENDED || (!killer && how == GENOCIDED))
565 		killer_format = NO_KILLER_PREFIX;
566 	/* Avoid killed by "a" burning or "a" starvation */
567 	if (!killer && (how == STARVING || how == BURNING))
568 		killer_format = KILLED_BY;
569 	Strcpy(kilbuf, (!killer || how >= PANICKED ? deaths[how] : killer));
570 	killer = kilbuf;
571 
572 	if (how < PANICKED) u.umortality++;
573 	if (Lifesaved && (how <= GENOCIDED)) {
574 		pline("But wait...");
575 		makeknown(AMULET_OF_LIFE_SAVING);
576 		Your("medallion %s!",
577 		      !Blind ? "begins to glow" : "feels warm");
578 		if (how == CHOKING) You("vomit ...");
579 		You_feel("much better!");
580 		pline_The("medallion crumbles to dust!");
581 		if (uamul) useup(uamul);
582 
583 		(void) adjattrib(A_CON, -1, TRUE);
584 		if(u.uhpmax <= 0) u.uhpmax = 10;	/* arbitrary */
585 		savelife(how);
586 		if (how == GENOCIDED)
587 			pline("Unfortunately you are still genocided...");
588 		else {
589 			killer = 0;
590 			killer_format = 0;
591 			return;
592 		}
593 	}
594 	if ((
595 #ifdef WIZARD
596 			wizard ||
597 #endif
598 			discover) && (how <= GENOCIDED)) {
599 		if(yn("Die?") == 'y') goto die;
600 		pline("OK, so you don't %s.",
601 			(how == CHOKING) ? "choke" : "die");
602 		if(u.uhpmax <= 0) u.uhpmax = u.ulevel * 8;	/* arbitrary */
603 		savelife(how);
604 		killer = 0;
605 		killer_format = 0;
606 		return;
607 	}
608 
609     /*
610      *	The game is now over...
611      */
612 
613 die:
614 	program_state.gameover = 1;
615 	/* in case of a subsequent panic(), there's no point trying to save */
616 	program_state.something_worth_saving = 0;
617 	/* render vision subsystem inoperative */
618 	iflags.vision_inited = 0;
619 	/* might have been killed while using a disposable item, so make sure
620 	   it's gone prior to inventory disclosure and creation of bones data */
621 	inven_inuse(TRUE);
622 
623 	/* Sometimes you die on the first move.  Life's not fair.
624 	 * On those rare occasions you get hosed immediately, go out
625 	 * smiling... :-)  -3.
626 	 */
627 	if (moves <= 1 && how < PANICKED)	/* You die... --More-- */
628 	    pline("Do not pass go.  Do not collect 200 %s.", currency(200L));
629 
630 	if (have_windows) wait_synch();	/* flush screen output */
631 #ifndef NO_SIGNAL
632 	(void) signal(SIGINT, (SIG_RET_TYPE) done_intr);
633 # if defined(UNIX) || defined(VMS) || defined (__EMX__)
634 	(void) signal(SIGQUIT, (SIG_RET_TYPE) done_intr);
635 	(void) signal(SIGHUP, (SIG_RET_TYPE) done_hangup);
636 # endif
637 #endif /* NO_SIGNAL */
638 
639 	bones_ok = (how < GENOCIDED) && can_make_bones();
640 
641 	if (how == TURNED_SLIME)
642 	    u.ugrave_arise = PM_GREEN_SLIME;
643 
644 	if (bones_ok && u.ugrave_arise < LOW_PM) {
645 	    /* corpse gets burnt up too */
646 	    if (how == BURNING)
647 		u.ugrave_arise = (NON_PM - 2);	/* leave no corpse */
648 	    else if (how == STONING)
649 		u.ugrave_arise = (NON_PM - 1);	/* statue instead of corpse */
650 	    else if (u.ugrave_arise == NON_PM &&
651 		     !(mvitals[u.umonnum].mvflags & G_NOCORPSE)) {
652 		int mnum = u.umonnum;
653 
654 		if (!Upolyd) {
655 		    /* Base corpse on race when not poly'd since original
656 		     * u.umonnum is based on role, and all role monsters
657 		     * are human.
658 		     */
659 		    mnum = (flags.female && urace.femalenum != NON_PM) ?
660 			urace.femalenum : urace.malenum;
661 		}
662 		corpse = mk_named_object(CORPSE, &mons[mnum],
663 				       u.ux, u.uy, plname);
664 		Sprintf(pbuf, "%s, %s%s", plname,
665 			killer_format == NO_KILLER_PREFIX ? "" :
666 			killed_by_prefix[how],
667 			killer_format == KILLED_BY_AN ? an(killer) : killer);
668 		make_grave(u.ux, u.uy, pbuf);
669 	    }
670 	}
671 
672 	if (how == QUIT) {
673 		killer_format = NO_KILLER_PREFIX;
674 		if (u.uhp < 1) {
675 			how = DIED;
676 			u.umortality++;	/* skipped above when how==QUIT */
677 			/* note that killer is pointing at kilbuf */
678 			Strcpy(kilbuf, "quit while already on Charon's boat");
679 		}
680 	}
681 	if (how == ESCAPED || how == PANICKED)
682 		killer_format = NO_KILLER_PREFIX;
683 
684 	if (how != PANICKED) {
685 	    /* these affect score and/or bones, but avoid them during panic */
686 	    taken = paybill((how == ESCAPED) ? -1 : (how != QUIT));
687 	    paygd();
688 	    clearpriests();
689 	} else	taken = FALSE;	/* lint; assert( !bones_ok ); */
690 
691 	clearlocks();
692 
693 	if (have_windows) display_nhwindow(WIN_MESSAGE, FALSE);
694 
695 	if (strcmp(flags.end_disclose, "none") && how != PANICKED)
696 		disclose(how, taken);
697 	/* finish_paybill should be called after disclosure but before bones */
698 	if (bones_ok && taken) finish_paybill();
699 
700 	/* calculate score, before creating bones [container gold] */
701 	{
702 	    long tmp;
703 	    int deepest = deepest_lev_reached(FALSE);
704 
705 #ifndef GOLDOBJ
706 	    umoney = u.ugold;
707 	    tmp = u.ugold0;
708 #else
709 	    umoney = money_cnt(invent);
710 	    tmp = u.umoney0;
711 #endif
712 	    umoney += hidden_gold();	/* accumulate gold from containers */
713 	    tmp = umoney - tmp;		/* net gain */
714 
715 	    if (tmp < 0L)
716 		tmp = 0L;
717 	    if (how < PANICKED)
718 		tmp -= tmp / 10L;
719 	    u.urexp += tmp;
720 	    u.urexp += 50L * (long)(deepest - 1);
721 	    if (deepest > 20)
722 		u.urexp += 1000L * (long)((deepest > 30) ? 10 : deepest - 20);
723 	    if (how == ASCENDED) u.urexp *= 2L;
724 	}
725 
726 	if (bones_ok) {
727 #ifdef WIZARD
728 	    if (!wizard || yn("Save bones?") == 'y')
729 #endif
730 		savebones(corpse);
731 	    /* corpse may be invalid pointer now so
732 		ensure that it isn't used again */
733 	    corpse = (struct obj *)0;
734 	}
735 
736 	/* update gold for the rip output, which can't use hidden_gold()
737 	   (containers will be gone by then if bones just got saved...) */
738 #ifndef GOLDOBJ
739 	u.ugold = umoney;
740 #else
741 	done_money = umoney;
742 #endif
743 
744 	/* clean up unneeded windows */
745 	if (have_windows) {
746 	    wait_synch();
747 	    display_nhwindow(WIN_MESSAGE, TRUE);
748 	    destroy_nhwindow(WIN_MAP);
749 	    destroy_nhwindow(WIN_STATUS);
750 	    destroy_nhwindow(WIN_MESSAGE);
751 	    WIN_MESSAGE = WIN_STATUS = WIN_MAP = WIN_ERR;
752 
753 	    if(!done_stopprint || flags.tombstone)
754 		endwin = create_nhwindow(NHW_TEXT);
755 
756 	    if (how < GENOCIDED && flags.tombstone && endwin != WIN_ERR)
757 		outrip(endwin, how);
758 	} else
759 	    done_stopprint = 1; /* just avoid any more output */
760 
761 /* changing kilbuf really changes killer. we do it this way because
762    killer is declared a (const char *)
763 */
764 	if (u.uhave.amulet) Strcat(kilbuf, " (with the Amulet)");
765 	else if (how == ESCAPED) {
766 	    if (Is_astralevel(&u.uz))	/* offered Amulet to wrong deity */
767 		Strcat(kilbuf, " (in celestial disgrace)");
768 	    else if (carrying(FAKE_AMULET_OF_YENDOR))
769 		Strcat(kilbuf, " (with a fake Amulet)");
770 		/* don't bother counting to see whether it should be plural */
771 	}
772 
773 	if (!done_stopprint) {
774 	    Sprintf(pbuf, "%s %s the %s...", Goodbye(), plname,
775 		   how != ASCENDED ?
776 		      (const char *) ((flags.female && urole.name.f) ?
777 		         urole.name.f : urole.name.m) :
778 		      (const char *) (flags.female ? "Demigoddess" : "Demigod"));
779 	    putstr(endwin, 0, pbuf);
780 	    putstr(endwin, 0, "");
781 	}
782 
783 	if (how == ESCAPED || how == ASCENDED) {
784 	    register struct monst *mtmp;
785 	    register struct obj *otmp;
786 	    register struct val_list *val;
787 	    register int i;
788 
789 	    for (val = valuables; val->list; val++)
790 		for (i = 0; i < val->size; i++) {
791 		    val->list[i].count = 0L;
792 		}
793 	    get_valuables(invent);
794 
795 	    /* add points for collected valuables */
796 	    for (val = valuables; val->list; val++)
797 		for (i = 0; i < val->size; i++)
798 		    if (val->list[i].count != 0L)
799 			u.urexp += val->list[i].count
800 				  * (long)objects[val->list[i].typ].oc_cost;
801 
802 	    /* count the points for artifacts */
803 	    artifact_score(invent, TRUE, endwin);
804 
805 	    keepdogs(TRUE);
806 	    viz_array[0][0] |= IN_SIGHT; /* need visibility for naming */
807 	    mtmp = mydogs;
808 	    if (!done_stopprint) Strcpy(pbuf, "You");
809 	    if (mtmp) {
810 		while (mtmp) {
811 		    if (!done_stopprint)
812 			Sprintf(eos(pbuf), " and %s", mon_nam(mtmp));
813 		    if (mtmp->mtame)
814 			u.urexp += mtmp->mhp;
815 		    mtmp = mtmp->nmon;
816 		}
817 		if (!done_stopprint) putstr(endwin, 0, pbuf);
818 		pbuf[0] = '\0';
819 	    } else {
820 		if (!done_stopprint) Strcat(pbuf, " ");
821 	    }
822 	    if (!done_stopprint) {
823 		Sprintf(eos(pbuf), "%s with %ld point%s,",
824 			how==ASCENDED ? "went to your reward" :
825 					"escaped from the dungeon",
826 			u.urexp, plur(u.urexp));
827 		putstr(endwin, 0, pbuf);
828 	    }
829 
830 	    if (!done_stopprint)
831 		artifact_score(invent, FALSE, endwin);	/* list artifacts */
832 
833 	    /* list valuables here */
834 	    for (val = valuables; val->list; val++) {
835 		sort_valuables(val->list, val->size);
836 		for (i = 0; i < val->size && !done_stopprint; i++) {
837 		    int typ = val->list[i].typ;
838 		    long count = val->list[i].count;
839 
840 		    if (count == 0L) continue;
841 		    if (objects[typ].oc_class != GEM_CLASS || typ <= LAST_GEM) {
842 			otmp = mksobj(typ, FALSE, FALSE);
843 			makeknown(otmp->otyp);
844 			otmp->known = 1;	/* for fake amulets */
845 			otmp->dknown = 1;	/* seen it (blindness fix) */
846 			otmp->onamelth = 0;
847 			otmp->quan = count;
848 			Sprintf(pbuf, "%8ld %s (worth %ld %s),",
849 				count, xname(otmp),
850 				count * (long)objects[typ].oc_cost, currency(2L));
851 			obfree(otmp, (struct obj *)0);
852 		    } else {
853 			Sprintf(pbuf,
854 				"%8ld worthless piece%s of colored glass,",
855 				count, plur(count));
856 		    }
857 		    putstr(endwin, 0, pbuf);
858 		}
859 	    }
860 
861 	} else if (!done_stopprint) {
862 	    /* did not escape or ascend */
863 	    if (u.uz.dnum == 0 && u.uz.dlevel <= 0) {
864 		/* level teleported out of the dungeon; `how' is DIED,
865 		   due to falling or to "arriving at heaven prematurely" */
866 		Sprintf(pbuf, "You %s beyond the confines of the dungeon",
867 			(u.uz.dlevel < 0) ? "passed away" : ends[how]);
868 	    } else {
869 		/* more conventional demise */
870 		const char *where = dungeons[u.uz.dnum].dname;
871 
872 		if (Is_astralevel(&u.uz)) where = "The Astral Plane";
873 		Sprintf(pbuf, "You %s in %s", ends[how], where);
874 		if (!In_endgame(&u.uz) && !Is_knox(&u.uz))
875 		    Sprintf(eos(pbuf), " on dungeon level %d",
876 			    In_quest(&u.uz) ? dunlev(&u.uz) : depth(&u.uz));
877 	    }
878 
879 	    Sprintf(eos(pbuf), " with %ld point%s,",
880 		    u.urexp, plur(u.urexp));
881 	    putstr(endwin, 0, pbuf);
882 	}
883 
884 	if (!done_stopprint) {
885 	    Sprintf(pbuf, "and %ld piece%s of gold, after %ld move%s.",
886 		    umoney, plur(umoney), moves, plur(moves));
887 	    putstr(endwin, 0, pbuf);
888 	}
889 	if (!done_stopprint) {
890 	    Sprintf(pbuf,
891 	     "You were level %d with a maximum of %d hit point%s when you %s.",
892 		    u.ulevel, u.uhpmax, plur(u.uhpmax), ends[how]);
893 	    putstr(endwin, 0, pbuf);
894 	    putstr(endwin, 0, "");
895 	}
896 	if (!done_stopprint)
897 	    display_nhwindow(endwin, TRUE);
898 	if (endwin != WIN_ERR)
899 	    destroy_nhwindow(endwin);
900 
901 	/* "So when I die, the first thing I will see in Heaven is a
902 	 * score list?" */
903 	if (flags.toptenwin) {
904 	    topten(how);
905 	    if (have_windows)
906 		exit_nhwindows((char *)0);
907 	} else {
908 	    if (have_windows)
909 		exit_nhwindows((char *)0);
910 	    topten(how);
911 	}
912 
913 	if(done_stopprint) { raw_print(""); raw_print(""); }
914 	terminate(EXIT_SUCCESS);
915 }
916 
917 
918 void
919 container_contents(list, identified, all_containers)
920 struct obj *list;
921 boolean identified, all_containers;
922 {
923 	register struct obj *box, *obj;
924 	char buf[BUFSZ];
925 
926 	for (box = list; box; box = box->nobj) {
927 	    if (Is_container(box) || box->otyp == STATUE) {
928 		if (box->otyp == BAG_OF_TRICKS) {
929 		    continue;	/* wrong type of container */
930 		} else if (box->cobj) {
931 		    winid tmpwin = create_nhwindow(NHW_MENU);
932 		    Sprintf(buf, "Contents of %s:", the(xname(box)));
933 		    putstr(tmpwin, 0, buf);
934 		    putstr(tmpwin, 0, "");
935 		    for (obj = box->cobj; obj; obj = obj->nobj) {
936 			if (identified) {
937 			    makeknown(obj->otyp);
938 			    obj->known = obj->bknown =
939 			    obj->dknown = obj->rknown = 1;
940 			}
941 			putstr(tmpwin, 0, doname(obj));
942 		    }
943 		    display_nhwindow(tmpwin, TRUE);
944 		    destroy_nhwindow(tmpwin);
945 		    if (all_containers)
946 			container_contents(box->cobj, identified, TRUE);
947 		} else {
948 		    pline("%s empty.", Tobjnam(box, "are"));
949 		    display_nhwindow(WIN_MESSAGE, FALSE);
950 		}
951 	    }
952 	    if (!all_containers)
953 		break;
954 	}
955 }
956 
957 
958 /* should be called with either EXIT_SUCCESS or EXIT_FAILURE */
959 void
960 terminate(status)
961 int status;
962 {
963 #ifdef MAC
964 	getreturn("to exit");
965 #endif
966 	/* don't bother to try to release memory if we're in panic mode, to
967 	   avoid trouble in case that happens to be due to memory problems */
968 	if (!program_state.panicking) {
969 	    freedynamicdata();
970 	    dlb_cleanup();
971 	}
972 
973 	nethack_exit(status);
974 }
975 
976 STATIC_OVL void
977 list_vanquished(defquery, ask)
978 char defquery;
979 boolean ask;
980 {
981     register int i, lev;
982     int ntypes = 0, max_lev = 0, nkilled;
983     long total_killed = 0L;
984     char c;
985     winid klwin;
986     char buf[BUFSZ];
987 
988     /* get totals first */
989     for (i = LOW_PM; i < NUMMONS; i++) {
990 	if (mvitals[i].died) ntypes++;
991 	total_killed += (long)mvitals[i].died;
992 	if (mons[i].mlevel > max_lev) max_lev = mons[i].mlevel;
993     }
994 
995     /* vanquished creatures list;
996      * includes all dead monsters, not just those killed by the player
997      */
998     if (ntypes != 0) {
999 	c = ask ? yn_function("Do you want an account of creatures vanquished?",
1000 			      ynqchars, defquery) : defquery;
1001 	if (c == 'q') done_stopprint++;
1002 	if (c == 'y') {
1003 	    klwin = create_nhwindow(NHW_MENU);
1004 	    putstr(klwin, 0, "Vanquished creatures:");
1005 	    putstr(klwin, 0, "");
1006 
1007 	    /* countdown by monster "toughness" */
1008 	    for (lev = max_lev; lev >= 0; lev--)
1009 	      for (i = LOW_PM; i < NUMMONS; i++)
1010 		if (mons[i].mlevel == lev && (nkilled = mvitals[i].died) > 0) {
1011 		    if ((mons[i].geno & G_UNIQ) && i != PM_HIGH_PRIEST) {
1012 			Sprintf(buf, "%s%s",
1013 				!type_is_pname(&mons[i]) ? "The " : "",
1014 				mons[i].mname);
1015 			if (nkilled > 1) {
1016 			    switch (nkilled) {
1017 				case 2:  Sprintf(eos(buf)," (twice)");  break;
1018 				case 3:  Sprintf(eos(buf)," (thrice)");  break;
1019 				default: Sprintf(eos(buf)," (%d time%s)",
1020 						 nkilled, plur(nkilled));
1021 					 break;
1022 			    }
1023 			}
1024 		    } else {
1025 			/* trolls or undead might have come back,
1026 			   but we don't keep track of that */
1027 			if (nkilled == 1)
1028 			    Strcpy(buf, an(mons[i].mname));
1029 			else
1030 			    Sprintf(buf, "%d %s",
1031 				    nkilled, makeplural(mons[i].mname));
1032 		    }
1033 		    putstr(klwin, 0, buf);
1034 		}
1035 	    /*
1036 	     * if (Hallucination)
1037 	     *     putstr(klwin, 0, "and a partridge in a pear tree");
1038 	     */
1039 	    if (ntypes > 1) {
1040 		putstr(klwin, 0, "");
1041 		Sprintf(buf, "%ld creatures vanquished.", total_killed);
1042 		putstr(klwin, 0, buf);
1043 	    }
1044 	    display_nhwindow(klwin, TRUE);
1045 	    destroy_nhwindow(klwin);
1046 	}
1047     }
1048 }
1049 
1050 /* number of monster species which have been genocided */
1051 int
1052 num_genocides()
1053 {
1054     int i, n = 0;
1055 
1056     for (i = LOW_PM; i < NUMMONS; ++i)
1057 	if (mvitals[i].mvflags & G_GENOD) ++n;
1058 
1059     return n;
1060 }
1061 
1062 STATIC_OVL void
1063 list_genocided(defquery, ask)
1064 char defquery;
1065 boolean ask;
1066 {
1067     register int i;
1068     int ngenocided;
1069     char c;
1070     winid klwin;
1071     char buf[BUFSZ];
1072 
1073     ngenocided = num_genocides();
1074 
1075     /* genocided species list */
1076     if (ngenocided != 0) {
1077 	c = ask ? yn_function("Do you want a list of species genocided?",
1078 			      ynqchars, defquery) : defquery;
1079 	if (c == 'q') done_stopprint++;
1080 	if (c == 'y') {
1081 	    klwin = create_nhwindow(NHW_MENU);
1082 	    putstr(klwin, 0, "Genocided species:");
1083 	    putstr(klwin, 0, "");
1084 
1085 	    for (i = LOW_PM; i < NUMMONS; i++)
1086 		if (mvitals[i].mvflags & G_GENOD) {
1087 		    if ((mons[i].geno & G_UNIQ) && i != PM_HIGH_PRIEST)
1088 			Sprintf(buf, "%s%s",
1089 				!type_is_pname(&mons[i]) ? "" : "the ",
1090 				mons[i].mname);
1091 		    else
1092 			Strcpy(buf, makeplural(mons[i].mname));
1093 		    putstr(klwin, 0, buf);
1094 		}
1095 
1096 	    putstr(klwin, 0, "");
1097 	    Sprintf(buf, "%d species genocided.", ngenocided);
1098 	    putstr(klwin, 0, buf);
1099 
1100 	    display_nhwindow(klwin, TRUE);
1101 	    destroy_nhwindow(klwin);
1102 	}
1103     }
1104 }
1105 
1106 /*end.c*/
1107