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