Source:NetHack 3.6.1/src/end.c

From NetHackWiki
(Redirected from Source:Ref/sort valuables)
Jump to: navigation, search

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

Top of file

  1.  /* NetHack 3.6	end.c	$NHDT-Date: 1512803167 2017/12/09 07:06:07 $  $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.137 $ */
  2.  /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
  3.  /*-Copyright (c) Robert Patrick Rankin, 2012. */
  4.  /* NetHack may be freely redistributed.  See license for details. */

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.

  1.  
  2.  #define NEED_VARARGS /* comment line for pre-compiled headers */
  3.  
  4.  #include "hack.h"
  5.  #include "lev.h"
  6.  #ifndef NO_SIGNAL
  7.  #include <signal.h>
  8.  #endif
  9.  #include <ctype.h>
  10.  #include <limits.h>
  11.  #include "dlb.h"
  12.  
  13.  /* add b to long a, convert wraparound to max value */
  14.  #define nowrap_add(a, b) (a = ((a + b) < 0 ? LONG_MAX : (a + b)))
  15.  
  16.  /* these probably ought to be generated by makedefs, like LAST_GEM */
  17.  #define FIRST_GEM DILITHIUM_CRYSTAL
  18.  #define FIRST_AMULET AMULET_OF_ESP
  19.  #define LAST_AMULET AMULET_OF_YENDOR
  20.  
  21.  struct valuable_data {
  22.      long count;
  23.      int typ;
  24.  };
  25.  
  26.  static struct valuable_data
  27.      gems[LAST_GEM + 1 - FIRST_GEM + 1], /* 1 extra for glass */
  28.      amulets[LAST_AMULET + 1 - FIRST_AMULET];
  29.  
  30.  static struct val_list {
  31.      struct valuable_data *list;
  32.      int size;
  33.  } valuables[] = { { gems, sizeof gems / sizeof *gems },
  34.                    { amulets, sizeof amulets / sizeof *amulets },
  35.                    { 0, 0 } };
  36.  
  37.  #ifndef NO_SIGNAL
  38.  STATIC_PTR void FDECL(done_intr, (int));
  39.  #if defined(UNIX) || defined(VMS) || defined(__EMX__)
  40.  static void FDECL(done_hangup, (int));
  41.  #endif
  42.  #endif
  43.  STATIC_DCL void FDECL(disclose, (int, BOOLEAN_P));
  44.  STATIC_DCL void FDECL(get_valuables, (struct obj *));
  45.  STATIC_DCL void FDECL(sort_valuables, (struct valuable_data *, int));
  46.  STATIC_DCL void FDECL(artifact_score, (struct obj *, BOOLEAN_P, winid));
  47.  STATIC_DCL void FDECL(really_done, (int)) NORETURN;
  48.  STATIC_DCL boolean FDECL(odds_and_ends, (struct obj *, int));
  49.  STATIC_DCL void FDECL(savelife, (int));
  50.  STATIC_PTR int FDECL(CFDECLSPEC vanqsort_cmp, (const genericptr,
  51.                                                 const genericptr));
  52.  STATIC_DCL int NDECL(set_vanq_order);
  53.  STATIC_DCL void FDECL(list_vanquished, (CHAR_P, BOOLEAN_P));
  54.  STATIC_DCL void FDECL(list_genocided, (CHAR_P, BOOLEAN_P));
  55.  STATIC_DCL boolean FDECL(should_query_disclose_option, (int, char *));
  56.  #ifdef DUMPLOG
  57.  STATIC_DCL void NDECL(dump_plines);
  58.  #endif
  59.  STATIC_DCL void FDECL(dump_everything, (int, time_t));
  60.  STATIC_DCL int NDECL(num_extinct);
  61.  
  62.  #if defined(__BEOS__) || defined(MICRO) || defined(WIN32) || defined(OS2)
  63.  extern void FDECL(nethack_exit, (int));
  64.  #else
  65.  #define nethack_exit exit
  66.  #endif
  67.  
  68.  #define done_stopprint program_state.stopprint
  69.  
  70.  #ifndef PANICTRACE
  71.  #define NH_abort NH_abort_
  72.  #endif
  73.  
  74.  #ifdef AMIGA
  75.  #define NH_abort_() Abort(0)
  76.  #else
  77.  #ifdef SYSV
  78.  #define NH_abort_() (void) abort()
  79.  #else
  80.  #ifdef WIN32
  81.  #define NH_abort_() win32_abort()
  82.  #else
  83.  #define NH_abort_() abort()
  84.  #endif
  85.  #endif /* !SYSV */
  86.  #endif /* !AMIGA */
  87.  
  88.  #ifdef PANICTRACE
  89.  #include <errno.h>
  90.  #ifdef PANICTRACE_LIBC
  91.  #include <execinfo.h>
  92.  #endif
  93.  
  94.  /* What do we try and in what order?  Tradeoffs:
  95.   * libc: +no external programs required
  96.   *        -requires newish libc/glibc
  97.   *        -requires -rdynamic
  98.   * gdb:   +gives more detailed information
  99.   *        +works on more OS versions
  100.   *        -requires -g, which may preclude -O on some compilers
  101.   */
  102.  #ifdef SYSCF
  103.  #define SYSOPT_PANICTRACE_GDB sysopt.panictrace_gdb
  104.  #ifdef PANICTRACE_LIBC
  105.  #define SYSOPT_PANICTRACE_LIBC sysopt.panictrace_libc
  106.  #else
  107.  #define SYSOPT_PANICTRACE_LIBC 0
  108.  #endif
  109.  #else
  110.  #define SYSOPT_PANICTRACE_GDB (nh_getenv("NETHACK_USE_GDB") == 0 ? 0 : 2)
  111.  #ifdef PANICTRACE_LIBC
  112.  #define SYSOPT_PANICTRACE_LIBC 1
  113.  #else
  114.  #define SYSOPT_PANICTRACE_LIBC 0
  115.  #endif
  116.  #endif
  117.  
  118.  static void NDECL(NH_abort);
  119.  #ifndef NO_SIGNAL
  120.  static void FDECL(panictrace_handler, (int));
  121.  #endif
  122.  static boolean NDECL(NH_panictrace_libc);
  123.  static boolean NDECL(NH_panictrace_gdb);
  124.  
  125.  #ifndef NO_SIGNAL
  126.  /*ARGSUSED*/
  127.  void panictrace_handler(
  128.      sig_unused) /* called as signal() handler, so sent at least one arg */
  129.  int sig_unused UNUSED;
  130.  {
  131.  #define SIG_MSG "\nSignal received.\n"
  132.      (void) write(2, SIG_MSG, sizeof(SIG_MSG) - 1);
  133.      NH_abort();
  134.  }
  135.  
  136.  void
  137.  panictrace_setsignals(set)
  138.  boolean set;
  139.  {
  140.  #define SETSIGNAL(sig) \
  141.      (void) signal(sig, set ? (SIG_RET_TYPE) panictrace_handler : SIG_DFL);
  142.  #ifdef SIGILL
  143.      SETSIGNAL(SIGILL);
  144.  #endif
  145.  #ifdef SIGTRAP
  146.      SETSIGNAL(SIGTRAP);
  147.  #endif
  148.  #ifdef SIGIOT
  149.      SETSIGNAL(SIGIOT);
  150.  #endif
  151.  #ifdef SIGBUS
  152.      SETSIGNAL(SIGBUS);
  153.  #endif
  154.  #ifdef SIGFPE
  155.      SETSIGNAL(SIGFPE);
  156.  #endif
  157.  #ifdef SIGSEGV
  158.      SETSIGNAL(SIGSEGV);
  159.  #endif
  160.  #ifdef SIGSTKFLT
  161.      SETSIGNAL(SIGSTKFLT);
  162.  #endif
  163.  #ifdef SIGSYS
  164.      SETSIGNAL(SIGSYS);
  165.  #endif
  166.  #ifdef SIGEMT
  167.      SETSIGNAL(SIGEMT);
  168.  #endif
  169.  #undef SETSIGNAL
  170.  }
  171.  #endif /* NO_SIGNAL */
  172.  

NH_abort

  1.  static void
  2.  NH_abort()
  3.  {
  4.      int gdb_prio = SYSOPT_PANICTRACE_GDB;
  5.      int libc_prio = SYSOPT_PANICTRACE_LIBC;
  6.      static boolean aborting = FALSE;
  7.  
  8.      if (aborting)
  9.          return;
  10.      aborting = TRUE;
  11.  
  12.  #ifndef VMS
  13.      if (gdb_prio == libc_prio && gdb_prio > 0)
  14.          gdb_prio++;
  15.  
  16.      if (gdb_prio > libc_prio) {
  17.          (void) (NH_panictrace_gdb() || (libc_prio && NH_panictrace_libc()));
  18.      } else {
  19.          (void) (NH_panictrace_libc() || (gdb_prio && NH_panictrace_gdb()));
  20.      }
  21.  
  22.  #else /* VMS */
  23.      /* overload otherwise unused priority for debug mode: 1 = show
  24.         traceback and exit; 2 = show traceback and stay in debugger */
  25.      /* if (wizard && gdb_prio == 1) gdb_prio = 2; */
  26.      vms_traceback(gdb_prio);
  27.      (void) libc_prio; /* half-hearted attempt at lint suppression */
  28.  
  29.  #endif /* ?VMS */
  30.  
  31.  #ifndef NO_SIGNAL
  32.      panictrace_setsignals(FALSE);
  33.  #endif
  34.      NH_abort_();
  35.  }
  36.  

NH_panictrace_libc

  1.  static boolean
  2.  NH_panictrace_libc()
  3.  {
  4.  #ifdef PANICTRACE_LIBC
  5.      void *bt[20];
  6.      size_t count, x;
  7.      char **info;
  8.  
  9.      raw_print("Generating more information you may report:\n");
  10.      count = backtrace(bt, SIZE(bt));
  11.      info = backtrace_symbols(bt, count);
  12.      for (x = 0; x < count; x++) {
  13.          raw_printf("[%lu] %s", (unsigned long) x, info[x]);
  14.      }
  15.      /* free(info);   -- Don't risk it. */
  16.      return TRUE;
  17.  #else
  18.      return FALSE;
  19.  #endif /* !PANICTRACE_LIBC */
  20.  }
  21.  
  22.  /*
  23.   *   fooPATH  file system path for foo
  24.   *   fooVAR   (possibly const) variable containing fooPATH
  25.   */
  26.  #ifdef PANICTRACE_GDB
  27.  #ifdef SYSCF
  28.  #define GDBVAR sysopt.gdbpath
  29.  #define GREPVAR sysopt.greppath
  30.  #else /* SYSCF */
  31.  #define GDBVAR GDBPATH
  32.  #define GREPVAR GREPPATH
  33.  #endif /* SYSCF */
  34.  #endif /* PANICTRACE_GDB */
  35.  

NH_panictrace_gdb

  1.  static boolean
  2.  NH_panictrace_gdb()
  3.  {
  4.  #ifdef PANICTRACE_GDB
  5.      /* A (more) generic method to get a stack trace - invoke
  6.       * gdb on ourself. */
  7.      char *gdbpath = GDBVAR;
  8.      char *greppath = GREPVAR;
  9.      char buf[BUFSZ];
  10.      FILE *gdb;
  11.  
  12.      if (gdbpath == NULL || gdbpath[0] == 0)
  13.          return FALSE;
  14.      if (greppath == NULL || greppath[0] == 0)
  15.          return FALSE;
  16.  
  17.      sprintf(buf, "%s -n -q %s %d 2>&1 | %s '^#'", gdbpath, ARGV0, getpid(),
  18.              greppath);
  19.      gdb = popen(buf, "w");
  20.      if (gdb) {
  21.          raw_print("Generating more information you may report:\n");
  22.          fprintf(gdb, "bt\nquit\ny");
  23.          fflush(gdb);
  24.          sleep(4); /* ugly */
  25.          pclose(gdb);
  26.          return TRUE;
  27.      } else {
  28.          return FALSE;
  29.      }
  30.  #else
  31.      return FALSE;
  32.  #endif /* !PANICTRACE_GDB */
  33.  }
  34.  #endif /* PANICTRACE */
  35.  
  36.  /*
  37.   * The order of these needs to match the macros in hack.h.
  38.   */
  39.  static NEARDATA const char *deaths[] = {
  40.      /* the array of death */
  41.      "died", "choked", "poisoned", "starvation", "drowning", "burning",
  42.      "dissolving under the heat and pressure", "crushed", "turned to stone",
  43.      "turned into slime", "genocided", "panic", "trickery", "quit",
  44.      "escaped", "ascended"
  45.  };
  46.  
  47.  static NEARDATA const char *ends[] = {
  48.      /* "when you %s" */
  49.      "died", "choked", "were poisoned",
  50.      "starved", "drowned", "burned",
  51.      "dissolved in the lava",
  52.      "were crushed", "turned to stone",
  53.      "turned into slime", "were genocided",
  54.      "panicked", "were tricked", "quit",
  55.      "escaped", "ascended"
  56.  };
  57.  
  58.  static boolean Schroedingers_cat = FALSE;
  59.  
  60.  /*ARGSUSED*/

done1

  1.  void
  2.  done1(sig_unused) /* called as signal() handler, so sent at least one arg */
  3.  int sig_unused UNUSED;
  4.  {
  5.  #ifndef NO_SIGNAL
  6.      (void) signal(SIGINT, SIG_IGN);
  7.  #endif
  8.      if (flags.ignintr) {
  9.  #ifndef NO_SIGNAL
  10.          (void) signal(SIGINT, (SIG_RET_TYPE) done1);
  11.  #endif
  12.          clear_nhwindow(WIN_MESSAGE);
  13.          curs_on_u();
  14.          wait_synch();
  15.          if (multi > 0)
  16.              nomul(0);
  17.      } else {
  18.          (void) done2();
  19.      }
  20.  }
  21.  

done2

  1.  /* "#quit" command or keyboard interrupt */
  2.  int
  3.  done2()
  4.  {
  5.      if (!paranoid_query(ParanoidQuit, "Really quit?")) {
  6.  #ifndef NO_SIGNAL
  7.          (void) signal(SIGINT, (SIG_RET_TYPE) done1);
  8.  #endif
  9.          clear_nhwindow(WIN_MESSAGE);
  10.          curs_on_u();
  11.          wait_synch();
  12.          if (multi > 0)
  13.              nomul(0);
  14.          if (multi == 0) {
  15.              u.uinvulnerable = FALSE; /* avoid ctrl-C bug -dlc */
  16.              u.usleep = 0;
  17.          }
  18.          return 0;
  19.      }
  20.  #if (defined(UNIX) || defined(VMS) || defined(LATTICE))
  21.      if (wizard) {
  22.          int c;
  23.  #ifdef VMS
  24.          extern int debuggable; /* sys/vms/vmsmisc.c, vmsunix.c */
  25.  
  26.          c = !debuggable ? 'n' : ynq("Enter debugger?");
  27.  #else
  28.  #ifdef LATTICE
  29.          c = ynq("Create SnapShot?");
  30.  #else
  31.          c = ynq("Dump core?");
  32.  #endif
  33.  #endif
  34.          if (c == 'y') {
  35.  #ifndef NO_SIGNAL
  36.              (void) signal(SIGINT, (SIG_RET_TYPE) done1);
  37.  #endif
  38.              exit_nhwindows((char *) 0);
  39.              NH_abort();
  40.          } else if (c == 'q')
  41.              done_stopprint++;
  42.      }
  43.  #endif
  44.  #ifndef LINT
  45.      done(QUIT);
  46.  #endif
  47.      return 0;
  48.  }
  49.  
  50.  #ifndef NO_SIGNAL
  51.  /*ARGSUSED*/

done_intr

  1.  STATIC_PTR void
  2.  done_intr(sig_unused) /* called as signal() handler, so sent at least 1 arg */
  3.  int sig_unused UNUSED;
  4.  {
  5.      done_stopprint++;
  6.      (void) signal(SIGINT, SIG_IGN);
  7.  #if defined(UNIX) || defined(VMS)
  8.      (void) signal(SIGQUIT, SIG_IGN);
  9.  #endif
  10.      return;
  11.  }
  12.  
  13.  #if defined(UNIX) || defined(VMS) || defined(__EMX__)

done_hangup

  1.  /* signal() handler */
  2.  static void
  3.  done_hangup(sig)
  4.  int sig;
  5.  {
  6.      program_state.done_hup++;
  7.      sethanguphandler((void FDECL((*), (int) )) SIG_IGN);
  8.      done_intr(sig);
  9.      return;
  10.  }
  11.  #endif
  12.  #endif /* NO_SIGNAL */
  13.  

done_in_by

  1.  void
  2.  done_in_by(mtmp, how)
  3.  struct monst *mtmp;
  4.  int how;
  5.  {
  6.      char buf[BUFSZ];
  7.      struct permonst *mptr = mtmp->data,
  8.                      *champtr = ((mtmp->cham >= LOW_PM)
  9.                                     ? &mons[mtmp->cham]
  10.                                     : mptr);
  11.      boolean distorted = (boolean) (Hallucination && canspotmon(mtmp)),
  12.              mimicker = (mtmp->m_ap_type == M_AP_MONSTER),
  13.              imitator = (mptr != champtr || mimicker);
  14.  
  15.      You((how == STONING) ? "turn to stone..." : "die...");
  16.      mark_synch(); /* flush buffered screen output */
  17.      buf[0] = '\0';
  18.      killer.format = KILLED_BY_AN;
  19.      /* "killed by the high priest of Crom" is okay,
  20.         "killed by the high priest" alone isn't */
  21.      if ((mptr->geno & G_UNIQ) != 0 && !(imitator && !mimicker)
  22.          && !(mptr == &mons[PM_HIGH_PRIEST] && !mtmp->ispriest)) {
  23.          if (!type_is_pname(mptr))
  24.              Strcat(buf, "the ");
  25.          killer.format = KILLED_BY;
  26.      }
  27.      /* _the_ <invisible> <distorted> ghost of Dudley */
  28.      if (mptr == &mons[PM_GHOST] && has_mname(mtmp)) {
  29.          Strcat(buf, "the ");
  30.          killer.format = KILLED_BY;
  31.      }
  32.      if (mtmp->minvis)
  33.          Strcat(buf, "invisible ");
  34.      if (distorted)
  35.          Strcat(buf, "hallucinogen-distorted ");
  36.  
  37.      if (imitator) {
  38.          char shape[BUFSZ];
  39.          const char *realnm = champtr->mname, *fakenm = mptr->mname;
  40.          boolean alt = is_vampshifter(mtmp);
  41.  
  42.          if (mimicker) {
  43.              /* realnm is already correct because champtr==mptr;
  44.                 set up fake mptr for type_is_pname/the_unique_pm */
  45.              mptr = &mons[mtmp->mappearance];
  46.              fakenm = mptr->mname;
  47.          } else if (alt && strstri(realnm, "vampire")
  48.                     && !strcmp(fakenm, "vampire bat")) {
  49.              /* special case: use "vampire in bat form" in preference
  50.                 to redundant looking "vampire in vampire bat form" */
  51.              fakenm = "bat";
  52.          }
  53.          /* for the alternate format, always suppress any article;
  54.             pname and the_unique should also have s_suffix() applied,
  55.             but vampires don't take on any shapes which warrant that */
  56.          if (alt || type_is_pname(mptr)) /* no article */
  57.              Strcpy(shape, fakenm);
  58.          else if (the_unique_pm(mptr)) /* "the"; don't use the() here */
  59.              Sprintf(shape, "the %s", fakenm);
  60.          else /* "a"/"an" */
  61.              Strcpy(shape, an(fakenm));
  62.          /* omit "called" to avoid excessive verbosity */
  63.          Sprintf(eos(buf),
  64.                  alt ? "%s in %s form"
  65.                      : mimicker ? "%s disguised as %s"
  66.                                 : "%s imitating %s",
  67.                  realnm, shape);
  68.          mptr = mtmp->data; /* reset for mimicker case */
  69.      } else if (mptr == &mons[PM_GHOST]) {
  70.          Strcat(buf, "ghost");
  71.          if (has_mname(mtmp))
  72.              Sprintf(eos(buf), " of %s", MNAME(mtmp));
  73.      } else if (mtmp->isshk) {
  74.          const char *shknm = shkname(mtmp),
  75.                     *honorific = shkname_is_pname(mtmp) ? ""
  76.                                     : mtmp->female ? "Ms. " : "Mr. ";
  77.  
  78.          Sprintf(eos(buf), "%s%s, the shopkeeper", honorific, shknm);
  79.          killer.format = KILLED_BY;
  80.      } else if (mtmp->ispriest || mtmp->isminion) {
  81.          /* m_monnam() suppresses "the" prefix plus "invisible", and
  82.             it overrides the effect of Hallucination on priestname() */
  83.          Strcat(buf, m_monnam(mtmp));
  84.      } else {
  85.          Strcat(buf, mptr->mname);
  86.          if (has_mname(mtmp))
  87.              Sprintf(eos(buf), " called %s", MNAME(mtmp));
  88.      }
  89.  
  90.      Strcpy(killer.name, buf);
  91.      if (mptr->mlet == S_WRAITH)
  92.          u.ugrave_arise = PM_WRAITH;
  93.      else if (mptr->mlet == S_MUMMY && urace.mummynum != NON_PM)
  94.          u.ugrave_arise = urace.mummynum;
  95.      else if (mptr->mlet == S_VAMPIRE && Race_if(PM_HUMAN))
  96.          u.ugrave_arise = PM_VAMPIRE;
  97.      else if (mptr == &mons[PM_GHOUL])
  98.          u.ugrave_arise = PM_GHOUL;
  99.      /* this could happen if a high-end vampire kills the hero
  100.         when ordinary vampires are genocided; ditto for wraiths */
  101.      if (u.ugrave_arise >= LOW_PM
  102.          && (mvitals[u.ugrave_arise].mvflags & G_GENOD))
  103.          u.ugrave_arise = NON_PM;
  104.  
  105.      done(how);
  106.      return;
  107.  }
  108.  
  109.  /* some special cases for overriding while-helpless reason */
  110.  static const struct {
  111.      int why, unmulti;
  112.      const char *exclude, *include;
  113.  } death_fixups[] = {
  114.      /* "petrified by <foo>, while getting stoned" -- "while getting stoned"
  115.         prevented any last-second recovery, but it was not the cause of
  116.         "petrified by <foo>" */
  117.      { STONING, 1, "getting stoned", (char *) 0 },
  118.      /* "died of starvation, while fainted from lack of food" is accurate
  119.         but sounds a fairly silly (and doesn't actually appear unless you
  120.         splice together death and while-helpless from xlogfile) */
  121.      { STARVING, 0, "fainted from lack of food", "fainted" },
  122.  };
  123.  

fixup_death

  1.  /* clear away while-helpless when the cause of death caused that
  2.     helplessness (ie, "petrified by <foo> while getting stoned") */
  3.  STATIC_DCL void
  4.  fixup_death(how)
  5.  int how;
  6.  {
  7.      int i;
  8.  
  9.      if (multi_reason) {
  10.          for (i = 0; i < SIZE(death_fixups); ++i)
  11.              if (death_fixups[i].why == how
  12.                  && !strcmp(death_fixups[i].exclude, multi_reason)) {
  13.                  if (death_fixups[i].include) /* substitute alternate reason */
  14.                      multi_reason = death_fixups[i].include;
  15.                  else /* remove the helplessness reason */
  16.                      multi_reason = (char *) 0;
  17.                  if (death_fixups[i].unmulti) /* possibly hide helplessness */
  18.                      multi = 0L;
  19.                  break;
  20.              }
  21.      }
  22.  }
  23.  
  24.  #if defined(WIN32) && !defined(SYSCF)
  25.  #define NOTIFY_NETHACK_BUGS
  26.  #endif
  27.  
  28.  /*VARARGS1*/

VA_DECL

  1.  void panic
  2.  VA_DECL(const char *, str)
  3.  {
  4.      VA_START(str);
  5.      VA_INIT(str, char *);
  6.  
  7.      if (program_state.panicking++)
  8.          NH_abort(); /* avoid loops - this should never happen*/
  9.  
  10.      if (iflags.window_inited) {
  11.          raw_print("\r\nOops...");
  12.          wait_synch(); /* make sure all pending output gets flushed */
  13.          exit_nhwindows((char *) 0);
  14.          iflags.window_inited = 0; /* they're gone; force raw_print()ing */
  15.      }
  16.  
  17.      raw_print(program_state.gameover
  18.                    ? "Postgame wrapup disrupted."
  19.                    : !program_state.something_worth_saving
  20.                          ? "Program initialization has failed."
  21.                          : "Suddenly, the dungeon collapses.");
  22.  #ifndef MICRO
  23.  #if defined(NOTIFY_NETHACK_BUGS)
  24.      if (!wizard)
  25.          raw_printf("Report the following error to \"%s\" or at \"%s\".",
  26.                     DEVTEAM_EMAIL, DEVTEAM_URL);
  27.      else if (program_state.something_worth_saving)
  28.          raw_print("\nError save file being written.\n");
  29.  #else
  30.      if (!wizard) {
  31.          const char *maybe_rebuild = !program_state.something_worth_saving
  32.                                       ? "."
  33.                                       : "\nand it may be possible to rebuild.";
  34.  
  35.          if (sysopt.support)
  36.              raw_printf("To report this error, %s%s", sysopt.support,
  37.                         maybe_rebuild);
  38.          else if (sysopt.fmtd_wizard_list) /* formatted SYSCF WIZARDS */
  39.              raw_printf("To report this error, contact %s%s",
  40.                         sysopt.fmtd_wizard_list, maybe_rebuild);
  41.          else
  42.              raw_printf("Report error to \"%s\"%s", WIZARD_NAME,
  43.                         maybe_rebuild);
  44.      }
  45.  #endif
  46.      /* XXX can we move this above the prints?  Then we'd be able to
  47.       * suppress "it may be possible to rebuild" based on dosave0()
  48.       * or say it's NOT possible to rebuild. */
  49.      if (program_state.something_worth_saving) {
  50.          set_error_savefile();
  51.          if (dosave0()) {
  52.              /* os/win port specific recover instructions */
  53.              if (sysopt.recover)
  54.                  raw_printf("%s", sysopt.recover);
  55.          }
  56.      }
  57.  #endif
  58.      {
  59.          char buf[BUFSZ];
  60.  
  61.          Vsprintf(buf, str, VA_ARGS);
  62.          raw_print(buf);
  63.          paniclog("panic", buf);
  64.      }
  65.  #ifdef WIN32
  66.      interject(INTERJECT_PANIC);
  67.  #endif
  68.  #if defined(UNIX) || defined(VMS) || defined(LATTICE) || defined(WIN32)
  69.      if (wizard)
  70.          NH_abort(); /* generate core dump */
  71.  #endif
  72.      VA_END();
  73.      really_done(PANICKED);
  74.  }
  75.  

should_query_disclose_option

  1.  STATIC_OVL boolean
  2.  should_query_disclose_option(category, defquery)
  3.  int category;
  4.  char *defquery;
  5.  {
  6.      int idx;
  7.      char disclose, *dop;
  8.  
  9.      *defquery = 'n';
  10.      if ((dop = index(disclosure_options, category)) != 0) {
  11.          idx = (int) (dop - disclosure_options);
  12.          if (idx < 0 || idx >= NUM_DISCLOSURE_OPTIONS) {
  13.              impossible(
  14.                     "should_query_disclose_option: bad disclosure index %d %c",
  15.                         idx, category);
  16.              *defquery = DISCLOSE_PROMPT_DEFAULT_YES;
  17.              return TRUE;
  18.          }
  19.          disclose = flags.end_disclose[idx];
  20.          if (disclose == DISCLOSE_YES_WITHOUT_PROMPT) {
  21.              *defquery = 'y';
  22.              return FALSE;
  23.          } else if (disclose == DISCLOSE_SPECIAL_WITHOUT_PROMPT) {
  24.              *defquery = 'a';
  25.              return FALSE;
  26.          } else if (disclose == DISCLOSE_NO_WITHOUT_PROMPT) {
  27.              *defquery = 'n';
  28.              return FALSE;
  29.          } else if (disclose == DISCLOSE_PROMPT_DEFAULT_YES) {
  30.              *defquery = 'y';
  31.              return TRUE;
  32.          } else if (disclose == DISCLOSE_PROMPT_DEFAULT_SPECIAL) {
  33.              *defquery = 'a';
  34.              return TRUE;
  35.          } else {
  36.              *defquery = 'n';
  37.              return TRUE;
  38.          }
  39.      }
  40.      impossible("should_query_disclose_option: bad category %c", category);
  41.      return TRUE;
  42.  }
  43.  

dump_plines

  1.  #ifdef DUMPLOG
  2.  STATIC_OVL void
  3.  dump_plines()
  4.  {
  5.      int i, j;
  6.      char buf[BUFSZ], **strp;
  7.      extern char *saved_plines[];
  8.      extern unsigned saved_pline_index;
  9.  
  10.      Strcpy(buf, " "); /* one space for indentation */
  11.      putstr(0, 0, "Latest messages:");
  12.      for (i = 0, j = (int) saved_pline_index; i < DUMPLOG_MSG_COUNT;
  13.           ++i, j = (j + 1) % DUMPLOG_MSG_COUNT) {
  14.          strp = &saved_plines[j];
  15.          if (*strp) {
  16.              copynchars(&buf[1], *strp, BUFSZ - 1 - 1);
  17.              putstr(0, 0, buf);
  18.  #ifdef FREE_ALL_MEMORY
  19.              free(*strp), *strp = 0;
  20.  #endif
  21.          }
  22.      }
  23.  }
  24.  #endif
  25.  
  26.  /*ARGSUSED*/

dump_everything

  1.  STATIC_OVL void
  2.  dump_everything(how, when)
  3.  int how;
  4.  time_t when; /* date+time at end of game */
  5.  {
  6.  #ifdef DUMPLOG
  7.      char pbuf[BUFSZ], datetimebuf[24]; /* [24]: room for 64-bit bogus value */
  8.  
  9.      dump_redirect(TRUE);
  10.      if (!iflags.in_dumplog)
  11.          return;
  12.  
  13.      init_symbols(); /* revert to default symbol set */
  14.  
  15.      /* one line version ID, which includes build date+time;
  16.         it's conceivable that the game started with a different
  17.         build date+time or even with an older nethack version,
  18.         but we only have access to the one it finished under */
  19.      putstr(0, 0, getversionstring(pbuf));
  20.      putstr(0, 0, "");
  21.  
  22.      /* game start and end date+time to disambiguate version date+time */
  23.      Strcpy(datetimebuf, yyyymmddhhmmss(ubirthday));
  24.      Sprintf(pbuf, "Game began %4.4s-%2.2s-%2.2s %2.2s:%2.2s:%2.2s",
  25.              &datetimebuf[0], &datetimebuf[4], &datetimebuf[6],
  26.              &datetimebuf[8], &datetimebuf[10], &datetimebuf[12]);
  27.      Strcpy(datetimebuf, yyyymmddhhmmss(when));
  28.      Sprintf(eos(pbuf), ", ended %4.4s-%2.2s-%2.2s %2.2s:%2.2s:%2.2s.",
  29.              &datetimebuf[0], &datetimebuf[4], &datetimebuf[6],
  30.              &datetimebuf[8], &datetimebuf[10], &datetimebuf[12]);
  31.      putstr(0, 0, pbuf);
  32.      putstr(0, 0, "");
  33.  
  34.      /* character name and basic role info */
  35.      Sprintf(pbuf, "%s, %s %s %s %s", plname,
  36.              aligns[1 - u.ualign.type].adj,
  37.              genders[flags.female].adj,
  38.              urace.adj,
  39.              (flags.female && urole.name.f) ? urole.name.f : urole.name.m);
  40.      putstr(0, 0, pbuf);
  41.      putstr(0, 0, "");
  42.  
  43.      dump_map();
  44.      putstr(0, 0, do_statusline1());
  45.      putstr(0, 0, do_statusline2());
  46.      putstr(0, 0, "");
  47.  
  48.      dump_plines();
  49.      putstr(0, 0, "");
  50.      putstr(0, 0, "Inventory:");
  51.      display_inventory((char *) 0, TRUE);
  52.      container_contents(invent, TRUE, TRUE, FALSE);
  53.      enlightenment((BASICENLIGHTENMENT | MAGICENLIGHTENMENT),
  54.                    (how >= PANICKED) ? ENL_GAMEOVERALIVE : ENL_GAMEOVERDEAD);
  55.      putstr(0, 0, "");
  56.      list_vanquished('d', FALSE); /* 'd' => 'y' */
  57.      putstr(0, 0, "");
  58.      list_genocided('d', FALSE); /* 'd' => 'y' */
  59.      putstr(0, 0, "");
  60.      show_conduct((how >= PANICKED) ? 1 : 2);
  61.      putstr(0, 0, "");
  62.      show_overview((how >= PANICKED) ? 1 : 2, how);
  63.      putstr(0, 0, "");
  64.      dump_redirect(FALSE);
  65.  #else
  66.      nhUse(how);
  67.      nhUse(when);
  68.  #endif
  69.  }
  70.  

disclose

  1.  STATIC_OVL void
  2.  disclose(how, taken)
  3.  int how;
  4.  boolean taken;
  5.  {
  6.      char c = '\0', defquery;
  7.      char qbuf[QBUFSZ];
  8.      boolean ask = FALSE;
  9.  
  10.      if (invent && !done_stopprint) {
  11.          if (taken)
  12.              Sprintf(qbuf, "Do you want to see what you had when you %s?",
  13.                      (how == QUIT) ? "quit" : "died");
  14.          else
  15.              Strcpy(qbuf, "Do you want your possessions identified?");
  16.  
  17.          ask = should_query_disclose_option('i', &defquery);
  18.          c = ask ? yn_function(qbuf, ynqchars, defquery) : defquery;
  19.          if (c == 'y') {
  20.              /* caller has already ID'd everything */
  21.              (void) display_inventory((char *) 0, TRUE);
  22.              container_contents(invent, TRUE, TRUE, FALSE);
  23.          }
  24.          if (c == 'q')
  25.              done_stopprint++;
  26.      }
  27.  
  28.      if (!done_stopprint) {
  29.          ask = should_query_disclose_option('a', &defquery);
  30.          c = ask ? yn_function("Do you want to see your attributes?", ynqchars,
  31.                                defquery)
  32.                  : defquery;
  33.          if (c == 'y')
  34.              enlightenment((BASICENLIGHTENMENT | MAGICENLIGHTENMENT),
  35.                            (how >= PANICKED) ? ENL_GAMEOVERALIVE
  36.                                              : ENL_GAMEOVERDEAD);
  37.          if (c == 'q')
  38.              done_stopprint++;
  39.      }
  40.  
  41.      if (!done_stopprint) {
  42.          ask = should_query_disclose_option('v', &defquery);
  43.          list_vanquished(defquery, ask);
  44.      }
  45.  
  46.      if (!done_stopprint) {
  47.          ask = should_query_disclose_option('g', &defquery);
  48.          list_genocided(defquery, ask);
  49.      }
  50.  
  51.      if (!done_stopprint) {
  52.          ask = should_query_disclose_option('c', &defquery);
  53.          c = ask ? yn_function("Do you want to see your conduct?", ynqchars,
  54.                                defquery)
  55.                  : defquery;
  56.          if (c == 'y')
  57.              show_conduct((how >= PANICKED) ? 1 : 2);
  58.          if (c == 'q')
  59.              done_stopprint++;
  60.      }
  61.  
  62.      if (!done_stopprint) {
  63.          ask = should_query_disclose_option('o', &defquery);
  64.          c = ask ? yn_function("Do you want to see the dungeon overview?",
  65.                                ynqchars, defquery)
  66.                  : defquery;
  67.          if (c == 'y')
  68.              show_overview((how >= PANICKED) ? 1 : 2, how);
  69.          if (c == 'q')
  70.              done_stopprint++;
  71.      }
  72.  }
  73.  

savelife

  1.  /* try to get the player back in a viable state after being killed */
  2.  STATIC_OVL void
  3.  savelife(how)
  4.  int how;
  5.  {
  6.      int uhpmin = max(2 * u.ulevel, 10);
  7.  
  8.      if (u.uhpmax < uhpmin)
  9.          u.uhpmax = uhpmin;
  10.      u.uhp = u.uhpmax;
  11.      u.uswldtim = 0;
  12.      if (u.uhunger < 500) {
  13.          u.uhunger = 500;
  14.          newuhs(FALSE);
  15.      }
  16.      /* cure impending doom of sickness hero won't have time to fix */
  17.      if ((Sick & TIMEOUT) == 1L) {
  18.          u.usick_type = 0;
  19.          set_itimeout(&Sick, 0L);
  20.      }
  21.      if (how == CHOKING)
  22.          init_uhunger();
  23.      nomovemsg = "You survived that attempt on your life.";
  24.      context.move = 0;
  25.      if (multi > 0)
  26.          multi = 0;
  27.      else
  28.          multi = -1;
  29.      if (u.utrap && u.utraptype == TT_LAVA)
  30.          u.utrap = 0;
  31.      context.botl = 1;
  32.      u.ugrave_arise = NON_PM;
  33.      HUnchanging = 0L;
  34.      curs_on_u();
  35.      if (!context.mon_moving)
  36.          endmultishot(FALSE);
  37.  }
  38.  

get_valuables

  1.  /*
  2.   * Get valuables from the given list.  Revised code: the list always remains
  3.   * intact.
  4.   */
  5.  STATIC_OVL void
  6.  get_valuables(list)
  7.  struct obj *list; /* inventory or container contents */
  8.  {
  9.      register struct obj *obj;
  10.      register int i;
  11.  
  12.      /* find amulets and gems, ignoring all artifacts */
  13.      for (obj = list; obj; obj = obj->nobj)
  14.          if (Has_contents(obj)) {
  15.              get_valuables(obj->cobj);
  16.          } else if (obj->oartifact) {
  17.              continue;
  18.          } else if (obj->oclass == AMULET_CLASS) {
  19.              i = obj->otyp - FIRST_AMULET;
  20.              if (!amulets[i].count) {
  21.                  amulets[i].count = obj->quan;
  22.                  amulets[i].typ = obj->otyp;
  23.              } else
  24.                  amulets[i].count += obj->quan; /* always adds one */
  25.          } else if (obj->oclass == GEM_CLASS && obj->otyp < LUCKSTONE) {
  26.              i = min(obj->otyp, LAST_GEM + 1) - FIRST_GEM;
  27.              if (!gems[i].count) {
  28.                  gems[i].count = obj->quan;
  29.                  gems[i].typ = obj->otyp;
  30.              } else
  31.                  gems[i].count += obj->quan;
  32.          }
  33.      return;
  34.  }
  35.  

sort_valuables

  1.  /*
  2.   *  Sort collected valuables, most frequent to least.  We could just
  3.   *  as easily use qsort, but we don't care about efficiency here.
  4.   */
  5.  STATIC_OVL void
  6.  sort_valuables(list, size)
  7.  struct valuable_data list[];
  8.  int size; /* max value is less than 20 */
  9.  {
  10.      register int i, j;
  11.      struct valuable_data ltmp;
  12.  
  13.      /* move greater quantities to the front of the list */
  14.      for (i = 1; i < size; i++) {
  15.          if (list[i].count == 0)
  16.              continue;   /* empty slot */
  17.          ltmp = list[i]; /* structure copy */
  18.          for (j = i; j > 0; --j)
  19.              if (list[j - 1].count >= ltmp.count)
  20.                  break;
  21.              else {
  22.                  list[j] = list[j - 1];
  23.              }
  24.          list[j] = ltmp;
  25.      }
  26.      return;
  27.  }
  28.  
  29.  #define CAT_CHECK 2
  30.  

odds_and_ends

  1.  STATIC_OVL boolean
  2.  odds_and_ends(list, what)
  3.  struct obj *list;
  4.  int what;
  5.  {
  6.      struct obj *otmp;
  7.      for (otmp = list; otmp; otmp = otmp->nobj) {
  8.          switch (what) {
  9.          case CAT_CHECK: /* Schroedinger's Cat */
  10.              /* Ascending is deterministic */
  11.              if (SchroedingersBox(otmp))
  12.                  return rn2(2);
  13.              break;
  14.          }
  15.          if (Has_contents(otmp))
  16.              return odds_and_ends(otmp->cobj, what);
  17.      }
  18.      return FALSE;
  19.  }
  20.  

artifact_score

  1.  /* called twice; first to calculate total, then to list relevant items */
  2.  STATIC_OVL void
  3.  artifact_score(list, counting, endwin)
  4.  struct obj *list;
  5.  boolean counting; /* true => add up points; false => display them */
  6.  winid endwin;
  7.  {
  8.      char pbuf[BUFSZ];
  9.      struct obj *otmp;
  10.      long value, points;
  11.      short dummy; /* object type returned by artifact_name() */
  12.  
  13.      for (otmp = list; otmp; otmp = otmp->nobj) {
  14.          if (otmp->oartifact || otmp->otyp == BELL_OF_OPENING
  15.              || otmp->otyp == SPE_BOOK_OF_THE_DEAD
  16.              || otmp->otyp == CANDELABRUM_OF_INVOCATION) {
  17.              value = arti_cost(otmp); /* zorkmid value */
  18.              points = value * 5 / 2;  /* score value */
  19.              if (counting) {
  20.                  nowrap_add(u.urexp, points);
  21.              } else {
  22.                  makeknown(otmp->otyp);
  23.                  otmp->known = otmp->dknown = otmp->bknown = otmp->rknown = 1;
  24.                  /* assumes artifacts don't have quan > 1 */
  25.                  Sprintf(pbuf, "%s%s (worth %ld %s and %ld points)",
  26.                          the_unique_obj(otmp) ? "The " : "",
  27.                          otmp->oartifact ? artifact_name(xname(otmp), &dummy)
  28.                                          : OBJ_NAME(objects[otmp->otyp]),
  29.                          value, currency(value), points);
  30.                  putstr(endwin, 0, pbuf);
  31.              }
  32.          }
  33.          if (Has_contents(otmp))
  34.              artifact_score(otmp->cobj, counting, endwin);
  35.      }
  36.  }
  37.  

done

  1.  /* Be careful not to call panic from here! */
  2.  void
  3.  done(how)
  4.  int how;
  5.  {
  6.      if (how == TRICKED) {
  7.          if (killer.name[0]) {
  8.              paniclog("trickery", killer.name);
  9.              killer.name[0] = 0;
  10.          }
  11.          if (wizard) {
  12.              You("are a very tricky wizard, it seems.");
  13.              return;
  14.          }
  15.      }
  16.      if (program_state.panicking
  17.  #ifdef HANGUPHANDLING
  18.          || program_state.done_hup
  19.  #endif
  20.          ) {
  21.          /* skip status update if panicking or disconnected */
  22.          context.botl = context.botlx = FALSE;
  23.      } else {
  24.          /* otherwise force full status update */
  25.          context.botlx = TRUE;
  26.          bot();
  27.      }
  28.  
  29.      if (how == ASCENDED || (!killer.name[0] && how == GENOCIDED))
  30.          killer.format = NO_KILLER_PREFIX;
  31.      /* Avoid killed by "a" burning or "a" starvation */
  32.      if (!killer.name[0] && (how == STARVING || how == BURNING))
  33.          killer.format = KILLED_BY;
  34.      if (!killer.name[0] || how >= PANICKED)
  35.          Strcpy(killer.name, deaths[how]);
  36.  
  37.      if (how < PANICKED)
  38.          u.umortality++;
  39.      if (Lifesaved && (how <= GENOCIDED)) {
  40.          pline("But wait...");
  41.          makeknown(AMULET_OF_LIFE_SAVING);
  42.          Your("medallion %s!", !Blind ? "begins to glow" : "feels warm");
  43.          if (how == CHOKING)
  44.              You("vomit ...");
  45.          You_feel("much better!");
  46.          pline_The("medallion crumbles to dust!");
  47.          if (uamul)
  48.              useup(uamul);
  49.  
  50.          (void) adjattrib(A_CON, -1, TRUE);
  51.          savelife(how);
  52.          if (how == GENOCIDED) {
  53.              pline("Unfortunately you are still genocided...");
  54.          } else {
  55.              killer.name[0] = 0;
  56.              killer.format = 0;
  57.              return;
  58.          }
  59.      }
  60.      if ((wizard || discover) && (how <= GENOCIDED)
  61.          && !paranoid_query(ParanoidDie, "Die?")) {
  62.          pline("OK, so you don't %s.", (how == CHOKING) ? "choke" : "die");
  63.          savelife(how);
  64.          killer.name[0] = 0;
  65.          killer.format = 0;
  66.          return;
  67.      }
  68.      really_done(how);
  69.  }
  70.  

really_done

  1.  /* separated from done() in order to specify the __noreturn__ attribute */
  2.  STATIC_OVL void
  3.  really_done(how)
  4.  int how;
  5.  {
  6.      boolean taken;
  7.      char pbuf[BUFSZ];
  8.      winid endwin = WIN_ERR;
  9.      boolean bones_ok, have_windows = iflags.window_inited;
  10.      struct obj *corpse = (struct obj *) 0;
  11.      time_t endtime;
  12.      long umoney;
  13.      long tmp;
  14.  
  15.      /*
  16.       *  The game is now over...
  17.       */
  18.      program_state.gameover = 1;
  19.      /* in case of a subsequent panic(), there's no point trying to save */
  20.      program_state.something_worth_saving = 0;
  21.      /* render vision subsystem inoperative */
  22.      iflags.vision_inited = 0;
  23.  
  24.      /* might have been killed while using a disposable item, so make sure
  25.         it's gone prior to inventory disclosure and creation of bones data */
  26.      inven_inuse(TRUE);
  27.      /* maybe not on object lists; if an active light source, would cause
  28.         big trouble (`obj_is_local' panic) for savebones() -> savelev() */
  29.      if (thrownobj && thrownobj->where == OBJ_FREE)
  30.          dealloc_obj(thrownobj);
  31.      if (kickedobj && kickedobj->where == OBJ_FREE)
  32.          dealloc_obj(kickedobj);
  33.  
  34.      /* remember time of death here instead of having bones, rip, and
  35.         topten figure it out separately and possibly getting different
  36.         time or even day if player is slow responding to --More-- */
  37.      urealtime.finish_time = endtime = getnow();
  38.      urealtime.realtime += (long) (endtime - urealtime.start_timing);
  39.  
  40.      dump_open_log(endtime);
  41.      /* Sometimes you die on the first move.  Life's not fair.
  42.       * On those rare occasions you get hosed immediately, go out
  43.       * smiling... :-)  -3.
  44.       */
  45.      if (moves <= 1 && how < PANICKED) /* You die... --More-- */
  46.          pline("Do not pass go.  Do not collect 200 %s.", currency(200L));
  47.  
  48.      if (have_windows)
  49.          wait_synch(); /* flush screen output */
  50.  #ifndef NO_SIGNAL
  51.      (void) signal(SIGINT, (SIG_RET_TYPE) done_intr);
  52.  #if defined(UNIX) || defined(VMS) || defined(__EMX__)
  53.      (void) signal(SIGQUIT, (SIG_RET_TYPE) done_intr);
  54.      sethanguphandler(done_hangup);
  55.  #endif
  56.  #endif /* NO_SIGNAL */
  57.  
  58.      bones_ok = (how < GENOCIDED) && can_make_bones();
  59.  
  60.      if (bones_ok && launch_in_progress())
  61.          force_launch_placement();
  62.  
  63.      /* maintain ugrave_arise even for !bones_ok */
  64.      if (how == PANICKED)
  65.          u.ugrave_arise = (NON_PM - 3); /* no corpse, no grave */
  66.      else if (how == BURNING || how == DISSOLVED) /* corpse burns up too */
  67.          u.ugrave_arise = (NON_PM - 2); /* leave no corpse */
  68.      else if (how == STONING)
  69.          u.ugrave_arise = (NON_PM - 1); /* statue instead of corpse */
  70.      else if (how == TURNED_SLIME)
  71.          u.ugrave_arise = PM_GREEN_SLIME;
  72.  
  73.      if (how == QUIT) {
  74.          killer.format = NO_KILLER_PREFIX;
  75.          if (u.uhp < 1) {
  76.              how = DIED;
  77.              u.umortality++; /* skipped above when how==QUIT */
  78.              Strcpy(killer.name, "quit while already on Charon's boat");
  79.          }
  80.      }
  81.      if (how == ESCAPED || how == PANICKED)
  82.          killer.format = NO_KILLER_PREFIX;
  83.  
  84.      fixup_death(how); /* actually, fixup multi_reason */
  85.  
  86.      if (how != PANICKED) {
  87.          /* these affect score and/or bones, but avoid them during panic */
  88.          taken = paybill((how == ESCAPED) ? -1 : (how != QUIT));
  89.          paygd();
  90.          clearpriests();
  91.      } else
  92.          taken = FALSE; /* lint; assert( !bones_ok ); */
  93.  
  94.      clearlocks();
  95.  
  96.      if (have_windows)
  97.          display_nhwindow(WIN_MESSAGE, FALSE);
  98.  
  99.      if (how != PANICKED) {
  100.          struct obj *obj;
  101.  
  102.          /*
  103.           * This is needed for both inventory disclosure and dumplog.
  104.           * Both are optional, so do it once here instead of duplicating
  105.           * it in both of those places.
  106.           */
  107.          for (obj = invent; obj; obj = obj->nobj) {
  108.              makeknown(obj->otyp);
  109.              obj->known = obj->bknown = obj->dknown = obj->rknown = 1;
  110.              if (Is_container(obj) || obj->otyp == STATUE)
  111.                  obj->cknown = obj->lknown = 1;
  112.          }
  113.  
  114.          if (strcmp(flags.end_disclose, "none"))
  115.              disclose(how, taken);
  116.  
  117.          dump_everything(how, endtime);
  118.      }
  119.  
  120.      /* if pets will contribute to score, populate mydogs list now
  121.         (bones creation isn't a factor, but pline() messaging is; used to
  122.         be done ever sooner, but we need it to come after dump_everything()
  123.         so that any accompanying pets are still on the map during dump) */
  124.      if (how == ESCAPED || how == ASCENDED)
  125.          keepdogs(TRUE);
  126.  
  127.      /* finish_paybill should be called after disclosure but before bones */
  128.      if (bones_ok && taken)
  129.          finish_paybill();
  130.  
  131.      /* grave creation should be after disclosure so it doesn't have
  132.         this grave in the current level's features for #overview */
  133.      if (bones_ok && u.ugrave_arise == NON_PM
  134.          && !(mvitals[u.umonnum].mvflags & G_NOCORPSE)) {
  135.          int mnum = u.umonnum;
  136.  
  137.          if (!Upolyd) {
  138.              /* Base corpse on race when not poly'd since original
  139.               * u.umonnum is based on role, and all role monsters
  140.               * are human.
  141.               */
  142.              mnum = (flags.female && urace.femalenum != NON_PM)
  143.                         ? urace.femalenum
  144.                         : urace.malenum;
  145.          }
  146.          corpse = mk_named_object(CORPSE, &mons[mnum], u.ux, u.uy, plname);
  147.          Sprintf(pbuf, "%s, ", plname);
  148.          formatkiller(eos(pbuf), sizeof pbuf - strlen(pbuf), how, TRUE);
  149.          make_grave(u.ux, u.uy, pbuf);
  150.      }
  151.      pbuf[0] = '\0'; /* clear grave text; also lint suppression */
  152.  
  153.      /* calculate score, before creating bones [container gold] */
  154.      {
  155.          int deepest = deepest_lev_reached(FALSE);
  156.  
  157.          umoney = money_cnt(invent);
  158.          tmp = u.umoney0;
  159.          umoney += hidden_gold(); /* accumulate gold from containers */
  160.          tmp = umoney - tmp;      /* net gain */
  161.  
  162.          if (tmp < 0L)
  163.              tmp = 0L;
  164.          if (how < PANICKED)
  165.              tmp -= tmp / 10L;
  166.          tmp += 50L * (long) (deepest - 1);
  167.          if (deepest > 20)
  168.              tmp += 1000L * (long) ((deepest > 30) ? 10 : deepest - 20);
  169.          nowrap_add(u.urexp, tmp);
  170.  
  171.          /* ascension gives a score bonus iff offering to original deity */
  172.          if (how == ASCENDED && u.ualign.type == u.ualignbase[A_ORIGINAL]) {
  173.              /* retaining original alignment: score *= 2;
  174.                 converting, then using helm-of-OA to switch back: *= 1.5 */
  175.              tmp = (u.ualignbase[A_CURRENT] == u.ualignbase[A_ORIGINAL])
  176.                        ? u.urexp
  177.                        : (u.urexp / 2L);
  178.              nowrap_add(u.urexp, tmp);
  179.          }
  180.      }
  181.  
  182.      if (u.ugrave_arise >= LOW_PM && u.ugrave_arise != PM_GREEN_SLIME) {
  183.          /* give this feedback even if bones aren't going to be created,
  184.             so that its presence or absence doesn't tip off the player to
  185.             new bones or their lack; it might be a lie if makemon fails */
  186.          Your("body rises from the dead as %s...",
  187.               an(mons[u.ugrave_arise].mname));
  188.          display_nhwindow(WIN_MESSAGE, FALSE);
  189.      }
  190.  
  191.      if (bones_ok) {
  192.          if (!wizard || paranoid_query(ParanoidBones, "Save bones?"))
  193.              savebones(how, endtime, corpse);
  194.          /* corpse may be invalid pointer now so
  195.              ensure that it isn't used again */
  196.          corpse = (struct obj *) 0;
  197.      }
  198.  
  199.      /* update gold for the rip output, which can't use hidden_gold()
  200.         (containers will be gone by then if bones just got saved...) */
  201.      done_money = umoney;
  202.  
  203.      /* clean up unneeded windows */
  204.      if (have_windows) {
  205.          wait_synch();
  206.          free_pickinv_cache(); /* extra persistent window if perm_invent */
  207.          if (WIN_INVEN != WIN_ERR) {
  208.              destroy_nhwindow(WIN_INVEN),  WIN_INVEN = WIN_ERR;
  209.              /* precaution in case any late update_inventory() calls occur */
  210.              flags.perm_invent = 0;
  211.          }
  212.          display_nhwindow(WIN_MESSAGE, TRUE);
  213.          destroy_nhwindow(WIN_MAP),  WIN_MAP = WIN_ERR;
  214.  #ifndef STATUS_HILITES
  215.          destroy_nhwindow(WIN_STATUS),  WIN_STATUS = WIN_ERR;
  216.  #endif
  217.          destroy_nhwindow(WIN_MESSAGE),  WIN_MESSAGE = WIN_ERR;
  218.  
  219.          if (!done_stopprint || flags.tombstone)
  220.              endwin = create_nhwindow(NHW_TEXT);
  221.  
  222.          if (how < GENOCIDED && flags.tombstone && endwin != WIN_ERR)
  223.              outrip(endwin, how, endtime);
  224.      } else
  225.          done_stopprint = 1; /* just avoid any more output */
  226.  
  227.  #ifdef DUMPLOG
  228.      /* 'how' reasons beyond genocide shouldn't show tombstone;
  229.         for normal end of game, genocide doesn't either */
  230.      if (how <= GENOCIDED) {
  231.          dump_redirect(TRUE);
  232.          if (iflags.in_dumplog)
  233.              genl_outrip(0, how, endtime);
  234.          dump_redirect(FALSE);
  235.      }
  236.  #endif
  237.      if (u.uhave.amulet) {
  238.          Strcat(killer.name, " (with the Amulet)");
  239.      } else if (how == ESCAPED) {
  240.          if (Is_astralevel(&u.uz)) /* offered Amulet to wrong deity */
  241.              Strcat(killer.name, " (in celestial disgrace)");
  242.          else if (carrying(FAKE_AMULET_OF_YENDOR))
  243.              Strcat(killer.name, " (with a fake Amulet)");
  244.          /* don't bother counting to see whether it should be plural */
  245.      }
  246.  
  247.      Sprintf(pbuf, "%s %s the %s...", Goodbye(), plname,
  248.              (how != ASCENDED)
  249.                  ? (const char *) ((flags.female && urole.name.f)
  250.                      ? urole.name.f
  251.                      : urole.name.m)
  252.                  : (const char *) (flags.female ? "Demigoddess" : "Demigod"));
  253.      dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
  254.      dump_forward_putstr(endwin, 0, "", done_stopprint);
  255.  
  256.      if (how == ESCAPED || how == ASCENDED) {
  257.          struct monst *mtmp;
  258.          struct obj *otmp;
  259.          register struct val_list *val;
  260.          register int i;
  261.  
  262.          for (val = valuables; val->list; val++)
  263.              for (i = 0; i < val->size; i++) {
  264.                  val->list[i].count = 0L;
  265.              }
  266.          get_valuables(invent);
  267.  
  268.          /* add points for collected valuables */
  269.          for (val = valuables; val->list; val++)
  270.              for (i = 0; i < val->size; i++)
  271.                  if (val->list[i].count != 0L) {
  272.                      tmp = val->list[i].count
  273.                            * (long) objects[val->list[i].typ].oc_cost;
  274.                      nowrap_add(u.urexp, tmp);
  275.                  }
  276.  
  277.          /* count the points for artifacts */
  278.          artifact_score(invent, TRUE, endwin);
  279.  
  280.          viz_array[0][0] |= IN_SIGHT; /* need visibility for naming */
  281.          mtmp = mydogs;
  282.          Strcpy(pbuf, "You");
  283.          if (!Schroedingers_cat) /* check here in case disclosure was off */
  284.              Schroedingers_cat = odds_and_ends(invent, CAT_CHECK);
  285.          if (Schroedingers_cat) {
  286.              int mhp, m_lev = adj_lev(&mons[PM_HOUSECAT]);
  287.  
  288.              mhp = d(m_lev, 8);
  289.              nowrap_add(u.urexp, mhp);
  290.              Strcat(eos(pbuf), " and Schroedinger's cat");
  291.          }
  292.          if (mtmp) {
  293.              while (mtmp) {
  294.                  Sprintf(eos(pbuf), " and %s", mon_nam(mtmp));
  295.                  if (mtmp->mtame)
  296.                      nowrap_add(u.urexp, mtmp->mhp);
  297.                  mtmp = mtmp->nmon;
  298.              }
  299.              dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
  300.              pbuf[0] = '\0';
  301.          } else {
  302.              Strcat(pbuf, " ");
  303.          }
  304.          Sprintf(eos(pbuf), "%s with %ld point%s,",
  305.                  how == ASCENDED ? "went to your reward"
  306.                                   : "escaped from the dungeon",
  307.                  u.urexp, plur(u.urexp));
  308.          dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
  309.  
  310.          if (!done_stopprint)
  311.              artifact_score(invent, FALSE, endwin); /* list artifacts */
  312.  #ifdef DUMPLOG
  313.          dump_redirect(TRUE);
  314.          if (iflags.in_dumplog)
  315.              artifact_score(invent, FALSE, 0);
  316.          dump_redirect(FALSE);
  317.  #endif
  318.  
  319.          /* list valuables here */
  320.          for (val = valuables; val->list; val++) {
  321.              sort_valuables(val->list, val->size);
  322.              for (i = 0; i < val->size && !done_stopprint; i++) {
  323.                  int typ = val->list[i].typ;
  324.                  long count = val->list[i].count;
  325.  
  326.                  if (count == 0L)
  327.                      continue;
  328.                  if (objects[typ].oc_class != GEM_CLASS || typ <= LAST_GEM) {
  329.                      otmp = mksobj(typ, FALSE, FALSE);
  330.                      makeknown(otmp->otyp);
  331.                      otmp->known = 1;  /* for fake amulets */
  332.                      otmp->dknown = 1; /* seen it (blindness fix) */
  333.                      if (has_oname(otmp))
  334.                          free_oname(otmp);
  335.                      otmp->quan = count;
  336.                      Sprintf(pbuf, "%8ld %s (worth %ld %s),", count,
  337.                              xname(otmp), count * (long) objects[typ].oc_cost,
  338.                              currency(2L));
  339.                      obfree(otmp, (struct obj *) 0);
  340.                  } else {
  341.                      Sprintf(pbuf, "%8ld worthless piece%s of colored glass,",
  342.                              count, plur(count));
  343.                  }
  344.                  dump_forward_putstr(endwin, 0, pbuf, 0);
  345.              }
  346.          }
  347.  
  348.      } else {
  349.          /* did not escape or ascend */
  350.          if (u.uz.dnum == 0 && u.uz.dlevel <= 0) {
  351.              /* level teleported out of the dungeon; `how' is DIED,
  352.                 due to falling or to "arriving at heaven prematurely" */
  353.              Sprintf(pbuf, "You %s beyond the confines of the dungeon",
  354.                      (u.uz.dlevel < 0) ? "passed away" : ends[how]);
  355.          } else {
  356.              /* more conventional demise */
  357.              const char *where = dungeons[u.uz.dnum].dname;
  358.  
  359.              if (Is_astralevel(&u.uz))
  360.                  where = "The Astral Plane";
  361.              Sprintf(pbuf, "You %s in %s", ends[how], where);
  362.              if (!In_endgame(&u.uz) && !Is_knox(&u.uz))
  363.                  Sprintf(eos(pbuf), " on dungeon level %d",
  364.                          In_quest(&u.uz) ? dunlev(&u.uz) : depth(&u.uz));
  365.          }
  366.  
  367.          Sprintf(eos(pbuf), " with %ld point%s,", u.urexp, plur(u.urexp));
  368.          dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
  369.      }
  370.  
  371.      Sprintf(pbuf, "and %ld piece%s of gold, after %ld move%s.", umoney,
  372.              plur(umoney), moves, plur(moves));
  373.      dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
  374.      Sprintf(pbuf,
  375.              "You were level %d with a maximum of %d hit point%s when you %s.",
  376.              u.ulevel, u.uhpmax, plur(u.uhpmax), ends[how]);
  377.      dump_forward_putstr(endwin, 0, pbuf, done_stopprint);
  378.      dump_forward_putstr(endwin, 0, "", done_stopprint);
  379.      if (!done_stopprint)
  380.          display_nhwindow(endwin, TRUE);
  381.      if (endwin != WIN_ERR)
  382.          destroy_nhwindow(endwin);
  383.  
  384.      dump_close_log();
  385.      /* "So when I die, the first thing I will see in Heaven is a
  386.       * score list?" */
  387.      if (have_windows && !iflags.toptenwin)
  388.          exit_nhwindows((char *) 0), have_windows = FALSE;
  389.      topten(how, endtime);
  390.      if (have_windows)
  391.          exit_nhwindows((char *) 0);
  392.  
  393.      if (done_stopprint) {
  394.          raw_print("");
  395.          raw_print("");
  396.      }
  397.      nh_terminate(EXIT_SUCCESS);
  398.  }
  399.  

container_contents

  1.  void
  2.  container_contents(list, identified, all_containers, reportempty)
  3.  struct obj *list;
  4.  boolean identified, all_containers, reportempty;
  5.  {
  6.      register struct obj *box, *obj;
  7.      char buf[BUFSZ];
  8.      boolean cat, deadcat;
  9.  
  10.      for (box = list; box; box = box->nobj) {
  11.          if (Is_container(box) || box->otyp == STATUE) {
  12.              box->cknown = 1; /* we're looking at the contents now */
  13.              if (identified)
  14.                  box->lknown = 1;
  15.              cat = deadcat = FALSE;
  16.              if (SchroedingersBox(box) && !Schroedingers_cat) {
  17.                  /* Schroedinger's Cat? */
  18.                  cat = odds_and_ends(box, CAT_CHECK);
  19.                  if (cat)
  20.                      Schroedingers_cat = TRUE;
  21.                  else
  22.                      deadcat = TRUE;
  23.                  box->spe = 0;
  24.              }
  25.              if (box->otyp == BAG_OF_TRICKS) {
  26.                  continue; /* wrong type of container */
  27.              } else if (box->cobj) {
  28.                  winid tmpwin = create_nhwindow(NHW_MENU);
  29.  
  30.                  sortloot(&box->cobj,
  31.                           (((flags.sortloot == 'l' || flags.sortloot == 'f')
  32.                             ? SORTLOOT_LOOT : 0)
  33.                            | (flags.sortpack ? SORTLOOT_PACK : 0)),
  34.                           FALSE);
  35.                  Sprintf(buf, "Contents of %s:", the(xname(box)));
  36.                  putstr(tmpwin, 0, buf);
  37.                  putstr(tmpwin, 0, "");
  38.                  for (obj = box->cobj; obj; obj = obj->nobj) {
  39.                      if (identified) {
  40.                          makeknown(obj->otyp);
  41.                          obj->known = obj->bknown = obj->dknown
  42.                              = obj->rknown = 1;
  43.                          if (Is_container(obj) || obj->otyp == STATUE)
  44.                              obj->cknown = obj->lknown = 1;
  45.                      }
  46.                      putstr(tmpwin, 0, doname(obj));
  47.                  }
  48.                  if (cat)
  49.                      putstr(tmpwin, 0, "Schroedinger's cat");
  50.                  else if (deadcat)
  51.                      putstr(tmpwin, 0, "Schroedinger's dead cat");
  52.                  display_nhwindow(tmpwin, TRUE);
  53.                  destroy_nhwindow(tmpwin);
  54.                  if (all_containers)
  55.                      container_contents(box->cobj, identified, TRUE,
  56.                                         reportempty);
  57.              } else if (cat || deadcat) {
  58.                  pline("%s Schroedinger's %scat!", Tobjnam(box, "contain"),
  59.                        deadcat ? "dead " : "");
  60.                  display_nhwindow(WIN_MESSAGE, FALSE);
  61.              } else if (reportempty) {
  62.                  pline("%s is empty.", upstart(thesimpleoname(box)));
  63.                  display_nhwindow(WIN_MESSAGE, FALSE);
  64.              }
  65.          }
  66.          if (!all_containers)
  67.              break;
  68.      }
  69.  }
  70.  

nh_terminate

  1.  /* should be called with either EXIT_SUCCESS or EXIT_FAILURE */
  2.  void
  3.  nh_terminate(status)
  4.  int status;
  5.  {
  6.      program_state.in_moveloop = 0; /* won't be returning to normal play */
  7.  #ifdef MAC
  8.      getreturn("to exit");
  9.  #endif
  10.      /* don't bother to try to release memory if we're in panic mode, to
  11.         avoid trouble in case that happens to be due to memory problems */
  12.      if (!program_state.panicking) {
  13.          freedynamicdata();
  14.          dlb_cleanup();
  15.      }
  16.  
  17.  #ifdef VMS
  18.      /*
  19.       *  This is liable to draw a warning if compiled with gcc, but it's
  20.       *  more important to flag panic() -> really_done() -> nh_terminate()
  21.       *  as __noreturn__ then to avoid the warning.
  22.       */
  23.      /* don't call exit() if already executing within an exit handler;
  24.         that would cancel any other pending user-mode handlers */
  25.      if (program_state.exiting)
  26.          return;
  27.  #endif
  28.      program_state.exiting = 1;
  29.      nethack_exit(status);
  30.  }
  31.  
  32.  extern const int monstr[];
  33.  
  34.  enum vanq_order_modes {
  35.      VANQ_MLVL_MNDX = 0,
  36.      VANQ_MSTR_MNDX,
  37.      VANQ_ALPHA_SEP,
  38.      VANQ_ALPHA_MIX,
  39.      VANQ_MCLS_HTOL,
  40.      VANQ_MCLS_LTOH,
  41.      VANQ_COUNT_H_L,
  42.      VANQ_COUNT_L_H,
  43.  
  44.      NUM_VANQ_ORDER_MODES
  45.  };
  46.  
  47.  static const char *vanqorders[NUM_VANQ_ORDER_MODES] = {
  48.      "traditional: by monster level, by internal monster index",
  49.      "by monster toughness, by internal monster index",
  50.      "alphabetically, first unique monsters, then others",
  51.      "alphabetically, unique monsters and others intermixed",
  52.      "by monster class, high to low level within class",
  53.      "by monster class, low to high level within class",
  54.      "by count, high to low, by internal index within tied count",
  55.      "by count, low to high, by internal index within tied count",
  56.  };
  57.  static int vanq_sortmode = VANQ_MLVL_MNDX;
  58.  

vanqsort_cmp

  1.  STATIC_PTR int CFDECLSPEC
  2.  vanqsort_cmp(vptr1, vptr2)
  3.  const genericptr vptr1;
  4.  const genericptr vptr2;
  5.  {
  6.      int indx1 = *(short *) vptr1, indx2 = *(short *) vptr2,
  7.          mlev1, mlev2, mstr1, mstr2, uniq1, uniq2, died1, died2, res;
  8.      const char *name1, *name2, *punct;
  9.      schar mcls1, mcls2;
  10.  
  11.      switch (vanq_sortmode) {
  12.      default:
  13.      case VANQ_MLVL_MNDX:
  14.          /* sort by monster level */
  15.          mlev1 = mons[indx1].mlevel, mlev2 = mons[indx2].mlevel;
  16.          res = mlev2 - mlev1; /* mlevel high to low */
  17.          break;
  18.      case VANQ_MSTR_MNDX:
  19.          /* sort by monster toughness */
  20.          mstr1 = monstr[indx1], mstr2 = monstr[indx2];
  21.          res = mstr2 - mstr1; /* monstr high to low */
  22.          break;
  23.      case VANQ_ALPHA_SEP:
  24.          uniq1 = ((mons[indx1].geno & G_UNIQ) && indx1 != PM_HIGH_PRIEST);
  25.          uniq2 = ((mons[indx2].geno & G_UNIQ) && indx2 != PM_HIGH_PRIEST);
  26.          if (uniq1 ^ uniq2) { /* one or other uniq, but not both */
  27.              res = uniq2 - uniq1;
  28.              break;
  29.          } /* else both unique or neither unique */
  30.          /*FALLTHRU*/
  31.      case VANQ_ALPHA_MIX:
  32.          name1 = mons[indx1].mname, name2 = mons[indx2].mname;
  33.          res = strcmpi(name1, name2); /* caseblind alhpa, low to high */
  34.          break;
  35.      case VANQ_MCLS_HTOL:
  36.      case VANQ_MCLS_LTOH:
  37.          /* mons[].mlet is a small integer, 1..N, of type plain char;
  38.             if 'char' happens to be unsigned, (mlet1 - mlet2) would yield
  39.             an inappropriate result when mlet2 is greater than mlet1,
  40.             so force our copies (mcls1, mcls2) to be signed */
  41.          mcls1 = (schar) mons[indx1].mlet, mcls2 = (schar) mons[indx2].mlet;
  42.          /* S_ANT through S_ZRUTY correspond to lowercase monster classes,
  43.             S_ANGEL through S_ZOMBIE correspond to uppercase, and various
  44.             punctuation characters are used for classes beyond those */
  45.          if (mcls1 > S_ZOMBIE && mcls2 > S_ZOMBIE) {
  46.              /* force a specific order to the punctuation classes that's
  47.                 different from the internal order;
  48.                 internal order is ok if neither or just one is punctuation
  49.                 since letters have lower values so come out before punct */
  50.              static const char punctclasses[] = {
  51.                  S_LIZARD, S_EEL, S_GOLEM, S_GHOST, S_DEMON, S_HUMAN, '\0'
  52.              };
  53.  
  54.              if ((punct = index(punctclasses, mcls1)) != 0)
  55.                  mcls1 = (schar) (S_ZOMBIE + 1 + (int) (punct - punctclasses));
  56.              if ((punct = index(punctclasses, mcls2)) != 0)
  57.                  mcls2 = (schar) (S_ZOMBIE + 1 + (int) (punct - punctclasses));
  58.          }
  59.          res = mcls1 - mcls2; /* class */
  60.          if (res == 0) {
  61.              mlev1 = mons[indx1].mlevel, mlev2 = mons[indx2].mlevel;
  62.              res = mlev1 - mlev2; /* mlevel low to high */
  63.              if (vanq_sortmode == VANQ_MCLS_HTOL)
  64.                  res = -res; /* mlevel high to low */
  65.          }
  66.          break;
  67.      case VANQ_COUNT_H_L:
  68.      case VANQ_COUNT_L_H:
  69.          died1 = mvitals[indx1].died, died2 = mvitals[indx2].died;
  70.          res = died2 - died1; /* dead count high to low */
  71.          if (vanq_sortmode == VANQ_COUNT_L_H)
  72.              res = -res; /* dead count low to high */
  73.          break;
  74.      }
  75.      /* tiebreaker: internal mons[] index */
  76.      if (res == 0)
  77.          res = indx1 - indx2; /* mndx low to high */
  78.      return res;
  79.  }
  80.  

set_vanq_order

  1.  /* returns -1 if cancelled via ESC */
  2.  STATIC_OVL int
  3.  set_vanq_order()
  4.  {
  5.      winid tmpwin;
  6.      menu_item *selected;
  7.      anything any;
  8.      int i, n, choice;
  9.  
  10.      tmpwin = create_nhwindow(NHW_MENU);
  11.      start_menu(tmpwin);
  12.      any = zeroany; /* zero out all bits */
  13.      for (i = 0; i < SIZE(vanqorders); i++) {
  14.          if (i == VANQ_ALPHA_MIX || i == VANQ_MCLS_HTOL) /* skip these */
  15.              continue;
  16.          any.a_int = i + 1;
  17.          add_menu(tmpwin, NO_GLYPH, &any, 0, 0, ATR_NONE, vanqorders[i],
  18.                   (i == vanq_sortmode) ? MENU_SELECTED : MENU_UNSELECTED);
  19.      }
  20.      end_menu(tmpwin, "Sort order for vanquished monster counts");
  21.  
  22.      n = select_menu(tmpwin, PICK_ONE, &selected);
  23.      destroy_nhwindow(tmpwin);
  24.      if (n > 0) {
  25.          choice = selected[0].item.a_int - 1;
  26.          /* skip preselected entry if we have more than one item chosen */
  27.          if (n > 1 && choice == vanq_sortmode)
  28.              choice = selected[1].item.a_int - 1;
  29.          free((genericptr_t) selected);
  30.          vanq_sortmode = choice;
  31.      }
  32.      return (n < 0) ? -1 : vanq_sortmode;
  33.  }
  34.  

dovanquished

  1.  /* #vanquished command */
  2.  int
  3.  dovanquished()
  4.  {
  5.      list_vanquished('a', FALSE);
  6.      return 0;
  7.  }
  8.  
  9.  /* high priests aren't unique but are flagged as such to simplify something */
  10.  #define UniqCritterIndx(mndx) ((mons[mndx].geno & G_UNIQ) \
  11.                                 && mndx != PM_HIGH_PRIEST)
  12.  

list_vanquished

  1.  STATIC_OVL void
  2.  list_vanquished(defquery, ask)
  3.  char defquery;
  4.  boolean ask;
  5.  {
  6.      register int i;
  7.      int pfx, nkilled;
  8.      unsigned ntypes, ni;
  9.      long total_killed = 0L;
  10.      winid klwin;
  11.      short mindx[NUMMONS];
  12.      char c, buf[BUFSZ], buftoo[BUFSZ];
  13.      boolean dumping; /* for DUMPLOG; doesn't need to be conditional */
  14.  
  15.      dumping = (defquery == 'd');
  16.      if (dumping)
  17.          defquery = 'y';
  18.  
  19.      /* get totals first */
  20.      ntypes = 0;
  21.      for (i = LOW_PM; i < NUMMONS; i++) {
  22.          if ((nkilled = (int) mvitals[i].died) == 0)
  23.              continue;
  24.          mindx[ntypes++] = i;
  25.          total_killed += (long) nkilled;
  26.      }
  27.  
  28.      /* vanquished creatures list;
  29.       * includes all dead monsters, not just those killed by the player
  30.       */
  31.      if (ntypes != 0) {
  32.          char mlet, prev_mlet = 0; /* used as small integer, not character */
  33.          boolean class_header, uniq_header, was_uniq = FALSE;
  34.  
  35.          c = ask ? yn_function(
  36.                              "Do you want an account of creatures vanquished?",
  37.                                ynaqchars, defquery)
  38.                  : defquery;
  39.          if (c == 'q')
  40.              done_stopprint++;
  41.          if (c == 'y' || c == 'a') {
  42.              if (c == 'a') { /* ask player to choose sort order */
  43.                  /* choose value for vanq_sortmode via menu; ESC cancels list
  44.                     of vanquished monsters but does not set 'done_stopprint' */
  45.                  if (set_vanq_order() < 0)
  46.                      return;
  47.              }
  48.              uniq_header = (vanq_sortmode == VANQ_ALPHA_SEP);
  49.              class_header = (vanq_sortmode == VANQ_MCLS_LTOH
  50.                              || vanq_sortmode == VANQ_MCLS_HTOL);
  51.  
  52.              klwin = create_nhwindow(NHW_MENU);
  53.              putstr(klwin, 0, "Vanquished creatures:");
  54.              if (!dumping)
  55.                  putstr(klwin, 0, "");
  56.  
  57.              qsort((genericptr_t) mindx, ntypes, sizeof *mindx, vanqsort_cmp);
  58.              for (ni = 0; ni < ntypes; ni++) {
  59.                  i = mindx[ni];
  60.                  nkilled = mvitals[i].died;
  61.                  mlet = mons[i].mlet;
  62.                  if (class_header && mlet != prev_mlet) {
  63.                      Strcpy(buf, def_monsyms[(int) mlet].explain);
  64.                      putstr(klwin, ask ? 0 : iflags.menu_headings,
  65.                             upstart(buf));
  66.                      prev_mlet = mlet;
  67.                  }
  68.                  if (UniqCritterIndx(i)) {
  69.                      Sprintf(buf, "%s%s",
  70.                              !type_is_pname(&mons[i]) ? "the " : "",
  71.                              mons[i].mname);
  72.                      if (nkilled > 1) {
  73.                          switch (nkilled) {
  74.                          case 2:
  75.                              Sprintf(eos(buf), " (twice)");
  76.                              break;
  77.                          case 3:
  78.                              Sprintf(eos(buf), " (thrice)");
  79.                              break;
  80.                          default:
  81.                              Sprintf(eos(buf), " (%d times)", nkilled);
  82.                              break;
  83.                          }
  84.                      }
  85.                      was_uniq = TRUE;
  86.                  } else {
  87.                      if (uniq_header && was_uniq) {
  88.                          putstr(klwin, 0, "");
  89.                          was_uniq = FALSE;
  90.                      }
  91.                      /* trolls or undead might have come back,
  92.                         but we don't keep track of that */
  93.                      if (nkilled == 1)
  94.                          Strcpy(buf, an(mons[i].mname));
  95.                      else
  96.                          Sprintf(buf, "%3d %s", nkilled,
  97.                                  makeplural(mons[i].mname));
  98.                  }
  99.                  /* number of leading spaces to match 3 digit prefix */
  100.                  pfx = !strncmpi(buf, "the ", 3) ? 0
  101.                        : !strncmpi(buf, "an ", 3) ? 1
  102.                          : !strncmpi(buf, "a ", 2) ? 2
  103.                            : !digit(buf[2]) ? 4 : 0;
  104.                  if (class_header)
  105.                      ++pfx;
  106.                  Sprintf(buftoo, "%*s%s", pfx, "", buf);
  107.                  putstr(klwin, 0, buftoo);
  108.              }
  109.              /*
  110.               * if (Hallucination)
  111.               *     putstr(klwin, 0, "and a partridge in a pear tree");
  112.               */
  113.              if (ntypes > 1) {
  114.                  if (!dumping)
  115.                      putstr(klwin, 0, "");
  116.                  Sprintf(buf, "%ld creatures vanquished.", total_killed);
  117.                  putstr(klwin, 0, buf);
  118.              }
  119.              display_nhwindow(klwin, TRUE);
  120.              destroy_nhwindow(klwin);
  121.          }
  122.      } else if (defquery == 'a') {
  123.          /* #dovanquished rather than final disclosure, so pline() is ok */
  124.          pline("No creatures have been vanquished.");
  125.  #ifdef DUMPLOG
  126.      } else if (dumping) {
  127.          putstr(0, 0, "No creatures were vanquished."); /* not pline() */
  128.  #endif
  129.      }
  130.  }
  131.  

num_genocides

  1.  /* number of monster species which have been genocided */
  2.  int
  3.  num_genocides()
  4.  {
  5.      int i, n = 0;
  6.  
  7.      for (i = LOW_PM; i < NUMMONS; ++i) {
  8.          if (mvitals[i].mvflags & G_GENOD) {
  9.              ++n;
  10.              if (UniqCritterIndx(i))
  11.                  impossible("unique creature '%d: %s' genocided?",
  12.                             i, mons[i].mname);
  13.          }
  14.      }
  15.      return n;
  16.  }
  17.  

num_extinct

  1.  STATIC_OVL int
  2.  num_extinct()
  3.  {
  4.      int i, n = 0;
  5.  
  6.      for (i = LOW_PM; i < NUMMONS; ++i) {
  7.          if (UniqCritterIndx(i))
  8.              continue;
  9.          if ((mvitals[i].mvflags & G_GONE) == G_EXTINCT)
  10.              ++n;
  11.      }
  12.      return n;
  13.  }
  14.  

list_genocided

  1.  STATIC_OVL void
  2.  list_genocided(defquery, ask)
  3.  char defquery;
  4.  boolean ask;
  5.  {
  6.      register int i;
  7.      int ngenocided, nextinct;
  8.      char c;
  9.      winid klwin;
  10.      char buf[BUFSZ];
  11.      boolean dumping; /* for DUMPLOG; doesn't need to be conditional */
  12.  
  13.      dumping = (defquery == 'd');
  14.      if (dumping)
  15.          defquery = 'y';
  16.  
  17.      ngenocided = num_genocides();
  18.      nextinct = num_extinct();
  19.  
  20.      /* genocided or extinct species list */
  21.      if (ngenocided != 0 || nextinct != 0) {
  22.          Sprintf(buf, "Do you want a list of %sspecies%s%s?",
  23.                  (nextinct && !ngenocided) ? "extinct " : "",
  24.                  (ngenocided) ? " genocided" : "",
  25.                  (nextinct && ngenocided) ? " and extinct" : "");
  26.          c = ask ? yn_function(buf, ynqchars, defquery) : defquery;
  27.          if (c == 'q')
  28.              done_stopprint++;
  29.          if (c == 'y') {
  30.              klwin = create_nhwindow(NHW_MENU);
  31.              Sprintf(buf, "%s%s species:",
  32.                      (ngenocided) ? "Genocided" : "Extinct",
  33.                      (nextinct && ngenocided) ? " or extinct" : "");
  34.              putstr(klwin, 0, buf);
  35.              if (!dumping)
  36.                  putstr(klwin, 0, "");
  37.  
  38.              for (i = LOW_PM; i < NUMMONS; i++) {
  39.                  /* uniques can't be genocided but can become extinct;
  40.                     however, they're never reported as extinct, so skip them */
  41.                  if (UniqCritterIndx(i))
  42.                      continue;
  43.                  if (mvitals[i].mvflags & G_GONE) {
  44.                      Sprintf(buf, " %s", makeplural(mons[i].mname));
  45.                      /*
  46.                       * "Extinct" is unfortunate terminology.  A species
  47.                       * is marked extinct when its birth limit is reached,
  48.                       * but there might be members of the species still
  49.                       * alive, contradicting the meaning of the word.
  50.                       */
  51.                      if ((mvitals[i].mvflags & G_GONE) == G_EXTINCT)
  52.                          Strcat(buf, " (extinct)");
  53.                      putstr(klwin, 0, buf);
  54.                  }
  55.              }
  56.              if (!dumping)
  57.                  putstr(klwin, 0, "");
  58.              if (ngenocided > 0) {
  59.                  Sprintf(buf, "%d species genocided.", ngenocided);
  60.                  putstr(klwin, 0, buf);
  61.              }
  62.              if (nextinct > 0) {
  63.                  Sprintf(buf, "%d species extinct.", nextinct);
  64.                  putstr(klwin, 0, buf);
  65.              }
  66.  
  67.              display_nhwindow(klwin, TRUE);
  68.              destroy_nhwindow(klwin);
  69.          }
  70.  #ifdef DUMPLOG
  71.      } else if (dumping) {
  72.          putstr(0, 0, "No species were genocided or became extinct.");
  73.  #endif
  74.      }
  75.  }
  76.  

delayed_killer

  1.  /* set a delayed killer, ensure non-delayed killer is cleared out */
  2.  void
  3.  delayed_killer(id, format, killername)
  4.  int id;
  5.  int format;
  6.  const char *killername;
  7.  {
  8.      struct kinfo *k = find_delayed_killer(id);
  9.  
  10.      if (k == (struct kinfo *) 0) {
  11.          /* no match, add a new delayed killer to the list */
  12.          k = (struct kinfo *) alloc(sizeof(struct kinfo));
  13.          (void) memset((genericptr_t)k, 0, sizeof(struct kinfo));
  14.          k->id = id;
  15.          k->next = killer.next;
  16.          killer.next = k;
  17.      }
  18.  
  19.      k->format = format;
  20.      Strcpy(k->name, killername ? killername : "");
  21.      killer.name[0] = 0;
  22.  }
  23.  

find_delayed_killer

  1.  struct kinfo *
  2.  find_delayed_killer(id)
  3.  int id;
  4.  {
  5.      struct kinfo *k;
  6.  
  7.      for (k = killer.next; k != (struct kinfo *) 0; k = k->next) {
  8.          if (k->id == id)
  9.              break;
  10.      }
  11.      return k;
  12.  }
  13.  

dealloc_killer

  1.  void
  2.  dealloc_killer(kptr)
  3.  struct kinfo *kptr;
  4.  {
  5.      struct kinfo *prev = &killer, *k;
  6.  
  7.      if (kptr == (struct kinfo *) 0)
  8.          return;
  9.      for (k = killer.next; k != (struct kinfo *) 0; k = k->next) {
  10.          if (k == kptr)
  11.              break;
  12.          prev = k;
  13.      }
  14.  
  15.      if (k == (struct kinfo *) 0) {
  16.          impossible("dealloc_killer not on list");
  17.      } else {
  18.          prev->next = k->next;
  19.          free((genericptr_t) k);
  20.      }
  21.  }
  22.  

save_killers

  1.  void
  2.  save_killers(fd, mode)
  3.  int fd;
  4.  int mode;
  5.  {
  6.      struct kinfo *kptr;
  7.  
  8.      if (perform_bwrite(mode)) {
  9.          for (kptr = &killer; kptr != (struct kinfo *) 0; kptr = kptr->next) {
  10.              bwrite(fd, (genericptr_t) kptr, sizeof(struct kinfo));
  11.          }
  12.      }
  13.      if (release_data(mode)) {
  14.          while (killer.next) {
  15.              kptr = killer.next->next;
  16.              free((genericptr_t) killer.next);
  17.              killer.next = kptr;
  18.          }
  19.      }
  20.  }
  21.  

restore_killers

  1.  void
  2.  restore_killers(fd)
  3.  int fd;
  4.  {
  5.      struct kinfo *kptr;
  6.  
  7.      for (kptr = &killer; kptr != (struct kinfo *) 0; kptr = kptr->next) {
  8.          mread(fd, (genericptr_t) kptr, sizeof(struct kinfo));
  9.          if (kptr->next) {
  10.              kptr->next = (struct kinfo *) alloc(sizeof(struct kinfo));
  11.          }
  12.      }
  13.  }
  14.  

wordcount

  1.  static int
  2.  wordcount(p)
  3.  char *p;
  4.  {
  5.      int words = 0;
  6.  
  7.      while (*p) {
  8.          while (*p && isspace((uchar) *p))
  9.              p++;
  10.          if (*p)
  11.              words++;
  12.          while (*p && !isspace((uchar) *p))
  13.              p++;
  14.      }
  15.      return words;
  16.  }
  17.  

bel_copy1

  1.  static void
  2.  bel_copy1(inp, out)
  3.  char **inp, *out;
  4.  {
  5.      char *in = *inp;
  6.  
  7.      out += strlen(out); /* eos() */
  8.      while (*in && isspace((uchar) *in))
  9.          in++;
  10.      while (*in && !isspace((uchar) *in))
  11.          *out++ = *in++;
  12.      *out = '\0';
  13.      *inp = in;
  14.  }
  15.  

build_english_list

  1.  char *
  2.  build_english_list(in)
  3.  char *in;
  4.  {
  5.      char *out, *p = in;
  6.      int len = (int) strlen(p), words = wordcount(p);
  7.  
  8.      /* +3: " or " - " "; +(words - 1): (N-1)*(", " - " ") */
  9.      if (words > 1)
  10.          len += 3 + (words - 1);
  11.      out = (char *) alloc(len + 1);
  12.      *out = '\0'; /* bel_copy1() appends */
  13.  
  14.      switch (words) {
  15.      case 0:
  16.          impossible("no words in list");
  17.          break;
  18.      case 1:
  19.          /* "single" */
  20.          bel_copy1(&p, out);
  21.          break;
  22.      default:
  23.          if (words == 2) {
  24.              /* "first or second" */
  25.              bel_copy1(&p, out);
  26.              Strcat(out, " ");
  27.          } else {
  28.              /* "first, second, or third */
  29.              do {
  30.                  bel_copy1(&p, out);
  31.                  Strcat(out, ", ");
  32.              } while (--words > 1);
  33.          }
  34.          Strcat(out, "or ");
  35.          bel_copy1(&p, out);
  36.          break;
  37.      }
  38.      return out;
  39.  }
  40.  
  41.  /*end.c*/