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