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