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