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