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
19struct valuable_data { long count; int typ; };
20
21static struct valuable_data
22	gems[LAST_GEM+1 - FIRST_GEM + 1], /* 1 extra for glass */
23	amulets[LAST_AMULET+1 - FIRST_AMULET];
24
25static 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
32STATIC_PTR void FDECL(done_intr, (int));
33# if defined(UNIX) || defined(VMS) || defined (__EMX__)
34static void FDECL(done_hangup, (int));
35# endif
36#endif
37STATIC_DCL void FDECL(disclose,(int,BOOLEAN_P));
38STATIC_DCL void FDECL(get_valuables, (struct obj *));
39STATIC_DCL void FDECL(sort_valuables, (struct valuable_data *,int));
40STATIC_DCL void FDECL(artifact_score, (struct obj *,BOOLEAN_P,winid));
41STATIC_DCL void FDECL(savelife, (int));
42STATIC_DCL void FDECL(list_vanquished, (CHAR_P,BOOLEAN_P));
43STATIC_DCL void FDECL(list_genocided, (CHAR_P,BOOLEAN_P));
44STATIC_DCL boolean FDECL(should_query_disclose_option, (int,char *));
45
46#if defined(__BEOS__) || defined(MICRO) || defined(WIN32) || defined(OS2)
47extern 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 */
71static 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
79static 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
87extern const char * const killed_by_prefix[];	/* from topten.c */
88
89/*ARGSUSED*/
90void
91done1(sig_unused)   /* called as signal() handler, so sent at least one arg */
92int 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 */
112int
113done2()
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*/
156STATIC_PTR void
157done_intr(sig_unused) /* called as signal() handler, so sent at least one arg */
158int 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__)
169static void
170done_hangup(sig)	/* signal() handler */
171int 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
181void
182done_in_by(mtmp)
183register 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*/
252void
253panic 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
312STATIC_OVL boolean
313should_query_disclose_option(category, defquery)
314int category;
315char *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
350STATIC_OVL void
351disclose(how,taken)
352int how;
353boolean 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 */
411STATIC_OVL void
412savelife(how)
413int 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 */
441STATIC_OVL void
442get_valuables(list)
443struct 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 */
474STATIC_OVL void
475sort_valuables(list, size)
476struct valuable_data list[];
477int 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 */
497STATIC_OVL void
498artifact_score(list, counting, endwin)
499struct obj *list;
500boolean counting;	/* true => add up points; false => display them */
501winid 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! */
535void
536done(how)
537int 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
613die:
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
918void
919container_contents(list, identified, all_containers)
920struct obj *list;
921boolean 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 */
959void
960terminate(status)
961int 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
976STATIC_OVL void
977list_vanquished(defquery, ask)
978char defquery;
979boolean 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 */
1051int
1052num_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
1062STATIC_OVL void
1063list_genocided(defquery, ask)
1064char defquery;
1065boolean 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