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*/