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