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