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