Source:NetHack 3.6.0/src/end.c

From NetHackWiki
Revision as of 01:58, 16 December 2015 by DizzyBot (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Below is the full text to end.c from the source code of NetHack 3.6.0. To link to a particular line, write [[Source:NetHack 3.6.0/src/end.c#line123]], for example.

The NetHack General Public License applies to screenshots, source code and other content from NetHack.

This content was modified from the original NetHack source code distribution (by splitting up NetHack content between wiki pages, and possibly further editing). See the page history for a list of who changed it, and on what dates.

Top of file

 /* NetHack 3.6	end.c	$NHDT-Date: 1448241780 2015/11/23 01:23:00 $  $NHDT-Branch: master $:$NHDT-Revision: 1.108 $ */
 /* 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 "lev.h"
 #ifndef NO_SIGNAL
 #include <signal.h>
 #endif
 #include <ctype.h>
 #include <limits.h>
 #include "dlb.h"
 
 /* add b to long a, convert wraparound to max value */
 #define nowrap_add(a, b) (a = ((a + b) < 0 ? LONG_MAX : (a + b)))
 
 /* 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;
 };
 
 static 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(artifact_score, (struct obj *, BOOLEAN_P, winid));
 STATIC_DCL void FDECL(really_done, (int)) NORETURN;
 STATIC_DCL boolean FDECL(odds_and_ends, (struct obj *, int));
 STATIC_DCL void FDECL(savelife, (int));
 STATIC_DCL void FDECL(list_vanquished, (CHAR_P, BOOLEAN_P));
 STATIC_DCL void FDECL(list_genocided, (CHAR_P, BOOLEAN_P));
 STATIC_DCL boolean FDECL(should_query_disclose_option, (int, char *));
 STATIC_DCL int NDECL(num_extinct);
 
 #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
 
 #ifndef PANICTRACE
 #define NH_abort NH_abort_
 #endif
 
 #ifdef AMIGA
 #define NH_abort_() Abort(0)
 #else
 #ifdef SYSV
 #define NH_abort_() (void) abort()
 #else
 #ifdef WIN32
 #define NH_abort_() win32_abort()
 #else
 #define NH_abort_() abort()
 #endif
 #endif /* !SYSV */
 #endif /* !AMIGA */
 
 #ifdef PANICTRACE
 #include <errno.h>
 #ifdef PANICTRACE_LIBC
 #include <execinfo.h>
 #endif
 
 /* What do we try and in what order?  Tradeoffs:
  * libc: +no external programs required
  *        -requires newish libc/glibc
  *        -requires -rdynamic
  * gdb:   +gives more detailed information
  *        +works on more OS versions
  *        -requires -g, which may preclude -O on some compilers
  */
 #ifdef SYSCF
 #define SYSOPT_PANICTRACE_GDB sysopt.panictrace_gdb
 #ifdef PANICTRACE_LIBC
 #define SYSOPT_PANICTRACE_LIBC sysopt.panictrace_libc
 #else
 #define SYSOPT_PANICTRACE_LIBC 0
 #endif
 #else
 #define SYSOPT_PANICTRACE_GDB (nh_getenv("NETHACK_USE_GDB") == 0 ? 0 : 2)
 #ifdef PANICTRACE_LIBC
 #define SYSOPT_PANICTRACE_LIBC 1
 #else
 #define SYSOPT_PANICTRACE_LIBC 0
 #endif
 #endif
 
 static void NDECL(NH_abort);
 #ifndef NO_SIGNAL
 static void FDECL(panictrace_handler, (int));
 #endif
 static boolean NDECL(NH_panictrace_libc);
 static boolean NDECL(NH_panictrace_gdb);
 

panictrace_handler

 #ifndef NO_SIGNAL
 /*ARGSUSED*/
 void panictrace_handler(
     sig_unused) /* called as signal() handler, so sent at least one arg */
 int sig_unused UNUSED;
 {
 #define SIG_MSG "\nSignal received.\n"
     (void) write(2, SIG_MSG, sizeof(SIG_MSG) - 1);
     NH_abort();
 }
 

panictrace_setsignals

 void
 panictrace_setsignals(set)
 boolean set;
 {
 #define SETSIGNAL(sig) \
     (void) signal(sig, set ? (SIG_RET_TYPE) panictrace_handler : SIG_DFL);
 #ifdef SIGILL
     SETSIGNAL(SIGILL);
 #endif
 #ifdef SIGTRAP
     SETSIGNAL(SIGTRAP);
 #endif
 #ifdef SIGIOT
     SETSIGNAL(SIGIOT);
 #endif
 #ifdef SIGBUS
     SETSIGNAL(SIGBUS);
 #endif
 #ifdef SIGFPE
     SETSIGNAL(SIGFPE);
 #endif
 #ifdef SIGSEGV
     SETSIGNAL(SIGSEGV);
 #endif
 #ifdef SIGSTKFLT
     SETSIGNAL(SIGSTKFLT);
 #endif
 #ifdef SIGSYS
     SETSIGNAL(SIGSYS);
 #endif
 #ifdef SIGEMT
     SETSIGNAL(SIGEMT);
 #endif
 #undef SETSIGNAL
 }
 #endif /* NO_SIGNAL */
 

NH_abort

 static void
 NH_abort()
 {
     int gdb_prio = SYSOPT_PANICTRACE_GDB;
     int libc_prio = SYSOPT_PANICTRACE_LIBC;
     static boolean aborting = FALSE;
 
     if (aborting)
         return;
     aborting = TRUE;
 
 #ifndef VMS
     if (gdb_prio == libc_prio && gdb_prio > 0)
         gdb_prio++;
 
     if (gdb_prio > libc_prio) {
         NH_panictrace_gdb() || (libc_prio && NH_panictrace_libc());
     } else {
         NH_panictrace_libc() || (gdb_prio && NH_panictrace_gdb());
     }
 
 #else /* VMS */
     /* overload otherwise unused priority for debug mode: 1 = show
        traceback and exit; 2 = show traceback and stay in debugger */
     /* if (wizard && gdb_prio == 1) gdb_prio = 2; */
     vms_traceback(gdb_prio);
     (void) libc_prio; /* half-hearted attempt at lint suppression */
 
 #endif /* ?VMS */
 
 #ifndef NO_SIGNAL
     panictrace_setsignals(FALSE);
 #endif
     NH_abort_();
 }
 

NH_panictrace_libc

 static boolean
 NH_panictrace_libc()
 {
 #ifdef PANICTRACE_LIBC
     void *bt[20];
     size_t count, x;
     char **info;
 
     raw_print("Generating more information you may report:\n");
     count = backtrace(bt, SIZE(bt));
     info = backtrace_symbols(bt, count);
     for (x = 0; x < count; x++) {
         raw_printf("[%lu] %s", (unsigned long) x, info[x]);
     }
     /* free(info);   -- Don't risk it. */
     return TRUE;
 #else
     return FALSE;
 #endif /* !PANICTRACE_LIBC */
 }
 

NH_panictrace_gdb

 /*
  *   fooPATH  file system path for foo
  *   fooVAR   (possibly const) variable containing fooPATH
  */
 #ifdef PANICTRACE_GDB
 #ifdef SYSCF
 #define GDBVAR sysopt.gdbpath
 #define GREPVAR sysopt.greppath
 #else /* SYSCF */
 #define GDBVAR GDBPATH
 #define GREPVAR GREPPATH
 #endif /* SYSCF */
 #endif /* PANICTRACE_GDB */
 
 static boolean
 NH_panictrace_gdb()
 {
 #ifdef PANICTRACE_GDB
     /* A (more) generic method to get a stack trace - invoke
      * gdb on ourself. */
     char *gdbpath = GDBVAR;
     char *greppath = GREPVAR;
     char buf[BUFSZ];
     FILE *gdb;
 
     if (gdbpath == NULL || gdbpath[0] == 0)
         return FALSE;
     if (greppath == NULL || greppath[0] == 0)
         return FALSE;
 
     sprintf(buf, "%s -n -q %s %d 2>&1 | %s '^#'", gdbpath, ARGV0, getpid(),
             greppath);
     gdb = popen(buf, "w");
     if (gdb) {
         raw_print("Generating more information you may report:\n");
         fprintf(gdb, "bt\nquit\ny");
         fflush(gdb);
         sleep(4); /* ugly */
         pclose(gdb);
         return TRUE;
     } else {
         return FALSE;
     }
 #else
     return FALSE;
 #endif /* !PANICTRACE_GDB */
 }
 #endif /* PANICTRACE */
 

Death/end strings

 /*
  * 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 %s" */
     "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"
 };
 
 static boolean Schroedingers_cat = FALSE;
 

done1

 /*ARGSUSED*/
 void
 done1(sig_unused) /* called as signal() handler, so sent at least one arg */
 int sig_unused 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();
     }
 }
 

done2

 /* "#quit" command or keyboard interrupt */
 int
 done2()
 {
     if (!paranoid_query(ParanoidQuit, "Really quit?")) {
 #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(UNIX) || defined(VMS) || defined(LATTICE))
     if (wizard) {
         int c;
 #ifdef VMS
         extern int debuggable; /* sys/vms/vmsmisc.c, vmsunix.c */
 
         c = !debuggable ? 'n' : ynq("Enter debugger?");
 #else
 #ifdef LATTICE
         c = ynq("Create SnapShot?");
 #else
         c = ynq("Dump core?");
 #endif
 #endif
         if (c == 'y') {
 #ifndef NO_SIGNAL
             (void) signal(SIGINT, (SIG_RET_TYPE) done1);
 #endif
             exit_nhwindows((char *) 0);
             NH_abort();
         } else if (c == 'q')
             done_stopprint++;
     }
 #endif
 #ifndef LINT
     done(QUIT);
 #endif
     return 0;
 }
 

done_intr

 #ifndef NO_SIGNAL
 /*ARGSUSED*/
 STATIC_PTR void
 done_intr(sig_unused) /* called as signal() handler, so sent at least 1 arg */
 int sig_unused UNUSED;
 {
     done_stopprint++;
     (void) signal(SIGINT, SIG_IGN);
 #if defined(UNIX) || defined(VMS)
     (void) signal(SIGQUIT, SIG_IGN);
 #endif
     return;
 }
 

done_hangup

 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
 /* signal() handler */
 static void
 done_hangup(sig)
 int sig;
 {
     program_state.done_hup++;
     sethanguphandler((void FDECL((*), (int) )) SIG_IGN);
     done_intr(sig);
     return;
 }
 #endif
 #endif /* NO_SIGNAL */
 

done_in_by

 void
 done_in_by(mtmp, how)
 struct monst *mtmp;
 int how;
 {
     char buf[BUFSZ];
     struct permonst *mptr = mtmp->data,
                     *champtr = ((mtmp->cham >= LOW_PM)
                                    ? &mons[mtmp->cham]
                                    : mptr);
     boolean distorted = (boolean) (Hallucination && canspotmon(mtmp)),
             mimicker = (mtmp->m_ap_type == M_AP_MONSTER),
             imitator = (mptr != champtr || mimicker);
 
     You((how == STONING) ? "turn to stone..." : "die...");
     mark_synch(); /* flush buffered screen output */
     buf[0] = '\0';
     killer.format = KILLED_BY_AN;
     /* "killed by the high priest of Crom" is okay,
        "killed by the high priest" alone isn't */
     if ((mptr->geno & G_UNIQ) != 0 && !(imitator && !mimicker)
         && !(mptr == &mons[PM_HIGH_PRIEST] && !mtmp->ispriest)) {
         if (!type_is_pname(mptr))
             Strcat(buf, "the ");
         killer.format = KILLED_BY;
     }
     /* _the_ <invisible> <distorted> ghost of Dudley */
     if (mptr == &mons[PM_GHOST] && has_mname(mtmp)) {
         Strcat(buf, "the ");
         killer.format = KILLED_BY;
     }
     if (mtmp->minvis)
         Strcat(buf, "invisible ");
     if (distorted)
         Strcat(buf, "hallucinogen-distorted ");
 
     if (imitator) {
         char shape[BUFSZ];
         const char *realnm = champtr->mname, *fakenm = mptr->mname;
         boolean alt = is_vampshifter(mtmp);
 
         if (mimicker) {
             /* realnm is already correct because champtr==mptr;
                set up fake mptr for type_is_pname/the_unique_pm */
             mptr = &mons[mtmp->mappearance];
             fakenm = mptr->mname;
         } else if (alt && strstri(realnm, "vampire")
                    && !strcmp(fakenm, "vampire bat")) {
             /* special case: use "vampire in bat form" in preference
                to redundant looking "vampire in vampire bat form" */
             fakenm = "bat";
         }
         /* for the alternate format, always suppress any article;
            pname and the_unique should also have s_suffix() applied,
            but vampires don't take on any shapes which warrant that */
         if (alt || type_is_pname(mptr)) /* no article */
             Strcpy(shape, fakenm);
         else if (the_unique_pm(mptr)) /* "the"; don't use the() here */
             Sprintf(shape, "the %s", fakenm);
         else /* "a"/"an" */
             Strcpy(shape, an(fakenm));
         /* omit "called" to avoid excessive verbosity */
         Sprintf(eos(buf),
                 alt ? "%s in %s form"
                     : mimicker ? "%s disguised as %s"
                                : "%s imitating %s",
                 realnm, shape);
         mptr = mtmp->data; /* reset for mimicker case */
     } else if (mptr == &mons[PM_GHOST]) {
         Strcat(buf, "ghost");
         if (has_mname(mtmp))
             Sprintf(eos(buf), " of %s", MNAME(mtmp));
     } else if (mtmp->isshk) {
         const char *shknm = shkname(mtmp),
                    *honorific = shkname_is_pname(mtmp) ? ""
                                    : mtmp->female ? "Ms. " : "Mr. ";
 
         Sprintf(eos(buf), "%s%s, the shopkeeper", honorific, shknm);
         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() */
         Strcat(buf, m_monnam(mtmp));
     } else {
         Strcat(buf, mptr->mname);
         if (has_mname(mtmp))
             Sprintf(eos(buf), " called %s", MNAME(mtmp));
     }
 
     Strcpy(killer.name, buf);
     if (mptr->mlet == S_WRAITH)
         u.ugrave_arise = PM_WRAITH;
     else if (mptr->mlet == S_MUMMY && urace.mummynum != NON_PM)
         u.ugrave_arise = urace.mummynum;
     else if (mptr->mlet == S_VAMPIRE && Race_if(PM_HUMAN))
         u.ugrave_arise = PM_VAMPIRE;
     else if (mptr == &mons[PM_GHOUL])
         u.ugrave_arise = PM_GHOUL;
     /* this could happen if a high-end vampire kills the hero
        when ordinary vampires are genocided; ditto for wraiths */
     if (u.ugrave_arise >= LOW_PM
         && (mvitals[u.ugrave_arise].mvflags & G_GENOD))
         u.ugrave_arise = NON_PM;
 
     done(how);
     return;
 }
 

panic

 #if defined(WIN32) && !defined(SYSCF)
 #define NOTIFY_NETHACK_BUGS
 #endif
 
 /*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.gameover
                   ? "Postgame wrapup disrupted."
                   : !program_state.something_worth_saving
                         ? "Program initialization has failed."
                         : "Suddenly, the dungeon collapses.");
 #ifndef MICRO
 #if defined(NOTIFY_NETHACK_BUGS)
     if (!wizard)
         raw_printf("Report the following error to \"%s\" or at \"%s\".",
                    DEVTEAM_EMAIL, DEVTEAM_URL);
     else if (program_state.something_worth_saving)
         raw_print("\nError save file being written.\n");
 #else
     if (!wizard) {
         const char *maybe_rebuild = !program_state.something_worth_saving
                                      ? "."
                                      : "\nand it may be possible to rebuild.";
 
         if (sysopt.support)
             raw_printf("To report this error, %s%s", sysopt.support,
                        maybe_rebuild);
         else if (sysopt.fmtd_wizard_list) /* formatted SYSCF WIZARDS */
             raw_printf("To report this error, contact %s%s",
                        sysopt.fmtd_wizard_list, maybe_rebuild);
         else
             raw_printf("Report error to \"%s\"%s", WIZARD_NAME,
                        maybe_rebuild);
     }
 #endif
     /* XXX can we move this above the prints?  Then we'd be able to
      * suppress "it may be possible to rebuild" based on dosave0()
      * or say it's NOT possible to rebuild. */
     if (program_state.something_worth_saving) {
         set_error_savefile();
         if (dosave0()) {
             /* os/win port specific recover instructions */
             if (sysopt.recover)
                 raw_printf("%s", sysopt.recover);
         }
     }
 #endif
     {
         char buf[BUFSZ];
 
         Vsprintf(buf, str, VA_ARGS);
         raw_print(buf);
         paniclog("panic", buf);
     }
 #ifdef WIN32
     interject(INTERJECT_PANIC);
 #endif
 #if defined(UNIX) || defined(VMS) || defined(LATTICE) || defined(WIN32)
     if (wizard)
         NH_abort(); /* generate core dump */
 #endif
     VA_END();
     really_done(PANICKED);
 }
 

should_query_diclose_option

 STATIC_OVL boolean
 should_query_disclose_option(category, defquery)
 int category;
 char *defquery;
 {
     int idx;
     char *dop;
 
     *defquery = 'n';
     if ((dop = index(disclosure_options, category)) != 0) {
         idx = (int) (dop - disclosure_options);
         if (idx < 0 || idx >= NUM_DISCLOSURE_OPTIONS) {
             impossible(
                    "should_query_disclose_option: bad disclosure index %d %c",
                        idx, category);
             *defquery = DISCLOSE_PROMPT_DEFAULT_YES;
             return TRUE;
         }
         if (flags.end_disclose[idx] == DISCLOSE_YES_WITHOUT_PROMPT) {
             *defquery = 'y';
             return FALSE;
         } else if (flags.end_disclose[idx] == DISCLOSE_NO_WITHOUT_PROMPT) {
             *defquery = 'n';
             return FALSE;
         } else if (flags.end_disclose[idx] == DISCLOSE_PROMPT_DEFAULT_YES) {
             *defquery = 'y';
             return TRUE;
         } else {
             *defquery = 'n';
             return TRUE;
         }
     }
     impossible("should_query_disclose_option: bad category %c", category);
     return TRUE;
 }
 

diclose

 STATIC_OVL void
 disclose(how, taken)
 int how;
 boolean taken;
 {
     char c = '\0', defquery;
     char qbuf[QBUFSZ];
     boolean ask = FALSE;
 
     if (invent && !done_stopprint) {
         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?");
 
         ask = should_query_disclose_option('i', &defquery);
         c = ask ? yn_function(qbuf, ynqchars, defquery) : defquery;
         if (c == 'y') {
             struct obj *obj;
 
             for (obj = invent; obj; obj = obj->nobj) {
                 makeknown(obj->otyp);
                 obj->known = obj->bknown = obj->dknown = obj->rknown = 1;
                 if (Is_container(obj) || obj->otyp == STATUE)
                     obj->cknown = obj->lknown = 1;
             }
             (void) display_inventory((char *) 0, TRUE);
             container_contents(invent, TRUE, TRUE, FALSE);
         }
         if (c == 'q')
             done_stopprint++;
     }
 
     if (!done_stopprint) {
         ask = should_query_disclose_option('a', &defquery);
         c = ask ? yn_function("Do you want to see your attributes?", ynqchars,
                               defquery)
                 : defquery;
         if (c == 'y')
             enlightenment((BASICENLIGHTENMENT | MAGICENLIGHTENMENT),
                           (how >= PANICKED) ? ENL_GAMEOVERALIVE
                                             : ENL_GAMEOVERDEAD);
         if (c == 'q')
             done_stopprint++;
     }
 
     if (!done_stopprint) {
         ask = should_query_disclose_option('v', &defquery);
         list_vanquished(defquery, ask);
     }
 
     if (!done_stopprint) {
         ask = should_query_disclose_option('g', &defquery);
         list_genocided(defquery, ask);
     }
 
     if (!done_stopprint) {
         ask = should_query_disclose_option('c', &defquery);
         c = ask ? yn_function("Do you want to see your conduct?", ynqchars,
                               defquery)
                 : defquery;
         if (c == 'y')
             show_conduct((how >= PANICKED) ? 1 : 2);
         if (c == 'q')
             done_stopprint++;
     }
 
     if (!done_stopprint) {
         ask = should_query_disclose_option('o', &defquery);
         c = ask ? yn_function("Do you want to see the dungeon overview?",
                               ynqchars, defquery)
                 : defquery;
         if (c == 'y')
             show_overview((how >= PANICKED) ? 1 : 2, how);
         if (c == 'q')
             done_stopprint++;
     }
 }
 

savelife

 /* try to get the player back in a viable state after being killed */
 STATIC_OVL void
 savelife(how)
 int how;
 {
     int uhpmin = max(2 * u.ulevel, 10);
 
     if (u.uhpmax < uhpmin)
         u.uhpmax = uhpmin;
     u.uhp = u.uhpmax;
     u.uswldtim = 0;
     if (u.uhunger < 500) {
         u.uhunger = 500;
         newuhs(FALSE);
     }
     /* cure impending doom of sickness hero won't have time to fix */
     if ((Sick & TIMEOUT) == 1L) {
         u.usick_type = 0;
         set_itimeout(&Sick, 0L);
     }
     if (how == CHOKING)
         init_uhunger();
     nomovemsg = "You survived that attempt on your life.";
     context.move = 0;
     if (multi > 0)
         multi = 0;
     else
         multi = -1;
     if (u.utrap && u.utraptype == TT_LAVA)
         u.utrap = 0;
     context.botl = 1;
     u.ugrave_arise = NON_PM;
     HUnchanging = 0L;
     curs_on_u();
 }
 

get_valuables

 /*
  * 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_valuables

 /*
  *  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;
 }
 

odds_and_ends

 #define CAT_CHECK 2
 
 STATIC_OVL boolean
 odds_and_ends(list, what)
 struct obj *list;
 int what;
 {
     struct obj *otmp;
     for (otmp = list; otmp; otmp = otmp->nobj) {
         switch (what) {
         case CAT_CHECK: /* Schroedinger's Cat */
             /* Ascending is deterministic */
             if (SchroedingersBox(otmp))
                 return rn2(2);
             break;
         }
         if (Has_contents(otmp))
             return odds_and_ends(otmp->cobj, what);
     }
     return FALSE;
 }
 

artifact_score

 /* called twice; first to calculate total, then to list relevant items */
 STATIC_OVL void
 artifact_score(list, counting, endwin)
 struct obj *list;
 boolean counting; /* true => add up points; false => display them */
 winid endwin;
 {
     char pbuf[BUFSZ];
     struct obj *otmp;
     long value, points;
     short dummy; /* object type returned by artifact_name() */
 
     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) {
             value = arti_cost(otmp); /* zorkmid value */
             points = value * 5 / 2;  /* score value */
             if (counting) {
                 nowrap_add(u.urexp, points);
             } else {
                 makeknown(otmp->otyp);
                 otmp->known = otmp->dknown = otmp->bknown = otmp->rknown = 1;
                 /* assumes artifacts don't have quan > 1 */
                 Sprintf(pbuf, "%s%s (worth %ld %s and %ld points)",
                         the_unique_obj(otmp) ? "The " : "",
                         otmp->oartifact ? artifact_name(xname(otmp), &dummy)
                                         : OBJ_NAME(objects[otmp->otyp]),
                         value, currency(value), points);
                 putstr(endwin, 0, pbuf);
             }
         }
         if (Has_contents(otmp))
             artifact_score(otmp->cobj, counting, endwin);
     }
 }
 

done

 /* Be careful not to call panic from here! */
 void
 done(how)
 int how;
 {
     if (how == TRICKED) {
         if (killer.name[0]) {
             paniclog("trickery", killer.name);
             killer.name[0] = 0;
         }
         if (wizard) {
             You("are a very tricky wizard, it seems.");
             return;
         }
     }
 
     if (how == ASCENDED || (!killer.name[0] && how == GENOCIDED))
         killer.format = NO_KILLER_PREFIX;
     /* Avoid killed by "a" burning or "a" starvation */
     if (!killer.name[0] && (how == STARVING || how == BURNING))
         killer.format = KILLED_BY;
     if (!killer.name[0] || how >= PANICKED)
         Strcpy(killer.name, deaths[how]);
 
     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);
         savelife(how);
         if (how == GENOCIDED) {
             pline("Unfortunately you are still genocided...");
         } else {
             killer.name[0] = 0;
             killer.format = 0;
             return;
         }
     }
     if ((wizard || discover) && (how <= GENOCIDED) &&
         !paranoid_query(ParanoidDie, "Die?")) {
         pline("OK, so you don't %s.", (how == CHOKING) ? "choke" : "die");
         savelife(how);
         killer.name[0] = 0;
         killer.format = 0;
         return;
     }
     really_done(how);
 }
 

really_done

 /* separated from done() in order to specify the __noreturn__ attribute */
 STATIC_OVL void
 really_done(how)
 int how;
 {
     boolean taken;
     char pbuf[BUFSZ];
     winid endwin = WIN_ERR;
     boolean bones_ok, have_windows = iflags.window_inited;
     struct obj *corpse = (struct obj *) 0;
     time_t endtime;
     long umoney;
     long tmp;
 
     /*
      *  The game is now over...
      */
     program_state.gameover = 1;
     /* in case of a subsequent panic(), there's no point trying to save */
     program_state.something_worth_saving = 0;
     /* render vision subsystem inoperative */
     iflags.vision_inited = 0;
 
     /* 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);
     /* maybe not on object lists; if an active light source, would cause
        big trouble (`obj_is_local' panic) for savebones() -> savelev() */
     if (thrownobj && thrownobj->where == OBJ_FREE)
         dealloc_obj(thrownobj);
     if (kickedobj && kickedobj->where == OBJ_FREE)
         dealloc_obj(kickedobj);
 
     /* remember time of death here instead of having bones, rip, and
        topten figure it out separately and possibly getting different
        time or even day if player is slow responding to --More-- */
     endtime = getnow();
     urealtime.realtime += (long) (endtime - urealtime.restored);
 
     /* 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 %s.", currency(200L));
 
     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);
     sethanguphandler(done_hangup);
 #endif
 #endif /* NO_SIGNAL */
 
     bones_ok = (how < GENOCIDED) && can_make_bones();
 
     if (bones_ok && launch_in_progress())
         force_launch_placement();
 
     /* maintain ugrave_arise even for !bones_ok */
     if (how == PANICKED)
         u.ugrave_arise = (NON_PM - 3); /* no corpse, no grave */
     else if (how == BURNING || how == DISSOLVED) /* corpse burns up too */
         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 (how == TURNED_SLIME)
         u.ugrave_arise = PM_GREEN_SLIME;
 
     /* if pets will contribute to score, populate mydogs list now
        (bones creation isn't a factor, but pline() messaging is) */
     if (how == ESCAPED || how == ASCENDED)
         keepdogs(TRUE);
 
     if (how == QUIT) {
         killer.format = NO_KILLER_PREFIX;
         if (u.uhp < 1) {
             how = DIED;
             u.umortality++; /* skipped above when how==QUIT */
             Strcpy(killer.name, "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 == ESCAPED) ? -1 : (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();
 
     /* grave creation should be after disclosure so it doesn't have
        this grave in the current level's features for #overview */
     if (bones_ok && u.ugrave_arise == NON_PM
         && !(mvitals[u.umonnum].mvflags & G_NOCORPSE)) {
         int mnum = u.umonnum;
 
         if (!Upolyd) {
             /* Base corpse on race when not poly'd since original
              * u.umonnum is based on role, and all role monsters
              * are human.
              */
             mnum = (flags.female && urace.femalenum != NON_PM)
                        ? urace.femalenum
                        : urace.malenum;
         }
         corpse = mk_named_object(CORPSE, &mons[mnum], u.ux, u.uy, plname);
         Sprintf(pbuf, "%s, ", plname);
         formatkiller(eos(pbuf), sizeof pbuf - strlen(pbuf), how);
         make_grave(u.ux, u.uy, pbuf);
     }
     pbuf[0] = '\0'; /* clear grave text; also lint suppression */
 
     /* calculate score, before creating bones [container gold] */
     {
         int deepest = deepest_lev_reached(FALSE);
 
         umoney = money_cnt(invent);
         tmp = u.umoney0;
         umoney += hidden_gold(); /* accumulate gold from containers */
         tmp = umoney - tmp;      /* net gain */
 
         if (tmp < 0L)
             tmp = 0L;
         if (how < PANICKED)
             tmp -= tmp / 10L;
         tmp += 50L * (long) (deepest - 1);
         if (deepest > 20)
             tmp += 1000L * (long) ((deepest > 30) ? 10 : deepest - 20);
         nowrap_add(u.urexp, tmp);
 
         /* ascension gives a score bonus iff offering to original deity */
         if (how == ASCENDED && u.ualign.type == u.ualignbase[A_ORIGINAL]) {
             /* retaining original alignment: score *= 2;
                converting, then using helm-of-OA to switch back: *= 1.5 */
             tmp = (u.ualignbase[A_CURRENT] == u.ualignbase[A_ORIGINAL])
                       ? u.urexp
                       : (u.urexp / 2L);
             nowrap_add(u.urexp, tmp);
         }
     }
 
     if (u.ugrave_arise >= LOW_PM && u.ugrave_arise != PM_GREEN_SLIME) {
         /* give this feedback even if bones aren't going to be created,
            so that its presence or absence doesn't tip off the player to
            new bones or their lack; it might be a lie if makemon fails */
         Your("body rises from the dead as %s...",
              an(mons[u.ugrave_arise].mname));
         display_nhwindow(WIN_MESSAGE, FALSE);
     }
 
     if (bones_ok) {
         if (!wizard || paranoid_query(ParanoidBones, "Save bones?"))
             savebones(how, endtime, 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...) */
     done_money = umoney;
 
     /* clean up unneeded windows */
     if (have_windows) {
         wait_synch();
         free_pickinv_cache(); /* extra persistent window if perm_invent */
         if (WIN_INVEN != WIN_ERR)
             destroy_nhwindow(WIN_INVEN),  WIN_INVEN = WIN_ERR;
         display_nhwindow(WIN_MESSAGE, TRUE);
         destroy_nhwindow(WIN_MAP),  WIN_MAP = WIN_ERR;
 #ifndef STATUS_VIA_WINDOWPORT
         destroy_nhwindow(WIN_STATUS),  WIN_STATUS = WIN_ERR;
 #endif
         destroy_nhwindow(WIN_MESSAGE),  WIN_MESSAGE = WIN_ERR;
 
         if (!done_stopprint || flags.tombstone)
             endwin = create_nhwindow(NHW_TEXT);
 
         if (how < GENOCIDED && flags.tombstone && endwin != WIN_ERR)
             outrip(endwin, how, endtime);
     } else
         done_stopprint = 1; /* just avoid any more output */
 
     if (u.uhave.amulet) {
         Strcat(killer.name, " (with the Amulet)");
     } else if (how == ESCAPED) {
         if (Is_astralevel(&u.uz)) /* offered Amulet to wrong deity */
             Strcat(killer.name, " (in celestial disgrace)");
         else if (carrying(FAKE_AMULET_OF_YENDOR))
             Strcat(killer.name, " (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) {
         struct monst *mtmp;
         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) {
                     tmp = val->list[i].count
                           * (long) objects[val->list[i].typ].oc_cost;
                     nowrap_add(u.urexp, tmp);
                 }
 
         /* count the points for artifacts */
         artifact_score(invent, TRUE, endwin);
 
         viz_array[0][0] |= IN_SIGHT; /* need visibility for naming */
         mtmp = mydogs;
         if (!done_stopprint)
             Strcpy(pbuf, "You");
         if (!Schroedingers_cat) /* check here in case disclosure was off */
             Schroedingers_cat = odds_and_ends(invent, CAT_CHECK);
         if (Schroedingers_cat) {
             int mhp, m_lev = adj_lev(&mons[PM_HOUSECAT]);
             mhp = d(m_lev, 8);
             nowrap_add(u.urexp, mhp);
             if (!done_stopprint)
                 Strcat(eos(pbuf), " and Schroedinger's cat");
         }
         if (mtmp) {
             while (mtmp) {
                 if (!done_stopprint)
                     Sprintf(eos(pbuf), " and %s", mon_nam(mtmp));
                 if (mtmp->mtame)
                     nowrap_add(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)
             artifact_score(invent, FALSE, endwin); /* list artifacts */
 
         /* 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) */
                     if (has_oname(otmp))
                         free_oname(otmp);
                     otmp->quan = count;
                     Sprintf(pbuf, "%8ld %s (worth %ld %s),", count,
                             xname(otmp), count * (long) objects[typ].oc_cost,
                             currency(2L));
                     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 (have_windows && !iflags.toptenwin)
         exit_nhwindows((char *) 0), have_windows = FALSE;
     topten(how, endtime);
     if (have_windows)
         exit_nhwindows((char *) 0);
 
     if (done_stopprint) {
         raw_print("");
         raw_print("");
     }
     terminate(EXIT_SUCCESS);
 }
 

container_contents

 void
 container_contents(list, identified, all_containers, reportempty)
 struct obj *list;
 boolean identified, all_containers, reportempty;
 {
     register struct obj *box, *obj;
     struct obj **oarray;
     int i, n;
     char *invlet;
     char buf[BUFSZ];
     boolean cat, deadcat;
 
     for (box = list; box; box = box->nobj) {
         if (Is_container(box) || box->otyp == STATUE) {
             box->cknown = 1; /* we're looking at the contents now */
             if (identified)
                 box->lknown = 1;
             cat = deadcat = FALSE;
             if (SchroedingersBox(box) && !Schroedingers_cat) {
                 /* Schroedinger's Cat? */
                 cat = odds_and_ends(box, CAT_CHECK);
                 if (cat)
                     Schroedingers_cat = TRUE;
                 else
                     deadcat = TRUE;
                 box->spe = 0;
             }
             if (box->otyp == BAG_OF_TRICKS) {
                 continue; /* wrong type of container */
             } else if (box->cobj) {
                 winid tmpwin = create_nhwindow(NHW_MENU);
 
                 /* count the number of items */
                 for (n = 0, obj = box->cobj; obj; obj = obj->nobj)
                     n++;
                 /* Make a temporary array to store the objects sorted */
                 oarray = objarr_init(n);
 
                 /* Add objects to the array */
                 i = 0;
                 invlet = flags.inv_order;
             nextclass:
                 for (obj = box->cobj; obj; obj = obj->nobj) {
                     if (!flags.sortpack || obj->oclass == *invlet) {
                         objarr_set(
                             obj, i++, oarray,
                             (flags.sortloot == 'f' || flags.sortloot == 'l'));
                     }
                 } /* for loop */
                 if (flags.sortpack) {
                     if (*++invlet)
                         goto nextclass;
                 }
 
                 Sprintf(buf, "Contents of %s:", the(xname(box)));
                 putstr(tmpwin, 0, buf);
                 putstr(tmpwin, 0, "");
                 for (i = 0; i < n; i++) {
                     obj = oarray[i];
                     if (identified) {
                         makeknown(obj->otyp);
                         obj->known = obj->bknown = obj->dknown = obj->rknown =
                             1;
                         if (Is_container(obj) || obj->otyp == STATUE)
                             obj->cknown = obj->lknown = 1;
                     }
                     putstr(tmpwin, 0, doname(obj));
                 }
                 free(oarray);
                 if (cat)
                     putstr(tmpwin, 0, "Schroedinger's cat");
                 else if (deadcat)
                     putstr(tmpwin, 0, "Schroedinger's dead cat");
                 display_nhwindow(tmpwin, TRUE);
                 destroy_nhwindow(tmpwin);
                 if (all_containers)
                     container_contents(box->cobj, identified, TRUE,
                                        reportempty);
             } else if (cat || deadcat) {
                 pline("%s Schroedinger's %scat!", Tobjnam(box, "contain"),
                       deadcat ? "dead " : "");
                 display_nhwindow(WIN_MESSAGE, FALSE);
             } else if (reportempty) {
                 pline("%s is empty.", upstart(thesimpleoname(box)));
                 display_nhwindow(WIN_MESSAGE, FALSE);
             }
         }
         if (!all_containers)
             break;
     }
 }
 

terminate

 /* should be called with either EXIT_SUCCESS or EXIT_FAILURE */
 void
 terminate(status)
 int status;
 {
     program_state.in_moveloop = 0; /* won't be returning to normal play */
 #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();
     }
 
 #ifdef VMS
     /*
      *  This is liable to draw a warning if compiled with gcc, but it's
      *  more important to flag panic() -> really_done() -> terminate()
      *  as __noreturn__ then to avoid the warning.
      */
     /* don't call exit() if already executing within an exit handler;
        that would cancel any other pending user-mode handlers */
     if (program_state.exiting)
         return;
 #endif
     program_state.exiting = 1;
     nethack_exit(status);
 }
 

dovanquished

 /* #vanquished command */
 int
 dovanquished()
 {
     list_vanquished('a', FALSE);
     return 0;
 }
 

list_vanquished

 STATIC_OVL void
 list_vanquished(defquery, ask)
 char defquery;
 boolean ask;
 {
     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 = ask ? yn_function(
                             "Do you want an account of creatures vanquished?",
                               ynqchars, defquery)
                 : defquery;
         if (c == 'q')
             done_stopprint++;
         if (c == 'y' || c == 'a') {
             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 times)", 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);
         }
     } else if (defquery == 'a') {
         /* #dovanquished rather than final disclosure, so pline() is ok */
         pline("No monsters have been vanquished.");
     }
 }
 

num_genocides

 /* 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;
 }
 

num_extinct

 int
 num_extinct()
 {
     int i, n = 0;
 
     for (i = LOW_PM; i < NUMMONS; ++i)
         if (!(mvitals[i].mvflags & G_GENOD) && (mvitals[i].mvflags & G_GONE)
             && !(mons[i].geno & G_UNIQ))
             ++n;
 
     return n;
 }
 

list_genocided

 STATIC_OVL void
 list_genocided(defquery, ask)
 char defquery;
 boolean ask;
 {
     register int i;
     int ngenocided, nextinct;
     char c;
     winid klwin;
     char buf[BUFSZ];
 
     ngenocided = num_genocides();
     nextinct = num_extinct();
 
     /* genocided or extinct species list */
     if (ngenocided != 0 || nextinct != 0) {
         Sprintf(buf, "Do you want a list of %sspecies%s%s?",
                 (nextinct && !ngenocided) ? "extinct " : "",
                 (ngenocided) ? " genocided" : "",
                 (nextinct && ngenocided) ? " and extinct" : "");
         c = ask ? yn_function(buf, ynqchars, defquery) : defquery;
         if (c == 'q')
             done_stopprint++;
         if (c == 'y') {
             klwin = create_nhwindow(NHW_MENU);
             Sprintf(buf, "%s%s species:",
                     (ngenocided) ? "Genocided" : "Extinct",
                     (nextinct && ngenocided) ? " or extinct" : "");
             putstr(klwin, 0, buf);
             putstr(klwin, 0, "");
 
             for (i = LOW_PM; i < NUMMONS; i++)
                 if (mvitals[i].mvflags & G_GONE && !(mons[i].geno & G_UNIQ)) {
                     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));
                     if (!(mvitals[i].mvflags & G_GENOD))
                         Strcat(buf, " (extinct)");
                     putstr(klwin, 0, buf);
                 }
 
             putstr(klwin, 0, "");
             if (ngenocided > 0) {
                 Sprintf(buf, "%d species genocided.", ngenocided);
                 putstr(klwin, 0, buf);
             }
             if (nextinct > 0) {
                 Sprintf(buf, "%d species extinct.", nextinct);
                 putstr(klwin, 0, buf);
             }
 
             display_nhwindow(klwin, TRUE);
             destroy_nhwindow(klwin);
         }
     }
 }
 

delayed_killer

 /* set a delayed killer, ensure non-delayed killer is cleared out */
 void
 delayed_killer(id, format, killername)
 int id;
 int format;
 const char *killername;
 {
     struct kinfo *k = find_delayed_killer(id);
 
     if (k == (struct kinfo *) 0) {
         /* no match, add a new delayed killer to the list */
         k = (struct kinfo *) alloc(sizeof(struct kinfo));
         k->id = id;
         k->next = killer.next;
         killer.next = k;
     }
 
     k->format = format;
     Strcpy(k->name, killername ? killername : "");
     killer.name[0] = 0;
 }
 

find_delayed_killer

 struct kinfo *
 find_delayed_killer(id)
 int id;
 {
     struct kinfo *k;
 
     for (k = killer.next; k != (struct kinfo *) 0; k = k->next) {
         if (k->id == id)
             break;
     }
     return k;
 }
 

dealloc_killer

 void
 dealloc_killer(kptr)
 struct kinfo *kptr;
 {
     struct kinfo *prev = &killer, *k;
 
     if (kptr == (struct kinfo *) 0)
         return;
     for (k = killer.next; k != (struct kinfo *) 0; k = k->next) {
         if (k == kptr)
             break;
         prev = k;
     }
 
     if (k == (struct kinfo *) 0) {
         impossible("dealloc_killer not on list");
     } else {
         prev->next = k->next;
         free((genericptr_t) k);
     }
 }
 

save_killers

 void
 save_killers(fd, mode)
 int fd;
 int mode;
 {
     struct kinfo *kptr;
 
     if (perform_bwrite(mode)) {
         for (kptr = &killer; kptr != (struct kinfo *) 0; kptr = kptr->next) {
             bwrite(fd, (genericptr_t) kptr, sizeof(struct kinfo));
         }
     }
     if (release_data(mode)) {
         while (killer.next) {
             kptr = killer.next->next;
             free((genericptr_t) killer.next);
             killer.next = kptr;
         }
     }
 }
 

restore_killers

 void
 restore_killers(fd)
 int fd;
 {
     struct kinfo *kptr;
 
     for (kptr = &killer; kptr != (struct kinfo *) 0; kptr = kptr->next) {
         mread(fd, (genericptr_t) kptr, sizeof(struct kinfo));
         if (kptr->next) {
             kptr->next = (struct kinfo *) alloc(sizeof(struct kinfo));
         }
     }
 }
 

wordcount

 static int
 wordcount(p)
 char *p;
 {
     int words = 0;
 
     while (*p) {
         while (*p && isspace((uchar) *p))
             p++;
         if (*p)
             words++;
         while (*p && !isspace((uchar) *p))
             p++;
     }
     return words;
 }
 

bel_copy1

 static void
 bel_copy1(inp, out)
 char **inp, *out;
 {
     char *in = *inp;
 
     out += strlen(out); /* eos() */
     while (*in && isspace((uchar) *in))
         in++;
     while (*in && !isspace((uchar) *in))
         *out++ = *in++;
     *out = '\0';
     *inp = in;
 }
 

build_english_list

 char *
 build_english_list(in)
 char *in;
 {
     char *out, *p = in;
     int len = (int) strlen(p), words = wordcount(p);
 
     /* +3: " or " - " "; +(words - 1): (N-1)*(", " - " ") */
     if (words > 1)
         len += 3 + (words - 1);
     out = (char *) alloc(len + 1);
     *out = '\0'; /* bel_copy1() appends */
 
     switch (words) {
     case 0:
         impossible("no words in list");
         break;
     case 1:
         /* "single" */
         bel_copy1(&p, out);
         break;
     default:
         if (words == 2) {
             /* "first or second" */
             bel_copy1(&p, out);
             Strcat(out, " ");
         } else {
             /* "first, second, or third */
             do {
                 bel_copy1(&p, out);
                 Strcat(out, ", ");
             } while (--words > 1);
         }
         Strcat(out, "or ");
         bel_copy1(&p, out);
         break;
     }
     return out;
 }
 
 /*end.c*/