From c7b42e7ba161dea5d7edaf8f253876fcabed34d6 Mon Sep 17 00:00:00 2001 From: jwalz Date: Sat, 5 Jan 2002 21:05:49 +0000 Subject: [PATCH] *** empty log message *** --- src/end.c | 1022 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1022 insertions(+) create mode 100644 src/end.c diff --git a/src/end.c b/src/end.c new file mode 100644 index 000000000..6dacd8a43 --- /dev/null +++ b/src/end.c @@ -0,0 +1,1022 @@ +/* SCCS Id: @(#)end.c 3.3 2001/09/24 */ +/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ +/* NetHack may be freely redistributed. See license for details. */ + +#define NEED_VARARGS /* comment line for pre-compiled headers */ + +#include "hack.h" +#include "eshk.h" +#ifndef NO_SIGNAL +#include +#endif +#include "dlb.h" + + /* these probably ought to be generated by makedefs, like LAST_GEM */ +#define FIRST_GEM DILITHIUM_CRYSTAL +#define FIRST_AMULET AMULET_OF_ESP +#define LAST_AMULET AMULET_OF_YENDOR + +struct valuable_data { long count; int typ; }; + +struct valuable_data + gems[LAST_GEM+1 - FIRST_GEM + 1], /* 1 extra for glass */ + amulets[LAST_AMULET+1 - FIRST_AMULET]; + +static struct val_list { struct valuable_data *list; int size; } valuables[] = { + { gems, sizeof gems / sizeof *gems }, + { amulets, sizeof amulets / sizeof *amulets }, + { 0, 0 } +}; + +#ifndef NO_SIGNAL +STATIC_PTR void FDECL(done_intr, (int)); +# if defined(UNIX) || defined(VMS) || defined (__EMX__) +static void FDECL(done_hangup, (int)); +# endif +#endif +STATIC_DCL void FDECL(disclose,(int,BOOLEAN_P)); +STATIC_DCL void FDECL(get_valuables, (struct obj *)); +STATIC_DCL void FDECL(sort_valuables, (struct valuable_data *,int)); +STATIC_DCL void FDECL(add_artifact_score, (struct obj *)); +STATIC_DCL void FDECL(display_artifact_score, (struct obj *,winid)); +STATIC_DCL void FDECL(savelife, (int)); +STATIC_DCL void NDECL(list_vanquished); +STATIC_DCL void NDECL(list_genocided); + +#if defined(__BEOS__) || defined(MICRO) || defined(WIN32) || defined(OS2) +extern void FDECL(nethack_exit,(int)); +#else +#define nethack_exit exit +#endif + +#define done_stopprint program_state.stopprint + +#ifdef AMIGA +# define NH_abort() Abort(0) +#else +# ifdef SYSV +# define NH_abort() (void) abort() +# else +# define NH_abort() abort() +# endif +#endif + +/* + * The order of these needs to match the macros in hack.h. + */ +static NEARDATA const char *deaths[] = { /* the array of death */ + "died", "choked", "poisoned", "starvation", "drowning", + "burning", "dissolving under the heat and pressure", + "crushed", "turned to stone", "turned into slime", + "genocided", "panic", "trickery", + "quit", "escaped", "ascended" +}; + +static NEARDATA const char *ends[] = { /* "when you..." */ + "died", "choked", "were poisoned", "starved", "drowned", + "burned", "dissolved in the lava", + "were crushed", "turned to stone", "turned into slime", + "were genocided", "panicked", "were tricked", + "quit", "escaped", "ascended" +}; + +extern const char *killed_by_prefix[]; + + +/*ARGSUSED*/ +void +done1(sig_unused) /* called as signal() handler, so sent at least one arg */ +int sig_unused; +{ +#ifndef NO_SIGNAL + (void) signal(SIGINT,SIG_IGN); +#endif + if(flags.ignintr) { +#ifndef NO_SIGNAL + (void) signal(SIGINT, (SIG_RET_TYPE) done1); +#endif + clear_nhwindow(WIN_MESSAGE); + curs_on_u(); + wait_synch(); + if(multi > 0) nomul(0); + } else { + (void)done2(); + } +} + + +/* "#quit" command or keyboard interrupt */ +int +done2() +{ + if(yn("Really quit?") == 'n') { +#ifndef NO_SIGNAL + (void) signal(SIGINT, (SIG_RET_TYPE) done1); +#endif + clear_nhwindow(WIN_MESSAGE); + curs_on_u(); + wait_synch(); + if(multi > 0) nomul(0); + if(multi == 0) { + u.uinvulnerable = FALSE; /* avoid ctrl-C bug -dlc */ + u.usleep = 0; + } + return 0; + } +#if defined(WIZARD) && (defined(UNIX) || defined(VMS) || defined(LATTICE)) + if(wizard) { + int c; +# ifdef VMS + const char *tmp = "Enter debugger?"; +# else +# ifdef LATTICE + const char *tmp = "Create SnapShot?"; +# else + const char *tmp = "Dump core?"; +# endif +# endif + if ((c = ynq(tmp)) == 'y') { + (void) signal(SIGINT, (SIG_RET_TYPE) done1); + exit_nhwindows((char *)0); + NH_abort(); + } else if (c == 'q') done_stopprint++; + } +#endif +#ifndef LINT + done(QUIT); +#endif + return 0; +} + +#ifndef NO_SIGNAL +/*ARGSUSED*/ +STATIC_PTR void +done_intr(sig_unused) /* called as signal() handler, so sent at least one arg */ +int sig_unused; +{ + done_stopprint++; + (void) signal(SIGINT, SIG_IGN); +# if defined(UNIX) || defined(VMS) + (void) signal(SIGQUIT, SIG_IGN); +# endif + return; +} + +# if defined(UNIX) || defined(VMS) || defined(__EMX__) +static void +done_hangup(sig) /* signal() handler */ +int sig; +{ + program_state.done_hup++; + (void)signal(SIGHUP, SIG_IGN); + done_intr(sig); + return; +} +# endif +#endif /* NO_SIGNAL */ + +void +done_in_by(mtmp) +register struct monst *mtmp; +{ + char buf[BUFSZ]; + boolean distorted = (boolean)(Hallucination && canspotmon(mtmp)); + + You("die..."); + mark_synch(); /* flush buffered screen output */ + buf[0] = '\0'; + if ((mtmp->data->geno & G_UNIQ) != 0) { + if (!type_is_pname(mtmp->data)) + Strcat(buf, "the "); + killer_format = KILLED_BY; + } + if (mtmp->minvis) + Strcat(buf, "invisible "); + if (distorted) + Strcat(buf, "hallucinogen-distorted "); + + if(mtmp->data == &mons[PM_GHOST]) { + char *gn = NAME(mtmp); + if (!distorted && !mtmp->minvis && *gn) { + Strcat(buf, "the "); + killer_format = KILLED_BY; + } + Sprintf(eos(buf), (*gn ? "ghost of %s" : "ghost%s"), gn); + } else if(mtmp->isshk) { + Sprintf(eos(buf), "%s %s, the shopkeeper", + (mtmp->female ? "Ms." : "Mr."), shkname(mtmp)); + killer_format = KILLED_BY; + } else if (mtmp->ispriest || mtmp->isminion) { + /* m_monnam() suppresses "the" prefix plus "invisible", and + it overrides the effect of Hallucination on priestname() */ + killer = m_monnam(mtmp); + Strcat(buf, killer); + } else { + Strcat(buf, mtmp->data->mname); + if (mtmp->mnamelth) + Sprintf(eos(buf), " called %s", NAME(mtmp)); + } + + if (multi) Strcat(buf, ", while helpless"); + killer = buf; + if (mtmp->data->mlet == S_WRAITH) + u.ugrave_arise = PM_WRAITH; + else if (mtmp->data->mlet == S_MUMMY && urace.mummynum != NON_PM) + u.ugrave_arise = urace.mummynum; + else if (mtmp->data->mlet == S_VAMPIRE && Race_if(PM_HUMAN)) + u.ugrave_arise = PM_VAMPIRE; + else if (mtmp->data == &mons[PM_GHOUL]) + u.ugrave_arise = PM_GHOUL; + if (u.ugrave_arise >= LOW_PM && + (mvitals[u.ugrave_arise].mvflags & G_GENOD)) + u.ugrave_arise = NON_PM; + if (touch_petrifies(mtmp->data)) + done(STONING); + else + done(DIED); + return; +} + +/*VARARGS1*/ +void +panic VA_DECL(const char *, str) + VA_START(str); + VA_INIT(str, char *); + + if (program_state.panicking++) + NH_abort(); /* avoid loops - this should never happen*/ + + if (iflags.window_inited) { + raw_print("\r\nOops..."); + wait_synch(); /* make sure all pending output gets flushed */ + exit_nhwindows((char *)0); + iflags.window_inited = 0; /* they're gone; force raw_print()ing */ + } + + raw_print(!program_state.something_worth_saving ? + "Program initialization has failed." : + "Suddenly, the dungeon collapses."); +#if defined(WIZARD) && !defined(MICRO) + if (!wizard) + raw_printf("Report error to \"%s\"%s.", +# ifdef WIZARD_NAME /*(KR1ED)*/ + WIZARD_NAME, +# else + WIZARD, +# endif + !program_state.something_worth_saving ? "" : + " and it may be possible to rebuild."); + if (program_state.something_worth_saving) { + set_error_savefile(); + (void) dosave0(); + } +#endif + { + char buf[BUFSZ]; + Vsprintf(buf,str,VA_ARGS); + raw_print(buf); + } +#if defined(WIZARD) && (defined(UNIX) || defined(VMS) || defined(LATTICE)) + if (wizard) + NH_abort(); /* generate core dump */ +#endif + VA_END(); + done(PANICKED); +} + +STATIC_OVL void +disclose(how,taken) +int how; +boolean taken; +{ + char c; + char qbuf[QBUFSZ]; + + if (invent && !done_stopprint && + (!flags.end_disclose[0] || index(flags.end_disclose, 'i'))) { + if(taken) + Sprintf(qbuf,"Do you want to see what you had when you %s?", + (how == QUIT) ? "quit" : "died"); + else + Strcpy(qbuf,"Do you want your possessions identified?"); + if ((c = yn_function(qbuf, ynqchars, 'y')) == 'y') { + /* New dump format by maartenj@cs.vu.nl */ + struct obj *obj; + + for (obj = invent; obj; obj = obj->nobj) { + makeknown(obj->otyp); + obj->known = obj->bknown = obj->dknown = obj->rknown = 1; + } + (void) display_inventory((char *)0, TRUE); + container_contents(invent, TRUE, TRUE); + } + if (c == 'q') done_stopprint++; + } + + if (!done_stopprint && + (!flags.end_disclose[0] || index(flags.end_disclose, 'a'))) { + c = yn_function("Do you want to see your attributes?",ynqchars,'y'); + if (c == 'y') enlightenment(how >= PANICKED ? 1 : 2); /* final */ + if (c == 'q') done_stopprint++; + } + + if (!done_stopprint && + (!flags.end_disclose[0] || index(flags.end_disclose, 'v'))) { + list_vanquished(); + } + + if (!done_stopprint && + (!flags.end_disclose[0] || index(flags.end_disclose, 'g'))) { + list_genocided(); + } + + if (!done_stopprint && + (!flags.end_disclose[0] || index(flags.end_disclose, 'c'))) { + c = yn_function("Do you want to see your conduct?",ynqchars,'y'); + if (c == 'y') show_conduct(how >= PANICKED ? 1 : 2); + if (c == 'q') done_stopprint++; + } +} + +/* try to get the player back in a viable state after being killed */ +STATIC_OVL void +savelife(how) +int how; +{ + u.uswldtim = 0; + u.uhp = u.uhpmax; + if (u.uhunger < 500) { + u.uhunger = 500; + newuhs(FALSE); + } + /* cure impending doom of sickness hero won't have time to fix */ + if ((Sick & TIMEOUT) == 1) { + u.usick_type = 0; + Sick = 0; + } + if (how == CHOKING) init_uhunger(); + nomovemsg = "You survived that attempt on your life."; + flags.move = 0; + if(multi > 0) multi = 0; else multi = -1; + if(u.utrap && u.utraptype == TT_LAVA) u.utrap = 0; + flags.botl = 1; + u.ugrave_arise = NON_PM; + HUnchanging = 0L; + curs_on_u(); +} + +/* + * Get valuables from the given list. Revised code: the list always remains + * intact. + */ +STATIC_OVL void +get_valuables(list) +struct obj *list; /* inventory or container contents */ +{ + register struct obj *obj; + register int i; + + /* find amulets and gems, ignoring all artifacts */ + for (obj = list; obj; obj = obj->nobj) + if (Has_contents(obj)) { + get_valuables(obj->cobj); + } else if (obj->oartifact) { + continue; + } else if (obj->oclass == AMULET_CLASS) { + i = obj->otyp - FIRST_AMULET; + if (!amulets[i].count) { + amulets[i].count = obj->quan; + amulets[i].typ = obj->otyp; + } else amulets[i].count += obj->quan; /* always adds one */ + } else if (obj->oclass == GEM_CLASS && obj->otyp < LUCKSTONE) { + i = min(obj->otyp, LAST_GEM + 1) - FIRST_GEM; + if (!gems[i].count) { + gems[i].count = obj->quan; + gems[i].typ = obj->otyp; + } else gems[i].count += obj->quan; + } + return; +} + +/* + * Sort collected valuables, most frequent to least. We could just + * as easily use qsort, but we don't care about efficiency here. + */ +STATIC_OVL void +sort_valuables(list, size) +struct valuable_data list[]; +int size; /* max value is less than 20 */ +{ + register int i, j; + struct valuable_data ltmp; + + /* move greater quantities to the front of the list */ + for (i = 1; i < size; i++) { + if (list[i].count == 0) continue; /* empty slot */ + ltmp = list[i]; /* structure copy */ + for (j = i; j > 0; --j) + if (list[j-1].count >= ltmp.count) break; + else { + list[j] = list[j-1]; + } + list[j] = ltmp; + } + return; +} + +STATIC_OVL void +add_artifact_score(list) +struct obj *list; +{ + struct obj *otmp; + + for (otmp = list; otmp; otmp = otmp->nobj) + if (otmp->oartifact || + otmp->otyp == BELL_OF_OPENING || + otmp->otyp == SPE_BOOK_OF_THE_DEAD || + otmp->otyp == CANDELABRUM_OF_INVOCATION) { + /* shopkeepers charge 100x; 250x is arbitrary */ + u.urexp += 250L * (long)objects[otmp->otyp].oc_cost; + if (Has_contents(otmp)) + add_artifact_score(otmp->cobj); + } +} + +STATIC_OVL void +display_artifact_score(list,endwin) +struct obj *list; +winid endwin; +{ + char pbuf[BUFSZ]; + struct obj *otmp; + + for (otmp = list; otmp; otmp = otmp->nobj) { + if (otmp->oartifact || + otmp->otyp == BELL_OF_OPENING || + otmp->otyp == SPE_BOOK_OF_THE_DEAD || + otmp->otyp == CANDELABRUM_OF_INVOCATION) { + short dummy; + + makeknown(otmp->otyp); + otmp->known = otmp->bknown = otmp->dknown = + otmp->rknown = 1; + /* assumes artifacts don't have quan>1 */ + Sprintf(pbuf, "%s (worth %ld zorkmids and %ld points)", + otmp->oartifact ? artifact_name(xname(otmp), &dummy) : + OBJ_NAME(objects[otmp->otyp]), + 100L * (long)objects[otmp->otyp].oc_cost, + 250L * (long)objects[otmp->otyp].oc_cost); + putstr(endwin, 0, pbuf); + } + if (Has_contents(otmp)) + display_artifact_score(otmp->cobj,endwin); + } +} + +/* Be careful not to call panic from here! */ +void +done(how) +int how; +{ + boolean taken; + char kilbuf[BUFSZ], pbuf[BUFSZ]; + winid endwin = WIN_ERR; + boolean bones_ok, have_windows = iflags.window_inited; + struct obj *corpse = (struct obj *)0; + long umoney; + + /* kilbuf: used to copy killer in case it comes from something like + * xname(), which would otherwise get overwritten when we call + * xname() when listing possessions + * pbuf: holds Sprintf'd output for raw_print and putstr + */ + if (how == ASCENDED || (!killer && how == GENOCIDED)) + killer_format = NO_KILLER_PREFIX; + /* Avoid killed by "a" burning or "a" starvation */ + if (!killer && (how == STARVING || how == BURNING)) + killer_format = KILLED_BY; + Strcpy(kilbuf, (!killer || how >= PANICKED ? deaths[how] : killer)); + killer = kilbuf; +#ifdef WIZARD + if (wizard && how == TRICKED) { + You("are a very tricky wizard, it seems."); + return; + } +#endif + if (how < PANICKED) u.umortality++; + if (Lifesaved && (how <= GENOCIDED)) { + pline("But wait..."); + makeknown(AMULET_OF_LIFE_SAVING); + Your("medallion %s!", + !Blind ? "begins to glow" : "feels warm"); + if (how == CHOKING) You("vomit ..."); + You_feel("much better!"); + pline_The("medallion crumbles to dust!"); + if (uamul) useup(uamul); + + (void) adjattrib(A_CON, -1, TRUE); + if(u.uhpmax <= 0) u.uhpmax = 10; /* arbitrary */ + savelife(how); + if (how == GENOCIDED) + pline("Unfortunately you are still genocided..."); + else { + killer = 0; + killer_format = 0; + return; + } + } + if (( +#ifdef WIZARD + wizard || +#endif + discover) && (how <= GENOCIDED)) { + if(yn("Die?") == 'y') goto die; + pline("OK, so you don't %s.", + (how == CHOKING) ? "choke" : "die"); + if(u.uhpmax <= 0) u.uhpmax = u.ulevel * 8; /* arbitrary */ + savelife(how); + killer = 0; + killer_format = 0; + return; + } + + /* + * The game is now over... + */ + +die: + program_state.gameover = 1; + /* in case of a subsequent panic(), there's no point trying to save */ + program_state.something_worth_saving = 0; + /* turn off vision subsystem */ + vision_recalc(2); + /* might have been killed while using a disposable item, so make sure + it's gone prior to inventory disclosure and creation of bones data */ + inven_inuse(TRUE); + + /* Sometimes you die on the first move. Life's not fair. + * On those rare occasions you get hosed immediately, go out + * smiling... :-) -3. + */ + if (moves <= 1 && how < PANICKED) /* You die... --More-- */ + pline("Do not pass go. Do not collect 200 zorkmids."); + + if (have_windows) wait_synch(); /* flush screen output */ +#ifndef NO_SIGNAL + (void) signal(SIGINT, (SIG_RET_TYPE) done_intr); +# if defined(UNIX) || defined(VMS) || defined (__EMX__) + (void) signal(SIGQUIT, (SIG_RET_TYPE) done_intr); + (void) signal(SIGHUP, (SIG_RET_TYPE) done_hangup); +# endif +#endif /* NO_SIGNAL */ + + bones_ok = (how < GENOCIDED) && can_make_bones(); + + if (how == TURNED_SLIME) + u.ugrave_arise = PM_GREEN_SLIME; + + if (bones_ok && u.ugrave_arise < LOW_PM) { + /* corpse gets burnt up too */ + if (how == BURNING) + u.ugrave_arise = (NON_PM - 2); /* leave no corpse */ + else if (how == STONING) + u.ugrave_arise = (NON_PM - 1); /* statue instead of corpse */ + else if (u.ugrave_arise == NON_PM && + !(mvitals[u.umonnum].mvflags & G_NOCORPSE)) { + corpse = mk_named_object(CORPSE, &mons[u.umonnum], + u.ux, u.uy, plname); + Sprintf(pbuf, "%s, %s%s", plname, + killer_format == NO_KILLER_PREFIX ? "" : + killed_by_prefix[how], + killer_format == KILLED_BY_AN ? an(killer) : killer); + make_grave(u.ux, u.uy, pbuf); + } + } + + if (how == QUIT) { + killer_format = NO_KILLER_PREFIX; + if (u.uhp < 1) { + how = DIED; + u.umortality++; /* skipped above when how==QUIT */ + /* note that killer is pointing at kilbuf */ + Strcpy(kilbuf, "quit while already on Charon's boat"); + } + } + if (how == ESCAPED || how == PANICKED) + killer_format = NO_KILLER_PREFIX; + + if (how != PANICKED) { + /* these affect score and/or bones, but avoid them during panic */ + taken = paybill(how != QUIT); + paygd(); + clearpriests(); + } else taken = FALSE; /* lint; assert( !bones_ok ); */ + + clearlocks(); + + if (have_windows) display_nhwindow(WIN_MESSAGE, FALSE); + + if (strcmp(flags.end_disclose, "none") && how != PANICKED) + disclose(how, taken); + /* finish_paybill should be called after disclosure but before bones */ + if (bones_ok && taken) finish_paybill(); + + /* calculate score, before creating bones [container gold] */ + { + long tmp; + int deepest = deepest_lev_reached(FALSE); + +#ifndef GOLDOBJ + umoney = u.ugold; + tmp = u.ugold0; +#else + umoney = money_cnt(invent); + tmp = u.umoney0; +#endif + umoney += hidden_gold(); /* accumulate gold from containers */ + tmp = umoney - tmp; /* net gain */ + + if (tmp < 0L) + tmp = 0L; + if (how < PANICKED) + tmp -= tmp / 10L; + u.urexp += tmp; + u.urexp += 50L * (long)(deepest - 1); + if (deepest > 20) + u.urexp += 1000L * (long)((deepest > 30) ? 10 : deepest - 20); + if (how == ASCENDED) u.urexp *= 2L; + } + + if (bones_ok) { +#ifdef WIZARD + if (!wizard || yn("Save bones?") == 'y') +#endif + savebones(corpse); + /* corpse may be invalid pointer now so + ensure that it isn't used again */ + corpse = (struct obj *)0; + } + + /* update gold for the rip output, which can't use hidden_gold() + (containers will be gone by then if bones just got saved...) */ +#ifndef GOLDOBJ + u.ugold = umoney; +#else + /* FIXME */ +#endif + + /* clean up unneeded windows */ + if (have_windows) { + wait_synch(); + display_nhwindow(WIN_MESSAGE, TRUE); + destroy_nhwindow(WIN_MAP); + destroy_nhwindow(WIN_STATUS); + destroy_nhwindow(WIN_MESSAGE); + WIN_MESSAGE = WIN_STATUS = WIN_MAP = WIN_ERR; + + if(!done_stopprint || flags.tombstone) + endwin = create_nhwindow(NHW_TEXT); + + if(how < GENOCIDED && flags.tombstone) outrip(endwin, how); + } else + done_stopprint = 1; /* just avoid any more output */ + +/* changing kilbuf really changes killer. we do it this way because + killer is declared a (const char *) +*/ + if (u.uhave.amulet) Strcat(kilbuf, " (with the Amulet)"); + else if (how == ESCAPED) { + if (Is_astralevel(&u.uz)) /* offered Amulet to wrong deity */ + Strcat(kilbuf, " (in celestial disgrace)"); + else if (carrying(FAKE_AMULET_OF_YENDOR)) + Strcat(kilbuf, " (with a fake Amulet)"); + /* don't bother counting to see whether it should be plural */ + } + + if (!done_stopprint) { + Sprintf(pbuf, "%s %s the %s...", Goodbye(), plname, + how != ASCENDED ? + (const char *) ((flags.female && urole.name.f) ? + urole.name.f : urole.name.m) : + (const char *) (flags.female ? "Demigoddess" : "Demigod")); + putstr(endwin, 0, pbuf); + putstr(endwin, 0, ""); + } + + if (how == ESCAPED || how == ASCENDED) { + register struct monst *mtmp; + register struct obj *otmp; + register struct val_list *val; + register int i; + + for (val = valuables; val->list; val++) + for (i = 0; i < val->size; i++) { + val->list[i].count = 0L; + } + get_valuables(invent); + + /* add points for collected valuables */ + for (val = valuables; val->list; val++) + for (i = 0; i < val->size; i++) + if (val->list[i].count != 0L) + u.urexp += val->list[i].count + * (long)objects[val->list[i].typ].oc_cost; + + add_artifact_score(invent); + + keepdogs(TRUE); + viz_array[0][0] |= IN_SIGHT; /* need visibility for naming */ + mtmp = mydogs; + if (!done_stopprint) Strcpy(pbuf, "You"); + if (mtmp) { + while (mtmp) { + if (!done_stopprint) + Sprintf(eos(pbuf), " and %s", mon_nam(mtmp)); + if (mtmp->mtame) + u.urexp += mtmp->mhp; + mtmp = mtmp->nmon; + } + if (!done_stopprint) putstr(endwin, 0, pbuf); + pbuf[0] = '\0'; + } else { + if (!done_stopprint) Strcat(pbuf, " "); + } + if (!done_stopprint) { + Sprintf(eos(pbuf), "%s with %ld point%s,", + how==ASCENDED ? "went to your reward" : + "escaped from the dungeon", + u.urexp, plur(u.urexp)); + putstr(endwin, 0, pbuf); + } + + if (!done_stopprint) + display_artifact_score(invent,endwin); + + /* list valuables here */ + for (val = valuables; val->list; val++) { + sort_valuables(val->list, val->size); + for (i = 0; i < val->size && !done_stopprint; i++) { + int typ = val->list[i].typ; + long count = val->list[i].count; + + if (count == 0L) continue; + if (objects[typ].oc_class != GEM_CLASS || typ <= LAST_GEM) { + otmp = mksobj(typ, FALSE, FALSE); + makeknown(otmp->otyp); + otmp->known = 1; /* for fake amulets */ + otmp->dknown = 1; /* seen it (blindness fix) */ + otmp->onamelth = 0; + otmp->quan = count; + Sprintf(pbuf, "%8ld %s (worth %ld zorkmids),", + count, xname(otmp), + count * (long)objects[typ].oc_cost); + obfree(otmp, (struct obj *)0); + } else { + Sprintf(pbuf, + "%8ld worthless piece%s of colored glass,", + count, plur(count)); + } + putstr(endwin, 0, pbuf); + } + } + + } else if (!done_stopprint) { + /* did not escape or ascend */ + if (u.uz.dnum == 0 && u.uz.dlevel <= 0) { + /* level teleported out of the dungeon; `how' is DIED, + due to falling or to "arriving at heaven prematurely" */ + Sprintf(pbuf, "You %s beyond the confines of the dungeon", + (u.uz.dlevel < 0) ? "passed away" : ends[how]); + } else { + /* more conventional demise */ + const char *where = dungeons[u.uz.dnum].dname; + + if (Is_astralevel(&u.uz)) where = "The Astral Plane"; + Sprintf(pbuf, "You %s in %s", ends[how], where); + if (!In_endgame(&u.uz) && !Is_knox(&u.uz)) + Sprintf(eos(pbuf), " on dungeon level %d", + In_quest(&u.uz) ? dunlev(&u.uz) : depth(&u.uz)); + } + + Sprintf(eos(pbuf), " with %ld point%s,", + u.urexp, plur(u.urexp)); + putstr(endwin, 0, pbuf); + } + + if (!done_stopprint) { + Sprintf(pbuf, "and %ld piece%s of gold, after %ld move%s.", + umoney, plur(umoney), moves, plur(moves)); + putstr(endwin, 0, pbuf); + } + if (!done_stopprint) { + Sprintf(pbuf, + "You were level %d with a maximum of %d hit point%s when you %s.", + u.ulevel, u.uhpmax, plur(u.uhpmax), ends[how]); + putstr(endwin, 0, pbuf); + putstr(endwin, 0, ""); + } + if (!done_stopprint) + display_nhwindow(endwin, TRUE); + if (endwin != WIN_ERR) + destroy_nhwindow(endwin); + + /* "So when I die, the first thing I will see in Heaven is a + * score list?" */ + if (flags.toptenwin) { + topten(how); + if (have_windows) + exit_nhwindows((char *)0); + } else { + if (have_windows) + exit_nhwindows((char *)0); + topten(how); + } + + if(done_stopprint) { raw_print(""); raw_print(""); } + terminate(EXIT_SUCCESS); +} + + +void +container_contents(list, identified, all_containers) +struct obj *list; +boolean identified, all_containers; +{ + register struct obj *box, *obj; + char buf[BUFSZ]; + + for (box = list; box; box = box->nobj) { + if (Is_container(box) && box->otyp != BAG_OF_TRICKS) { + if (box->cobj) { + winid tmpwin = create_nhwindow(NHW_MENU); + Sprintf(buf, "Contents of %s:", the(xname(box))); + putstr(tmpwin, 0, buf); + putstr(tmpwin, 0, ""); + for (obj = box->cobj; obj; obj = obj->nobj) { + if (identified) { + makeknown(obj->otyp); + obj->known = obj->bknown = + obj->dknown = obj->rknown = 1; + } + putstr(tmpwin, 0, doname(obj)); + } + display_nhwindow(tmpwin, TRUE); + destroy_nhwindow(tmpwin); + if (all_containers) + container_contents(box->cobj, identified, TRUE); + } else { + pline("%s is empty.", The(xname(box))); + display_nhwindow(WIN_MESSAGE, FALSE); + } + } + if (!all_containers) + break; + } +} + + +/* should be called with either EXIT_SUCCESS or EXIT_FAILURE */ +void +terminate(status) +int status; +{ +#ifdef MAC + getreturn("to exit"); +#endif + /* don't bother to try to release memory if we're in panic mode, to + avoid trouble in case that happens to be due to memory problems */ + if (!program_state.panicking) { + freedynamicdata(); + dlb_cleanup(); + } + + nethack_exit(status); +} + +STATIC_OVL void +list_vanquished() +{ + register int i, lev; + int ntypes = 0, max_lev = 0, nkilled; + long total_killed = 0L; + char c; + winid klwin; + char buf[BUFSZ]; + + /* get totals first */ + for (i = LOW_PM; i < NUMMONS; i++) { + if (mvitals[i].died) ntypes++; + total_killed += (long)mvitals[i].died; + if (mons[i].mlevel > max_lev) max_lev = mons[i].mlevel; + } + + /* vanquished creatures list; + * includes all dead monsters, not just those killed by the player + */ + if (ntypes != 0) { + c = yn_function("Do you want an account of creatures vanquished?", + ynqchars, 'n'); + if (c == 'q') done_stopprint++; + if (c == 'y') { + klwin = create_nhwindow(NHW_MENU); + putstr(klwin, 0, "Vanquished creatures:"); + putstr(klwin, 0, ""); + + /* countdown by monster "toughness" */ + for (lev = max_lev; lev >= 0; lev--) + for (i = LOW_PM; i < NUMMONS; i++) + if (mons[i].mlevel == lev && (nkilled = mvitals[i].died) > 0) { + if ((mons[i].geno & G_UNIQ) && i != PM_HIGH_PRIEST) { + Sprintf(buf, "%s%s", + !type_is_pname(&mons[i]) ? "The " : "", + mons[i].mname); + if (nkilled > 1) { + switch (nkilled) { + case 2: Sprintf(eos(buf)," (twice)"); break; + case 3: Sprintf(eos(buf)," (thrice)"); break; + default: Sprintf(eos(buf)," (%d time%s)", + nkilled, plur(nkilled)); + break; + } + } + } else { + /* trolls or undead might have come back, + but we don't keep track of that */ + if (nkilled == 1) + Strcpy(buf, an(mons[i].mname)); + else + Sprintf(buf, "%d %s", + nkilled, makeplural(mons[i].mname)); + } + putstr(klwin, 0, buf); + } + /* + * if (Hallucination) + * putstr(klwin, 0, "and a partridge in a pear tree"); + */ + if (ntypes > 1) { + putstr(klwin, 0, ""); + Sprintf(buf, "%ld creatures vanquished.", total_killed); + putstr(klwin, 0, buf); + } + display_nhwindow(klwin, TRUE); + destroy_nhwindow(klwin); + } + } +} + +/* number of monster species which have been genocided */ +int +num_genocides() +{ + int i, n = 0; + + for (i = LOW_PM; i < NUMMONS; ++i) + if (mvitals[i].mvflags & G_GENOD) ++n; + + return n; +} + +STATIC_OVL void +list_genocided() +{ + register int i; + int ngenocided; + char c; + winid klwin; + char buf[BUFSZ]; + + ngenocided = num_genocides(); + + /* genocided species list */ + if (ngenocided != 0) { + c = yn_function("Do you want a list of species genocided?", + ynqchars, 'n'); + if (c == 'q') done_stopprint++; + if (c == 'y') { + klwin = create_nhwindow(NHW_MENU); + putstr(klwin, 0, "Genocided species:"); + putstr(klwin, 0, ""); + + for (i = LOW_PM; i < NUMMONS; i++) + if (mvitals[i].mvflags & G_GENOD) { + if ((mons[i].geno & G_UNIQ) && i != PM_HIGH_PRIEST) + Sprintf(buf, "%s%s", + !type_is_pname(&mons[i]) ? "" : "the ", + mons[i].mname); + else + Strcpy(buf, makeplural(mons[i].mname)); + putstr(klwin, 0, buf); + } + + putstr(klwin, 0, ""); + Sprintf(buf, "%d species genocided.", ngenocided); + putstr(klwin, 0, buf); + + display_nhwindow(klwin, TRUE); + destroy_nhwindow(klwin); + } + } +} + +/*end.c*/