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