Source:NetHack 3.6.0/src/objnam.c

From NetHackWiki
(Redirected from Objnam.c)
Jump to: navigation, search

Below is the full text to objnam.c from the source code of NetHack 3.6.0. To link to a particular line, write [[Source:NetHack 3.6.0/src/objnam.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

  1.  /* NetHack 3.6	objnam.c	$NHDT-Date: 1447490776 2015/11/14 08:46:16 $  $NHDT-Branch: master $:$NHDT-Revision: 1.154 $ */
  2.  /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
  3.  /* NetHack may be freely redistributed.  See license for details. */
  4.  
  5.  #include "hack.h"
  6.  
  7.  /* "an uncursed greased partly eaten guardian naga hatchling [corpse]" */
  8.  #define PREFIX 80 /* (56) */
  9.  #define SCHAR_LIM 127
  10.  #define NUMOBUF 12
  11.  
  12.  STATIC_DCL char *FDECL(strprepend, (char *, const char *));
  13.  STATIC_DCL short FDECL(rnd_otyp_by_wpnskill, (SCHAR_P));
  14.  STATIC_DCL boolean FDECL(wishymatch, (const char *, const char *, BOOLEAN_P));
  15.  STATIC_DCL char *NDECL(nextobuf);
  16.  STATIC_DCL void FDECL(releaseobuf, (char *));
  17.  STATIC_DCL char *FDECL(minimal_xname, (struct obj *));
  18.  STATIC_DCL void FDECL(add_erosion_words, (struct obj *, char *));
  19.  STATIC_DCL boolean
  20.  FDECL(singplur_lookup, (char *, char *, BOOLEAN_P, const char *const *));
  21.  STATIC_DCL char *FDECL(singplur_compound, (char *));
  22.  STATIC_DCL char *FDECL(xname_flags, (struct obj *, unsigned));
  23.  
  24.  struct Jitem {
  25.      int item;
  26.      const char *name;
  27.  };
  28.  
  29.  #define BSTRCMPI(base, ptr, str) ((ptr) < base || strcmpi((ptr), str))
  30.  #define BSTRNCMPI(base, ptr, str, num) \
  31.      ((ptr) < base || strncmpi((ptr), str, num))
  32.  #define Strcasecpy(dst, src) (void) strcasecpy(dst, src)
  33.  
  34.  /* true for gems/rocks that should have " stone" appended to their names */
  35.  #define GemStone(typ)                                                  \
  36.      (typ == FLINT                                                      \
  37.       || (objects[typ].oc_material == GEMSTONE                          \
  38.           && (typ != DILITHIUM_CRYSTAL && typ != RUBY && typ != DIAMOND \
  39.               && typ != SAPPHIRE && typ != BLACK_OPAL && typ != EMERALD \
  40.               && typ != OPAL)))
  41.  
  42.  STATIC_OVL struct Jitem Japanese_items[] = { { SHORT_SWORD, "wakizashi" },
  43.                                               { BROADSWORD, "ninja-to" },
  44.                                               { FLAIL, "nunchaku" },
  45.                                               { GLAIVE, "naginata" },
  46.                                               { LOCK_PICK, "osaku" },
  47.                                               { WOODEN_HARP, "koto" },
  48.                                               { KNIFE, "shito" },
  49.                                               { PLATE_MAIL, "tanko" },
  50.                                               { HELMET, "kabuto" },
  51.                                               { LEATHER_GLOVES, "yugake" },
  52.                                               { FOOD_RATION, "gunyoki" },
  53.                                               { POT_BOOZE, "sake" },
  54.                                               { 0, "" } };
  55.  
  56.  STATIC_DCL const char *FDECL(Japanese_item_name, (int i));
  57.  

strprepend

  1.  STATIC_OVL char *
  2.  strprepend(s, pref)
  3.  register char *s;
  4.  register const char *pref;
  5.  {
  6.      register int i = (int) strlen(pref);
  7.  
  8.      if (i > PREFIX) {
  9.          impossible("PREFIX too short (for %d).", i);
  10.          return s;
  11.      }
  12.      s -= i;
  13.      (void) strncpy(s, pref, i); /* do not copy trailing 0 */
  14.      return s;
  15.  }
  16.  

nextobuf

  1.  /* manage a pool of BUFSZ buffers, so callers don't have to */
  2.  static char NEARDATA obufs[NUMOBUF][BUFSZ];
  3.  static int obufidx = 0;
  4.  
  5.  STATIC_OVL char *
  6.  nextobuf()
  7.  {
  8.      obufidx = (obufidx + 1) % NUMOBUF;
  9.      return obufs[obufidx];
  10.  }
  11.  

releaseobuf

  1.  /* put the most recently allocated buffer back if possible */
  2.  STATIC_OVL void
  3.  releaseobuf(bufp)
  4.  char *bufp;
  5.  {
  6.      /* caller may not know whether bufp is the most recently allocated
  7.         buffer; if it isn't, do nothing */
  8.      if (bufp == obufs[obufidx])
  9.          obufidx = (obufidx - 1 + NUMOBUF) % NUMOBUF;
  10.  }
  11.  

obj_typename

  1.  char *
  2.  obj_typename(otyp)
  3.  register int otyp;
  4.  {
  5.      char *buf = nextobuf();
  6.      register struct objclass *ocl = &objects[otyp];
  7.      register const char *actualn = OBJ_NAME(*ocl);
  8.      register const char *dn = OBJ_DESCR(*ocl);
  9.      register const char *un = ocl->oc_uname;
  10.      register int nn = ocl->oc_name_known;
  11.  
  12.      if (Role_if(PM_SAMURAI) && Japanese_item_name(otyp))
  13.          actualn = Japanese_item_name(otyp);
  14.      switch (ocl->oc_class) {
  15.      case COIN_CLASS:
  16.          Strcpy(buf, "coin");
  17.          break;
  18.      case POTION_CLASS:
  19.          Strcpy(buf, "potion");
  20.          break;
  21.      case SCROLL_CLASS:
  22.          Strcpy(buf, "scroll");
  23.          break;
  24.      case WAND_CLASS:
  25.          Strcpy(buf, "wand");
  26.          break;
  27.      case SPBOOK_CLASS:
  28.          Strcpy(buf, "spellbook");
  29.          break;
  30.      case RING_CLASS:
  31.          Strcpy(buf, "ring");
  32.          break;
  33.      case AMULET_CLASS:
  34.          if (nn)
  35.              Strcpy(buf, actualn);
  36.          else
  37.              Strcpy(buf, "amulet");
  38.          if (un)
  39.              Sprintf(eos(buf), " called %s", un);
  40.          if (dn)
  41.              Sprintf(eos(buf), " (%s)", dn);
  42.          return buf;
  43.      default:
  44.          if (nn) {
  45.              Strcpy(buf, actualn);
  46.              if (GemStone(otyp))
  47.                  Strcat(buf, " stone");
  48.              if (un)
  49.                  Sprintf(eos(buf), " called %s", un);
  50.              if (dn)
  51.                  Sprintf(eos(buf), " (%s)", dn);
  52.          } else {
  53.              Strcpy(buf, dn ? dn : actualn);
  54.              if (ocl->oc_class == GEM_CLASS)
  55.                  Strcat(buf,
  56.                         (ocl->oc_material == MINERAL) ? " stone" : " gem");
  57.              if (un)
  58.                  Sprintf(eos(buf), " called %s", un);
  59.          }
  60.          return buf;
  61.      }
  62.      /* here for ring/scroll/potion/wand */
  63.      if (nn) {
  64.          if (ocl->oc_unique)
  65.              Strcpy(buf, actualn); /* avoid spellbook of Book of the Dead */
  66.          else
  67.              Sprintf(eos(buf), " of %s", actualn);
  68.      }
  69.      if (un)
  70.          Sprintf(eos(buf), " called %s", un);
  71.      if (dn)
  72.          Sprintf(eos(buf), " (%s)", dn);
  73.      return buf;
  74.  }
  75.  

simple_typename

  1.  /* less verbose result than obj_typename(); either the actual name
  2.     or the description (but not both); user-assigned name is ignored */
  3.  char *
  4.  simple_typename(otyp)
  5.  int otyp;
  6.  {
  7.      char *bufp, *pp, *save_uname = objects[otyp].oc_uname;
  8.  
  9.      objects[otyp].oc_uname = 0; /* suppress any name given by user */
  10.      bufp = obj_typename(otyp);
  11.      objects[otyp].oc_uname = save_uname;
  12.      if ((pp = strstri(bufp, " (")) != 0)
  13.          *pp = '\0'; /* strip the appended description */
  14.      return bufp;
  15.  }
  16.  

obj_is_pname

  1.  boolean
  2.  obj_is_pname(obj)
  3.  struct obj *obj;
  4.  {
  5.      if (!obj->oartifact || !has_oname(obj))
  6.          return FALSE;
  7.      if (!program_state.gameover && !iflags.override_ID) {
  8.          if (not_fully_identified(obj))
  9.              return FALSE;
  10.      }
  11.      return TRUE;
  12.  }
  13.  

distant_name

  1.  /* Give the name of an object seen at a distance.  Unlike xname/doname,
  2.   * we don't want to set dknown if it's not set already.  The kludge used is
  3.   * to temporarily set Blind so that xname() skips the dknown setting.  This
  4.   * assumes that we don't want to do this too often; if this function becomes
  5.   * frequently used, it'd probably be better to pass a parameter to xname()
  6.   * or doname() instead.
  7.   */
  8.  char *
  9.  distant_name(obj, func)
  10.  struct obj *obj;
  11.  char *FDECL((*func), (OBJ_P));
  12.  {
  13.      char *str;
  14.  
  15.      long save_Blinded = Blinded;
  16.      Blinded = 1;
  17.      str = (*func)(obj);
  18.      Blinded = save_Blinded;
  19.      return str;
  20.  }
  21.  

fruitname

  1.  /* convert player specified fruit name into corresponding fruit juice name
  2.     ("slice of pizza" -> "pizza juice" rather than "slice of pizza juice") */
  3.  char *
  4.  fruitname(juice)
  5.  boolean juice; /* whether or not to append " juice" to the name */
  6.  {
  7.      char *buf = nextobuf();
  8.      const char *fruit_nam = strstri(pl_fruit, " of ");
  9.  
  10.      if (fruit_nam)
  11.          fruit_nam += 4; /* skip past " of " */
  12.      else
  13.          fruit_nam = pl_fruit; /* use it as is */
  14.  
  15.      Sprintf(buf, "%s%s", makesingular(fruit_nam), juice ? " juice" : "");
  16.      return buf;
  17.  }
  18.  

xname

  1.  char *
  2.  xname(obj)
  3.  struct obj *obj;
  4.  {
  5.      return xname_flags(obj, CXN_NORMAL);
  6.  }
  7.  

xname_flags

  1.  char *
  2.  xname_flags(obj, cxn_flags)
  3.  register struct obj *obj;
  4.  unsigned cxn_flags; /* bitmask of CXN_xxx values */
  5.  {
  6.      register char *buf;
  7.      register int typ = obj->otyp;
  8.      register struct objclass *ocl = &objects[typ];
  9.      int nn = ocl->oc_name_known, omndx = obj->corpsenm;
  10.      const char *actualn = OBJ_NAME(*ocl);
  11.      const char *dn = OBJ_DESCR(*ocl);
  12.      const char *un = ocl->oc_uname;
  13.      boolean pluralize = (obj->quan != 1L) && !(cxn_flags & CXN_SINGULAR);
  14.      boolean known, dknown, bknown;
  15.  
  16.      buf = nextobuf() + PREFIX; /* leave room for "17 -3 " */
  17.      if (Role_if(PM_SAMURAI) && Japanese_item_name(typ))
  18.          actualn = Japanese_item_name(typ);
  19.  
  20.      buf[0] = '\0';
  21.      /*
  22.       * clean up known when it's tied to oc_name_known, eg after AD_DRIN
  23.       * This is only required for unique objects since the article
  24.       * printed for the object is tied to the combination of the two
  25.       * and printing the wrong article gives away information.
  26.       */
  27.      if (!nn && ocl->oc_uses_known && ocl->oc_unique)
  28.          obj->known = 0;
  29.      if (!Blind)
  30.          obj->dknown = TRUE;
  31.      if (Role_if(PM_PRIEST))
  32.          obj->bknown = TRUE;
  33.  
  34.      if (iflags.override_ID) {
  35.          known = dknown = bknown = TRUE;
  36.          nn = 1;
  37.      } else {
  38.          known = obj->known;
  39.          dknown = obj->dknown;
  40.          bknown = obj->bknown;
  41.      }
  42.  
  43.      if (obj_is_pname(obj))
  44.          goto nameit;
  45.      switch (obj->oclass) {
  46.      case AMULET_CLASS:
  47.          if (!dknown)
  48.              Strcpy(buf, "amulet");
  49.          else if (typ == AMULET_OF_YENDOR || typ == FAKE_AMULET_OF_YENDOR)
  50.              /* each must be identified individually */
  51.              Strcpy(buf, known ? actualn : dn);
  52.          else if (nn)
  53.              Strcpy(buf, actualn);
  54.          else if (un)
  55.              Sprintf(buf, "amulet called %s", un);
  56.          else
  57.              Sprintf(buf, "%s amulet", dn);
  58.          break;
  59.      case WEAPON_CLASS:
  60.          if (is_poisonable(obj) && obj->opoisoned)
  61.              Strcpy(buf, "poisoned ");
  62.      case VENOM_CLASS:
  63.      case TOOL_CLASS:
  64.          if (typ == LENSES)
  65.              Strcpy(buf, "pair of ");
  66.          else if (is_wet_towel(obj))
  67.              Strcpy(buf, (obj->spe < 3) ? "moist " : "wet ");
  68.  
  69.          if (!dknown)
  70.              Strcat(buf, dn ? dn : actualn);
  71.          else if (nn)
  72.              Strcat(buf, actualn);
  73.          else if (un) {
  74.              Strcat(buf, dn ? dn : actualn);
  75.              Strcat(buf, " called ");
  76.              Strcat(buf, un);
  77.          } else
  78.              Strcat(buf, dn ? dn : actualn);
  79.          /* If we use an() here we'd have to remember never to use */
  80.          /* it whenever calling doname() or xname(). */
  81.          if (typ == FIGURINE && omndx != NON_PM) {
  82.              Sprintf(eos(buf), " of a%s %s",
  83.                      index(vowels, *mons[omndx].mname) ? "n" : "",
  84.                      mons[omndx].mname);
  85.          } else if (is_wet_towel(obj)) {
  86.              if (wizard)
  87.                  Sprintf(eos(buf), " (%d)", obj->spe);
  88.          }
  89.          break;
  90.      case ARMOR_CLASS:
  91.          /* depends on order of the dragon scales objects */
  92.          if (typ >= GRAY_DRAGON_SCALES && typ <= YELLOW_DRAGON_SCALES) {
  93.              Sprintf(buf, "set of %s", actualn);
  94.              break;
  95.          }
  96.          if (is_boots(obj) || is_gloves(obj))
  97.              Strcpy(buf, "pair of ");
  98.  
  99.          if (obj->otyp >= ELVEN_SHIELD && obj->otyp <= ORCISH_SHIELD
  100.              && !dknown) {
  101.              Strcpy(buf, "shield");
  102.              break;
  103.          }
  104.          if (obj->otyp == SHIELD_OF_REFLECTION && !dknown) {
  105.              Strcpy(buf, "smooth shield");
  106.              break;
  107.          }
  108.  
  109.          if (nn)
  110.              Strcat(buf, actualn);
  111.          else if (un) {
  112.              if (is_boots(obj))
  113.                  Strcat(buf, "boots");
  114.              else if (is_gloves(obj))
  115.                  Strcat(buf, "gloves");
  116.              else if (is_cloak(obj))
  117.                  Strcpy(buf, "cloak");
  118.              else if (is_helmet(obj))
  119.                  Strcpy(buf, "helmet");
  120.              else if (is_shield(obj))
  121.                  Strcpy(buf, "shield");
  122.              else
  123.                  Strcpy(buf, "armor");
  124.              Strcat(buf, " called ");
  125.              Strcat(buf, un);
  126.          } else
  127.              Strcat(buf, dn);
  128.          break;
  129.      case FOOD_CLASS:
  130.          if (typ == SLIME_MOLD) {
  131.              register struct fruit *f;
  132.  
  133.              for (f = ffruit; f; f = f->nextf) {
  134.                  if (f->fid == obj->spe) {
  135.                      Strcpy(buf, f->fname);
  136.                      break;
  137.                  }
  138.              }
  139.              if (!f) {
  140.                  impossible("Bad fruit #%d?", obj->spe);
  141.                  Strcpy(buf, "fruit");
  142.              } else if (pluralize) {
  143.                  /* ick; already pluralized fruit names
  144.                     are allowed--we want to try to avoid
  145.                     adding a redundant plural suffix */
  146.                  Strcpy(buf, makeplural(makesingular(buf)));
  147.                  pluralize = FALSE;
  148.              }
  149.              break;
  150.          }
  151.          if (Is_pudding(obj)) {
  152.              Sprintf(buf, "%s%s",
  153.                      (obj->owt < 100)
  154.                         ? "small "
  155.                         : (obj->owt > 500)
  156.                            ? "very large "
  157.                            : (obj->owt > 300)
  158.                               ? "large "
  159.                               : "",
  160.                      actualn);
  161.              break;
  162.          }
  163.  
  164.          Strcpy(buf, actualn);
  165.          if (typ == TIN && known)
  166.              tin_details(obj, omndx, buf);
  167.          break;
  168.      case COIN_CLASS:
  169.      case CHAIN_CLASS:
  170.          Strcpy(buf, actualn);
  171.          break;
  172.      case ROCK_CLASS:
  173.          if (typ == STATUE && omndx != NON_PM)
  174.              Sprintf(buf, "%s%s of %s%s",
  175.                      (Role_if(PM_ARCHEOLOGIST) && (obj->spe & STATUE_HISTORIC))
  176.                         ? "historic "
  177.                         : "",
  178.                      actualn,
  179.                      type_is_pname(&mons[omndx])
  180.                         ? ""
  181.                         : the_unique_pm(&mons[omndx])
  182.                            ? "the "
  183.                            : index(vowels, *mons[omndx].mname)
  184.                               ? "an "
  185.                               : "a ",
  186.                      mons[omndx].mname);
  187.          else
  188.              Strcpy(buf, actualn);
  189.          break;
  190.      case BALL_CLASS:
  191.          Sprintf(buf, "%sheavy iron ball",
  192.                  (obj->owt > ocl->oc_weight) ? "very " : "");
  193.          break;
  194.      case POTION_CLASS:
  195.          if (dknown && obj->odiluted)
  196.              Strcpy(buf, "diluted ");
  197.          if (nn || un || !dknown) {
  198.              Strcat(buf, "potion");
  199.              if (!dknown)
  200.                  break;
  201.              if (nn) {
  202.                  Strcat(buf, " of ");
  203.                  if (typ == POT_WATER && bknown
  204.                      && (obj->blessed || obj->cursed)) {
  205.                      Strcat(buf, obj->blessed ? "holy " : "unholy ");
  206.                  }
  207.                  Strcat(buf, actualn);
  208.              } else {
  209.                  Strcat(buf, " called ");
  210.                  Strcat(buf, un);
  211.              }
  212.          } else {
  213.              Strcat(buf, dn);
  214.              Strcat(buf, " potion");
  215.          }
  216.          break;
  217.      case SCROLL_CLASS:
  218.          Strcpy(buf, "scroll");
  219.          if (!dknown)
  220.              break;
  221.          if (nn) {
  222.              Strcat(buf, " of ");
  223.              Strcat(buf, actualn);
  224.          } else if (un) {
  225.              Strcat(buf, " called ");
  226.              Strcat(buf, un);
  227.          } else if (ocl->oc_magic) {
  228.              Strcat(buf, " labeled ");
  229.              Strcat(buf, dn);
  230.          } else {
  231.              Strcpy(buf, dn);
  232.              Strcat(buf, " scroll");
  233.          }
  234.          break;
  235.      case WAND_CLASS:
  236.          if (!dknown)
  237.              Strcpy(buf, "wand");
  238.          else if (nn)
  239.              Sprintf(buf, "wand of %s", actualn);
  240.          else if (un)
  241.              Sprintf(buf, "wand called %s", un);
  242.          else
  243.              Sprintf(buf, "%s wand", dn);
  244.          break;
  245.      case SPBOOK_CLASS:
  246.          if (typ == SPE_NOVEL) { /* 3.6 tribute */
  247.              if (!dknown)
  248.                  Strcpy(buf, "book");
  249.              else if (nn)
  250.                  Strcpy(buf, actualn);
  251.              else if (un)
  252.                  Sprintf(buf, "novel called %s", un);
  253.              else
  254.                  Sprintf(buf, "%s book", dn);
  255.              break;
  256.              /* end of tribute */
  257.          } else if (!dknown) {
  258.              Strcpy(buf, "spellbook");
  259.          } else if (nn) {
  260.              if (typ != SPE_BOOK_OF_THE_DEAD)
  261.                  Strcpy(buf, "spellbook of ");
  262.              Strcat(buf, actualn);
  263.          } else if (un) {
  264.              Sprintf(buf, "spellbook called %s", un);
  265.          } else
  266.              Sprintf(buf, "%s spellbook", dn);
  267.          break;
  268.      case RING_CLASS:
  269.          if (!dknown)
  270.              Strcpy(buf, "ring");
  271.          else if (nn)
  272.              Sprintf(buf, "ring of %s", actualn);
  273.          else if (un)
  274.              Sprintf(buf, "ring called %s", un);
  275.          else
  276.              Sprintf(buf, "%s ring", dn);
  277.          break;
  278.      case GEM_CLASS: {
  279.          const char *rock = (ocl->oc_material == MINERAL) ? "stone" : "gem";
  280.  
  281.          if (!dknown) {
  282.              Strcpy(buf, rock);
  283.          } else if (!nn) {
  284.              if (un)
  285.                  Sprintf(buf, "%s called %s", rock, un);
  286.              else
  287.                  Sprintf(buf, "%s %s", dn, rock);
  288.          } else {
  289.              Strcpy(buf, actualn);
  290.              if (GemStone(typ))
  291.                  Strcat(buf, " stone");
  292.          }
  293.          break;
  294.      }
  295.      default:
  296.          Sprintf(buf, "glorkum %d %d %d", obj->oclass, typ, obj->spe);
  297.      }
  298.      if (pluralize)
  299.          Strcpy(buf, makeplural(buf));
  300.  
  301.      if (obj->otyp == T_SHIRT && program_state.gameover) {
  302.          char tmpbuf[BUFSZ];
  303.  
  304.          Sprintf(eos(buf), " with text \"%s\"", tshirt_text(obj, tmpbuf));
  305.      }
  306.  
  307.      if (has_oname(obj) && dknown) {
  308.          Strcat(buf, " named ");
  309.      nameit:
  310.          Strcat(buf, ONAME(obj));
  311.      }
  312.  
  313.      if (!strncmpi(buf, "the ", 4))
  314.          buf += 4;
  315.      return buf;
  316.  }
  317.  

minimal_xname

  1.  /* similar to simple_typename but minimal_xname operates on a particular
  2.     object rather than its general type; it formats the most basic info:
  3.       potion                     -- if description not known
  4.       brown potion               -- if oc_name_known not set
  5.       potion of object detection -- if discovered
  6.   */
  7.  static char *
  8.  minimal_xname(obj)
  9.  struct obj *obj;
  10.  {
  11.      char *bufp;
  12.      struct obj bareobj;
  13.      struct objclass saveobcls;
  14.      int otyp = obj->otyp;
  15.  
  16.      /* suppress user-supplied name */
  17.      saveobcls.oc_uname = objects[otyp].oc_uname;
  18.      objects[otyp].oc_uname = 0;
  19.      /* suppress actual name if object's description is unknown */
  20.      saveobcls.oc_name_known = objects[otyp].oc_name_known;
  21.      if (!obj->dknown)
  22.          objects[otyp].oc_name_known = 0;
  23.  
  24.      /* caveat: this makes a lot of assumptions about which fields
  25.         are required in order for xname() to yield a sensible result */
  26.      bareobj = zeroobj;
  27.      bareobj.otyp = otyp;
  28.      bareobj.oclass = obj->oclass;
  29.      bareobj.dknown = obj->dknown;
  30.      /* suppress known except for amulets (needed for fakes and real A-of-Y) */
  31.      bareobj.known = (obj->oclass == AMULET_CLASS)
  32.                          ? obj->known
  33.                          /* default is "on" for types which don't use it */
  34.                          : !objects[otyp].oc_uses_known;
  35.      bareobj.quan = 1L;         /* don't want plural */
  36.      bareobj.corpsenm = NON_PM; /* suppress statue and figurine details */
  37.      /* but suppressing fruit details leads to "bad fruit #0"
  38.         [perhaps we should force "slime mold" rather than use xname?] */
  39.      if (obj->otyp == SLIME_MOLD)
  40.          bareobj.spe = obj->spe;
  41.  
  42.      bufp = distant_name(&bareobj, xname); /* xname(&bareobj) */
  43.      if (!strncmp(bufp, "uncursed ", 9))
  44.          bufp += 9; /* Role_if(PM_PRIEST) */
  45.  
  46.      objects[otyp].oc_uname = saveobcls.oc_uname;
  47.      objects[otyp].oc_name_known = saveobcls.oc_name_known;
  48.      return bufp;
  49.  }
  50.  

mshot_xname

  1.  /* xname() output augmented for multishot missile feedback */
  2.  char *
  3.  mshot_xname(obj)
  4.  struct obj *obj;
  5.  {
  6.      char tmpbuf[BUFSZ];
  7.      char *onm = xname(obj);
  8.  
  9.      if (m_shot.n > 1 && m_shot.o == obj->otyp) {
  10.          /* "the Nth arrow"; value will eventually be passed to an() or
  11.             The(), both of which correctly handle this "the " prefix */
  12.          Sprintf(tmpbuf, "the %d%s ", m_shot.i, ordin(m_shot.i));
  13.          onm = strprepend(onm, tmpbuf);
  14.      }
  15.      return onm;
  16.  }
  17.  

the_unique_obj

  1.  /* used for naming "the unique_item" instead of "a unique_item" */
  2.  boolean
  3.  the_unique_obj(obj)
  4.  struct obj *obj;
  5.  {
  6.      boolean known = (obj->known || iflags.override_ID);
  7.  
  8.      if (!obj->dknown && !iflags.override_ID)
  9.          return FALSE;
  10.      else if (obj->otyp == FAKE_AMULET_OF_YENDOR && !known)
  11.          return TRUE; /* lie */
  12.      else
  13.          return (boolean) (objects[obj->otyp].oc_unique
  14.                            && (known || obj->otyp == AMULET_OF_YENDOR));
  15.  }
  16.  

the_unique_pm

  1.  /* should monster type be prefixed with "the"? (mostly used for corpses) */
  2.  boolean
  3.  the_unique_pm(ptr)
  4.  struct permonst *ptr;
  5.  {
  6.      boolean uniq;
  7.  
  8.      /* even though monsters with personal names are unique, we want to
  9.         describe them as "Name" rather than "the Name" */
  10.      if (type_is_pname(ptr))
  11.          return FALSE;
  12.  
  13.      uniq = (ptr->geno & G_UNIQ) ? TRUE : FALSE;
  14.      /* high priest is unique if it includes "of <deity>", otherwise not
  15.         (caller needs to handle the 1st possibility; we assume the 2nd);
  16.         worm tail should be irrelevant but is included for completeness */
  17.      if (ptr == &mons[PM_HIGH_PRIEST] || ptr == &mons[PM_LONG_WORM_TAIL])
  18.          uniq = FALSE;
  19.      /* Wizard no longer needs this; he's flagged as unique these days */
  20.      if (ptr == &mons[PM_WIZARD_OF_YENDOR])
  21.          uniq = TRUE;
  22.      return uniq;
  23.  }
  24.  

add_erosion_words

  1.  STATIC_OVL void
  2.  add_erosion_words(obj, prefix)
  3.  struct obj *obj;
  4.  char *prefix;
  5.  {
  6.      boolean iscrys = (obj->otyp == CRYSKNIFE);
  7.      boolean rknown;
  8.  
  9.      rknown = (iflags.override_ID == 0) ? obj->rknown : TRUE;
  10.  
  11.      if (!is_damageable(obj) && !iscrys)
  12.          return;
  13.  
  14.      /* The only cases where any of these bits do double duty are for
  15.       * rotted food and diluted potions, which are all not is_damageable().
  16.       */
  17.      if (obj->oeroded && !iscrys) {
  18.          switch (obj->oeroded) {
  19.          case 2:
  20.              Strcat(prefix, "very ");
  21.              break;
  22.          case 3:
  23.              Strcat(prefix, "thoroughly ");
  24.              break;
  25.          }
  26.          Strcat(prefix, is_rustprone(obj) ? "rusty " : "burnt ");
  27.      }
  28.      if (obj->oeroded2 && !iscrys) {
  29.          switch (obj->oeroded2) {
  30.          case 2:
  31.              Strcat(prefix, "very ");
  32.              break;
  33.          case 3:
  34.              Strcat(prefix, "thoroughly ");
  35.              break;
  36.          }
  37.          Strcat(prefix, is_corrodeable(obj) ? "corroded " : "rotted ");
  38.      }
  39.      if (rknown && obj->oerodeproof)
  40.          Strcat(prefix, iscrys
  41.                            ? "fixed "
  42.                            : is_rustprone(obj)
  43.                               ? "rustproof "
  44.                               : is_corrodeable(obj)
  45.                                  ? "corrodeproof " /* "stainless"? */
  46.                                  : is_flammable(obj)
  47.                                     ? "fireproof "
  48.                                     : "");
  49.  }
  50.  

doname_base

  1.  static char *
  2.  doname_base(obj, with_price)
  3.  register struct obj *obj;
  4.  boolean with_price;
  5.  {
  6.      boolean ispoisoned = FALSE;
  7.      boolean known, cknown, bknown, lknown;
  8.      int omndx = obj->corpsenm;
  9.      char prefix[PREFIX];
  10.      char tmpbuf[PREFIX + 1]; /* for when we have to add something at
  11.                                  the start of prefix instead of the
  12.                                  end (Strcat is used on the end) */
  13.      register char *bp = xname(obj);
  14.  
  15.      if (iflags.override_ID) {
  16.          known = cknown = bknown = lknown = TRUE;
  17.      } else {
  18.          known = obj->known;
  19.          cknown = obj->cknown;
  20.          bknown = obj->bknown;
  21.          lknown = obj->lknown;
  22.      }
  23.  
  24.      /* When using xname, we want "poisoned arrow", and when using
  25.       * doname, we want "poisoned +0 arrow".  This kludge is about the only
  26.       * way to do it, at least until someone overhauls xname() and doname(),
  27.       * combining both into one function taking a parameter.
  28.       */
  29.      /* must check opoisoned--someone can have a weirdly-named fruit */
  30.      if (!strncmp(bp, "poisoned ", 9) && obj->opoisoned) {
  31.          bp += 9;
  32.          ispoisoned = TRUE;
  33.      }
  34.  
  35.      if (obj->quan != 1L) {
  36.          Sprintf(prefix, "%ld ", obj->quan);
  37.      } else if (obj->otyp == CORPSE) {
  38.          /* skip article prefix for corpses [else corpse_xname()
  39.             would have to be taught how to strip it off again] */
  40.          *prefix = '\0';
  41.      } else if (obj_is_pname(obj) || the_unique_obj(obj)) {
  42.          if (!strncmpi(bp, "the ", 4))
  43.              bp += 4;
  44.          Strcpy(prefix, "the ");
  45.      } else {
  46.          Strcpy(prefix, "a ");
  47.      }
  48.  
  49.      /* "empty" goes at the beginning, but item count goes at the end */
  50.      if (cknown
  51.          /* bag of tricks: include "empty" prefix if it's known to
  52.             be empty but its precise number of charges isn't known
  53.             (when that is known, suffix of "(n:0)" will be appended,
  54.             making the prefix be redundant; note that 'known' flag
  55.             isn't set when emptiness gets discovered because then
  56.             charging magic would yield known number of new charges) */
  57.          && (obj->otyp == BAG_OF_TRICKS
  58.               ? (obj->spe == 0 && !obj->known)
  59.               /* not bag of tricks: empty if container which has no contents */
  60.               : (Is_container(obj) || obj->otyp == STATUE)
  61.              && !Has_contents(obj)))
  62.          Strcat(prefix, "empty ");
  63.  
  64.      if (bknown && obj->oclass != COIN_CLASS
  65.          && (obj->otyp != POT_WATER || !objects[POT_WATER].oc_name_known
  66.              || (!obj->cursed && !obj->blessed))) {
  67.          /* allow 'blessed clear potion' if we don't know it's holy water;
  68.           * always allow "uncursed potion of water"
  69.           */
  70.          if (obj->cursed)
  71.              Strcat(prefix, "cursed ");
  72.          else if (obj->blessed)
  73.              Strcat(prefix, "blessed ");
  74.          else if (!iflags.implicit_uncursed
  75.              /* For most items with charges or +/-, if you know how many
  76.               * charges are left or what the +/- is, then you must have
  77.               * totally identified the item, so "uncursed" is unnecessary,
  78.               * because an identified object not described as "blessed" or
  79.               * "cursed" must be uncursed.
  80.               *
  81.               * If the charges or +/- is not known, "uncursed" must be
  82.               * printed to avoid ambiguity between an item whose curse
  83.               * status is unknown, and an item known to be uncursed.
  84.               */
  85.                   || ((!known || !objects[obj->otyp].oc_charged
  86.                        || obj->oclass == ARMOR_CLASS
  87.                        || obj->oclass == RING_CLASS)
  88.  #ifdef MAIL
  89.                       && obj->otyp != SCR_MAIL
  90.  #endif
  91.                       && obj->otyp != FAKE_AMULET_OF_YENDOR
  92.                       && obj->otyp != AMULET_OF_YENDOR
  93.                       && !Role_if(PM_PRIEST)))
  94.              Strcat(prefix, "uncursed ");
  95.      }
  96.  
  97.      if (lknown && Is_box(obj)) {
  98.          if (obj->obroken)
  99.              Strcat(prefix, "unlockable ");
  100.          else if (obj->olocked)
  101.              Strcat(prefix, "locked ");
  102.          else
  103.              Strcat(prefix, "unlocked ");
  104.      }
  105.  
  106.      if (obj->greased)
  107.          Strcat(prefix, "greased ");
  108.  
  109.      if (cknown && Has_contents(obj)) {
  110.          /* we count all objects (obj->quantity); perhaps we should
  111.             count separate stacks instead (or even introduce a user
  112.             preference option to choose between the two alternatives)
  113.             since it's somewhat odd so see "containing 1002 items"
  114.             when there are 2 scrolls plus 1000 gold pieces */
  115.          long itemcount = count_contents(obj, FALSE, FALSE, TRUE);
  116.  
  117.          Sprintf(eos(bp), " containing %ld item%s", itemcount,
  118.                  plur(itemcount));
  119.      }
  120.  
  121.      switch (obj->oclass) {
  122.      case AMULET_CLASS:
  123.          if (obj->owornmask & W_AMUL)
  124.              Strcat(bp, " (being worn)");
  125.          break;
  126.      case WEAPON_CLASS:
  127.          if (ispoisoned)
  128.              Strcat(prefix, "poisoned ");
  129.      plus:
  130.          add_erosion_words(obj, prefix);
  131.          if (known) {
  132.              Strcat(prefix, sitoa(obj->spe));
  133.              Strcat(prefix, " ");
  134.          }
  135.          break;
  136.      case ARMOR_CLASS:
  137.          if (obj->owornmask & W_ARMOR)
  138.              Strcat(bp, (obj == uskin) ? " (embedded in your skin)"
  139.                                        : " (being worn)");
  140.          goto plus;
  141.      case TOOL_CLASS:
  142.          /* weptools already get this done when we go to the +n code */
  143.          if (!is_weptool(obj))
  144.              add_erosion_words(obj, prefix);
  145.          if (obj->owornmask & (W_TOOL /* blindfold */ | W_SADDLE)) {
  146.              Strcat(bp, " (being worn)");
  147.              break;
  148.          }
  149.          if (obj->otyp == LEASH && obj->leashmon != 0) {
  150.              Strcat(bp, " (in use)");
  151.              break;
  152.          }
  153.          if (is_weptool(obj))
  154.              goto plus;
  155.          if (obj->otyp == CANDELABRUM_OF_INVOCATION) {
  156.              if (!obj->spe)
  157.                  Strcpy(tmpbuf, "no");
  158.              else
  159.                  Sprintf(tmpbuf, "%d", obj->spe);
  160.              Sprintf(eos(bp), " (%s candle%s%s)", tmpbuf, plur(obj->spe),
  161.                      !obj->lamplit ? " attached" : ", lit");
  162.              break;
  163.          } else if (obj->otyp == OIL_LAMP || obj->otyp == MAGIC_LAMP
  164.                     || obj->otyp == BRASS_LANTERN || Is_candle(obj)) {
  165.              if (Is_candle(obj)
  166.                  && obj->age < 20L * (long) objects[obj->otyp].oc_cost)
  167.                  Strcat(prefix, "partly used ");
  168.              if (obj->lamplit)
  169.                  Strcat(bp, " (lit)");
  170.              break;
  171.          }
  172.          if (objects[obj->otyp].oc_charged)
  173.              goto charges;
  174.          break;
  175.      case WAND_CLASS:
  176.          add_erosion_words(obj, prefix);
  177.      charges:
  178.          if (known)
  179.              Sprintf(eos(bp), " (%d:%d)", (int) obj->recharged, obj->spe);
  180.          break;
  181.      case POTION_CLASS:
  182.          if (obj->otyp == POT_OIL && obj->lamplit)
  183.              Strcat(bp, " (lit)");
  184.          break;
  185.      case RING_CLASS:
  186.          add_erosion_words(obj, prefix);
  187.      ring:
  188.          if (obj->owornmask & W_RINGR)
  189.              Strcat(bp, " (on right ");
  190.          if (obj->owornmask & W_RINGL)
  191.              Strcat(bp, " (on left ");
  192.          if (obj->owornmask & W_RING) {
  193.              Strcat(bp, body_part(HAND));
  194.              Strcat(bp, ")");
  195.          }
  196.          if (known && objects[obj->otyp].oc_charged) {
  197.              Strcat(prefix, sitoa(obj->spe));
  198.              Strcat(prefix, " ");
  199.          }
  200.          break;
  201.      case FOOD_CLASS:
  202.          if (obj->oeaten)
  203.              Strcat(prefix, "partly eaten ");
  204.          if (obj->otyp == CORPSE) {
  205.              Sprintf(prefix, "%s ",
  206.                      corpse_xname(obj, prefix, CXN_ARTICLE | CXN_NOCORPSE));
  207.          } else if (obj->otyp == EGG) {
  208.  #if 0 /* corpses don't tell if they're stale either */
  209.              if (known && stale_egg(obj))
  210.                  Strcat(prefix, "stale ");
  211.  #endif
  212.              if (omndx >= LOW_PM
  213.                  && (known || (mvitals[omndx].mvflags & MV_KNOWS_EGG))) {
  214.                  Strcat(prefix, mons[omndx].mname);
  215.                  Strcat(prefix, " ");
  216.                  if (obj->spe)
  217.                      Strcat(bp, " (laid by you)");
  218.              }
  219.          }
  220.          if (obj->otyp == MEAT_RING)
  221.              goto ring;
  222.          break;
  223.      case BALL_CLASS:
  224.      case CHAIN_CLASS:
  225.          add_erosion_words(obj, prefix);
  226.          if (obj->owornmask & W_BALL)
  227.              Strcat(bp, " (chained to you)");
  228.          break;
  229.      }
  230.  
  231.      if ((obj->owornmask & W_WEP) && !mrg_to_wielded) {
  232.          if (obj->quan != 1L) {
  233.              Strcat(bp, " (wielded)");
  234.          } else {
  235.              const char *hand_s = body_part(HAND);
  236.  
  237.              if (bimanual(obj))
  238.                  hand_s = makeplural(hand_s);
  239.              Sprintf(eos(bp), " (weapon in %s)", hand_s);
  240.  
  241.              if (warn_obj_cnt && obj == uwep && (EWarn_of_mon & W_WEP) != 0L) {
  242.                  /* presumably can be felt when blind */
  243.                  Strcat(bp, " (glowing");
  244.                  if (!Blind)
  245.                      Sprintf(eos(bp), " %s", glow_color(obj->oartifact));
  246.                  Strcat(bp, ")");
  247.              }
  248.          }
  249.      }
  250.      if (obj->owornmask & W_SWAPWEP) {
  251.          if (u.twoweap)
  252.              Sprintf(eos(bp), " (wielded in other %s)", body_part(HAND));
  253.          else
  254.              Strcat(bp, " (alternate weapon; not wielded)");
  255.      }
  256.      if (obj->owornmask & W_QUIVER) {
  257.          switch (obj->oclass) {
  258.          case WEAPON_CLASS:
  259.              if (is_ammo(obj)) {
  260.                  if (objects[obj->otyp].oc_skill == -P_BOW) {
  261.                      /* Ammo for a bow */
  262.                      Strcat(bp, " (in quiver)");
  263.                      break;
  264.                  } else {
  265.                      /* Ammo not for a bow */
  266.                      Strcat(bp, " (in quiver pouch)");
  267.                      break;
  268.                  }
  269.              } else {
  270.                  /* Weapons not considered ammo */
  271.                  Strcat(bp, " (at the ready)");
  272.                  break;
  273.              }
  274.          /* Small things and ammo not for a bow */
  275.          case RING_CLASS:
  276.          case AMULET_CLASS:
  277.          case WAND_CLASS:
  278.          case COIN_CLASS:
  279.          case GEM_CLASS:
  280.              Strcat(bp, " (in quiver pouch)");
  281.              break;
  282.          default: /* odd things */
  283.              Strcat(bp, " (at the ready)");
  284.          }
  285.      }
  286.      if (!iflags.suppress_price && is_unpaid(obj)) {
  287.          long quotedprice = unpaid_cost(obj, TRUE);
  288.  
  289.          Sprintf(eos(bp), " (%s, %ld %s)",
  290.                  obj->unpaid ? "unpaid" : "contents",
  291.                  quotedprice, currency(quotedprice));
  292.      } else if (with_price) {
  293.          long price = get_cost_of_shop_item(obj);
  294.  
  295.          if (price > 0)
  296.              Sprintf(eos(bp), " (%ld %s)", price, currency(price));
  297.      }
  298.      if (!strncmp(prefix, "a ", 2)
  299.          && index(vowels, *(prefix + 2) ? *(prefix + 2) : *bp)
  300.          && (*(prefix + 2)
  301.              || (strncmp(bp, "uranium", 7) && strncmp(bp, "unicorn", 7)
  302.                  && strncmp(bp, "eucalyptus", 10)))) {
  303.          Strcpy(tmpbuf, prefix);
  304.          Strcpy(prefix, "an ");
  305.          Strcpy(prefix + 3, tmpbuf + 2);
  306.      }
  307.  
  308.      /* show weight for items (debug tourist info)
  309.       * aum is stolen from Crawl's "Arbitrary Unit of Measure" */
  310.      if (wizard) {
  311.          Sprintf(eos(bp), " (%d aum)", obj->owt);
  312.      }
  313.      bp = strprepend(bp, prefix);
  314.      return bp;
  315.  }
  316.  

doname

  1.  char *
  2.  doname(obj)
  3.  register struct obj *obj;
  4.  {
  5.      return doname_base(obj, FALSE);
  6.  }
  7.  

doname_with_price

  1.  /* Name of object including price. */
  2.  char *
  3.  doname_with_price(obj)
  4.  register struct obj *obj;
  5.  {
  6.      return doname_base(obj, TRUE);
  7.  }
  8.  

not_fully_identified

  1.  /* used from invent.c */
  2.  boolean
  3.  not_fully_identified(otmp)
  4.  register struct obj *otmp;
  5.  {
  6.      /* gold doesn't have any interesting attributes [yet?] */
  7.      if (otmp->oclass == COIN_CLASS)
  8.          return FALSE; /* always fully ID'd */
  9.      /* check fundamental ID hallmarks first */
  10.      if (!otmp->known || !otmp->dknown
  11.  #ifdef MAIL
  12.          || (!otmp->bknown && otmp->otyp != SCR_MAIL)
  13.  #else
  14.          || !otmp->bknown
  15.  #endif
  16.          || !objects[otmp->otyp].oc_name_known)
  17.          return TRUE;
  18.      if ((!otmp->cknown && (Is_container(otmp) || otmp->otyp == STATUE))
  19.          || (!otmp->lknown && Is_box(otmp)))
  20.          return TRUE;
  21.      if (otmp->oartifact && undiscovered_artifact(otmp->oartifact))
  22.          return TRUE;
  23.      /* otmp->rknown is the only item of interest if we reach here */
  24.      /*
  25.       *  Note:  if a revision ever allows scrolls to become fireproof or
  26.       *  rings to become shockproof, this checking will need to be revised.
  27.       *  `rknown' ID only matters if xname() will provide the info about it.
  28.       */
  29.      if (otmp->rknown
  30.          || (otmp->oclass != ARMOR_CLASS && otmp->oclass != WEAPON_CLASS
  31.              && !is_weptool(otmp)            /* (redundant) */
  32.              && otmp->oclass != BALL_CLASS)) /* (useless) */
  33.          return FALSE;
  34.      else /* lack of `rknown' only matters for vulnerable objects */
  35.          return (boolean) (is_rustprone(otmp) || is_corrodeable(otmp)
  36.                            || is_flammable(otmp));
  37.  }
  38.  

corpse_xname

  1.  char *
  2.  corpse_xname(otmp, adjective, cxn_flags)
  3.  struct obj *otmp;
  4.  const char *adjective;
  5.  unsigned cxn_flags; /* bitmask of CXN_xxx values */
  6.  {
  7.      char *nambuf = nextobuf();
  8.      int omndx = otmp->corpsenm;
  9.      boolean ignore_quan = (cxn_flags & CXN_SINGULAR) != 0,
  10.              /* suppress "the" from "the unique monster corpse" */
  11.          no_prefix = (cxn_flags & CXN_NO_PFX) != 0,
  12.              /* include "the" for "the woodchuck corpse */
  13.          the_prefix = (cxn_flags & CXN_PFX_THE) != 0,
  14.              /* include "an" for "an ogre corpse */
  15.          any_prefix = (cxn_flags & CXN_ARTICLE) != 0,
  16.              /* leave off suffix (do_name() appends "corpse" itself) */
  17.          omit_corpse = (cxn_flags & CXN_NOCORPSE) != 0, possessive = FALSE;
  18.      const char *mname;
  19.  
  20.      if (omndx == NON_PM) { /* paranoia */
  21.          mname = "thing";
  22.          /* [Possible enhancement:  check whether corpse has monster traits
  23.              attached in order to use priestname() for priests and minions.] */
  24.      } else if (omndx == PM_ALIGNED_PRIEST) {
  25.          /* avoid "aligned priest"; it just exposes internal details */
  26.          mname = "priest";
  27.      } else {
  28.          mname = mons[omndx].mname;
  29.          if (the_unique_pm(&mons[omndx]) || type_is_pname(&mons[omndx])) {
  30.              mname = s_suffix(mname);
  31.              possessive = TRUE;
  32.              /* don't precede personal name like "Medusa" with an article */
  33.              if (type_is_pname(&mons[omndx]))
  34.                  no_prefix = TRUE;
  35.              /* always precede non-personal unique monster name like
  36.                 "Oracle" with "the" unless explicitly overridden */
  37.              else if (the_unique_pm(&mons[omndx]) && !no_prefix)
  38.                  the_prefix = TRUE;
  39.          }
  40.      }
  41.      if (no_prefix)
  42.          the_prefix = any_prefix = FALSE;
  43.      else if (the_prefix)
  44.          any_prefix = FALSE; /* mutually exclusive */
  45.  
  46.      *nambuf = '\0';
  47.      /* can't use the() the way we use an() below because any capitalized
  48.         Name causes it to assume a personal name and return Name as-is;
  49.         that's usually the behavior wanted, but here we need to force "the"
  50.         to precede capitalized unique monsters (pnames are handled above) */
  51.      if (the_prefix)
  52.          Strcat(nambuf, "the ");
  53.  
  54.      if (!adjective || !*adjective) {
  55.          /* normal case:  newt corpse */
  56.          Strcat(nambuf, mname);
  57.      } else {
  58.          /* adjective positioning depends upon format of monster name */
  59.          if (possessive) /* Medusa's cursed partly eaten corpse */
  60.              Sprintf(eos(nambuf), "%s %s", mname, adjective);
  61.          else /* cursed partly eaten troll corpse */
  62.              Sprintf(eos(nambuf), "%s %s", adjective, mname);
  63.          /* in case adjective has a trailing space, squeeze it out */
  64.          mungspaces(nambuf);
  65.          /* doname() might include a count in the adjective argument;
  66.             if so, don't prepend an article */
  67.          if (digit(*adjective))
  68.              any_prefix = FALSE;
  69.      }
  70.  
  71.      if (!omit_corpse) {
  72.          Strcat(nambuf, " corpse");
  73.          /* makeplural(nambuf) => append "s" to "corpse" */
  74.          if (otmp->quan > 1L && !ignore_quan) {
  75.              Strcat(nambuf, "s");
  76.              any_prefix = FALSE; /* avoid "a newt corpses" */
  77.          }
  78.      }
  79.  
  80.      /* it's safe to overwrite our nambuf after an() has copied
  81.         its old value into another buffer */
  82.      if (any_prefix)
  83.          Strcpy(nambuf, an(nambuf));
  84.  
  85.      return nambuf;
  86.  }
  87.  

cxname

  1.  /* xname doesn't include monster type for "corpse"; cxname does */
  2.  char *
  3.  cxname(obj)
  4.  struct obj *obj;
  5.  {
  6.      if (obj->otyp == CORPSE)
  7.          return corpse_xname(obj, (const char *) 0, CXN_NORMAL);
  8.      return xname(obj);
  9.  }
  10.  

cxname_singular

  1.  /* like cxname, but ignores quantity */
  2.  char *
  3.  cxname_singular(obj)
  4.  struct obj *obj;
  5.  {
  6.      if (obj->otyp == CORPSE)
  7.          return corpse_xname(obj, (const char *) 0, CXN_SINGULAR);
  8.      return xname_flags(obj, CXN_SINGULAR);
  9.  }
  10.  

killer_xname

  1.  /* treat an object as fully ID'd when it might be used as reason for death */
  2.  char *
  3.  killer_xname(obj)
  4.  struct obj *obj;
  5.  {
  6.      struct obj save_obj;
  7.      unsigned save_ocknown;
  8.      char *buf, *save_ocuname, *save_oname = (char *) 0;
  9.  
  10.      /* bypass object twiddling for artifacts */
  11.      if (obj->oartifact)
  12.          return bare_artifactname(obj);
  13.  
  14.      /* remember original settings for core of the object;
  15.         oextra structs other than oname don't matter here--since they
  16.         aren't modified they don't need to be saved and restored */
  17.      save_obj = *obj;
  18.      if (has_oname(obj))
  19.          save_oname = ONAME(obj);
  20.  
  21.      /* killer name should be more specific than general xname; however, exact
  22.         info like blessed/cursed and rustproof makes things be too verbose */
  23.      obj->known = obj->dknown = 1;
  24.      obj->bknown = obj->rknown = obj->greased = 0;
  25.      /* if character is a priest[ess], bknown will get toggled back on */
  26.      if (obj->otyp != POT_WATER)
  27.          obj->blessed = obj->cursed = 0;
  28.      else
  29.          obj->bknown = 1; /* describe holy/unholy water as such */
  30.      /* "killed by poisoned <obj>" would be misleading when poison is
  31.         not the cause of death and "poisoned by poisoned <obj>" would
  32.         be redundant when it is, so suppress "poisoned" prefix */
  33.      obj->opoisoned = 0;
  34.      /* strip user-supplied name; artifacts keep theirs */
  35.      if (!obj->oartifact && save_oname)
  36.          ONAME(obj) = (char *) 0;
  37.      /* temporarily identify the type of object */
  38.      save_ocknown = objects[obj->otyp].oc_name_known;
  39.      objects[obj->otyp].oc_name_known = 1;
  40.      save_ocuname = objects[obj->otyp].oc_uname;
  41.      objects[obj->otyp].oc_uname = 0; /* avoid "foo called bar" */
  42.  
  43.      /* format the object */
  44.      if (obj->otyp == CORPSE) {
  45.          buf = nextobuf();
  46.          Strcpy(buf, corpse_xname(obj, (const char *) 0, CXN_NORMAL));
  47.      } else if (obj->otyp == SLIME_MOLD) {
  48.          /* concession to "most unique deaths competition" in the annual
  49.             devnull tournament, suppress player supplied fruit names because
  50.             those can be used to fake other objects and dungeon features */
  51.          buf = nextobuf();
  52.          Sprintf(buf, "deadly slime mold%s", plur(obj->quan));
  53.      } else {
  54.          buf = xname(obj);
  55.      }
  56.      /* apply an article if appropriate; caller should always use KILLED_BY */
  57.      if (obj->quan == 1L && !strstri(buf, "'s ") && !strstri(buf, "s' "))
  58.          buf = (obj_is_pname(obj) || the_unique_obj(obj)) ? the(buf) : an(buf);
  59.  
  60.      objects[obj->otyp].oc_name_known = save_ocknown;
  61.      objects[obj->otyp].oc_uname = save_ocuname;
  62.      *obj = save_obj; /* restore object's core settings */
  63.      if (!obj->oartifact && save_oname)
  64.          ONAME(obj) = save_oname;
  65.  
  66.      return buf;
  67.  }
  68.  

short_oname

  1.  /* xname,doname,&c with long results reformatted to omit some stuff */
  2.  char *
  3.  short_oname(obj, func, altfunc, lenlimit)
  4.  struct obj *obj;
  5.  char *FDECL((*func), (OBJ_P)),    /* main formatting routine */
  6.       *FDECL((*altfunc), (OBJ_P)); /* alternate for shortest result */
  7.  unsigned lenlimit;
  8.  {
  9.      struct obj save_obj;
  10.      char unamebuf[12], onamebuf[12], *save_oname, *save_uname, *outbuf;
  11.  
  12.      outbuf = (*func)(obj);
  13.      if ((unsigned) strlen(outbuf) <= lenlimit)
  14.          return outbuf;
  15.  
  16.      /* shorten called string to fairly small amount */
  17.      save_uname = objects[obj->otyp].oc_uname;
  18.      if (save_uname && strlen(save_uname) >= sizeof unamebuf) {
  19.          (void) strncpy(unamebuf, save_uname, sizeof unamebuf - 4);
  20.          Strcpy(unamebuf + sizeof unamebuf - 4, "...");
  21.          objects[obj->otyp].oc_uname = unamebuf;
  22.          releaseobuf(outbuf);
  23.          outbuf = (*func)(obj);
  24.          objects[obj->otyp].oc_uname = save_uname; /* restore called string */
  25.          if ((unsigned) strlen(outbuf) <= lenlimit)
  26.              return outbuf;
  27.      }
  28.  
  29.      /* shorten named string to fairly small amount */
  30.      save_oname = has_oname(obj) ? ONAME(obj) : 0;
  31.      if (save_oname && strlen(save_oname) >= sizeof onamebuf) {
  32.          (void) strncpy(onamebuf, save_oname, sizeof onamebuf - 4);
  33.          Strcpy(onamebuf + sizeof onamebuf - 4, "...");
  34.          ONAME(obj) = onamebuf;
  35.          releaseobuf(outbuf);
  36.          outbuf = (*func)(obj);
  37.          ONAME(obj) = save_oname; /* restore named string */
  38.          if ((unsigned) strlen(outbuf) <= lenlimit)
  39.              return outbuf;
  40.      }
  41.  
  42.      /* shorten both called and named strings;
  43.         unamebuf and onamebuf have both already been populated */
  44.      if (save_uname && strlen(save_uname) >= sizeof unamebuf && save_oname
  45.          && strlen(save_oname) >= sizeof onamebuf) {
  46.          objects[obj->otyp].oc_uname = unamebuf;
  47.          ONAME(obj) = onamebuf;
  48.          releaseobuf(outbuf);
  49.          outbuf = (*func)(obj);
  50.          if ((unsigned) strlen(outbuf) <= lenlimit) {
  51.              objects[obj->otyp].oc_uname = save_uname;
  52.              ONAME(obj) = save_oname;
  53.              return outbuf;
  54.          }
  55.      }
  56.  
  57.      /* still long; strip several name-lengthening attributes;
  58.         called and named strings are still in truncated form */
  59.      save_obj = *obj;
  60.      obj->bknown = obj->rknown = obj->greased = 0;
  61.      obj->oeroded = obj->oeroded2 = 0;
  62.      releaseobuf(outbuf);
  63.      outbuf = (*func)(obj);
  64.      if (altfunc && (unsigned) strlen(outbuf) > lenlimit) {
  65.          /* still long; use the alternate function (usually one of
  66.             the jackets around minimal_xname()) */
  67.          releaseobuf(outbuf);
  68.          outbuf = (*altfunc)(obj);
  69.      }
  70.      /* restore the object */
  71.      *obj = save_obj;
  72.      if (save_oname)
  73.          ONAME(obj) = save_oname;
  74.      if (save_uname)
  75.          objects[obj->otyp].oc_uname = save_uname;
  76.  
  77.      /* use whatever we've got, whether it's too long or not */
  78.      return outbuf;
  79.  }
  80.  

singular

  1.  /*
  2.   * Used if only one of a collection of objects is named (e.g. in eat.c).
  3.   */
  4.  const char *
  5.  singular(otmp, func)
  6.  register struct obj *otmp;
  7.  char *FDECL((*func), (OBJ_P));
  8.  {
  9.      long savequan;
  10.      char *nam;
  11.  
  12.      /* using xname for corpses does not give the monster type */
  13.      if (otmp->otyp == CORPSE && func == xname)
  14.          func = cxname;
  15.  
  16.      savequan = otmp->quan;
  17.      otmp->quan = 1L;
  18.      nam = (*func)(otmp);
  19.      otmp->quan = savequan;
  20.      return nam;
  21.  }
  22.  

an

  1.  char *
  2.  an(str)
  3.  register const char *str;
  4.  {
  5.      char *buf = nextobuf();
  6.  
  7.      buf[0] = '\0';
  8.  
  9.      if (strncmpi(str, "the ", 4) && strcmp(str, "molten lava")
  10.          && strcmp(str, "iron bars") && strcmp(str, "ice")) {
  11.          if (index(vowels, *str) && strncmp(str, "one-", 4)
  12.              && strncmp(str, "useful", 6) && strncmp(str, "unicorn", 7)
  13.              && strncmp(str, "uranium", 7) && strncmp(str, "eucalyptus", 10))
  14.              Strcpy(buf, "an ");
  15.          else
  16.              Strcpy(buf, "a ");
  17.      }
  18.  
  19.      Strcat(buf, str);
  20.      return buf;
  21.  }
  22.  

An

  1.  char *
  2.  An(str)
  3.  const char *str;
  4.  {
  5.      char *tmp = an(str);
  6.  
  7.      *tmp = highc(*tmp);
  8.      return tmp;
  9.  }
  10.  

the

  1.  /*
  2.   * Prepend "the" if necessary; assumes str is a subject derived from xname.
  3.   * Use type_is_pname() for monster names, not the().  the() is idempotent.
  4.   */
  5.  char *
  6.  the(str)
  7.  const char *str;
  8.  {
  9.      char *buf = nextobuf();
  10.      boolean insert_the = FALSE;
  11.  
  12.      if (!strncmpi(str, "the ", 4)) {
  13.          buf[0] = lowc(*str);
  14.          Strcpy(&buf[1], str + 1);
  15.          return buf;
  16.      } else if (*str < 'A' || *str > 'Z') {
  17.          /* not a proper name, needs an article */
  18.          insert_the = TRUE;
  19.      } else {
  20.          /* Probably a proper name, might not need an article */
  21.          register char *tmp, *named, *called;
  22.          int l;
  23.  
  24.          /* some objects have capitalized adjectives in their names */
  25.          if (((tmp = rindex(str, ' ')) != 0 || (tmp = rindex(str, '-')) != 0)
  26.              && (tmp[1] < 'A' || tmp[1] > 'Z')) {
  27.              insert_the = TRUE;
  28.          } else if (tmp && index(str, ' ') < tmp) { /* has spaces */
  29.              /* it needs an article if the name contains "of" */
  30.              tmp = strstri(str, " of ");
  31.              named = strstri(str, " named ");
  32.              called = strstri(str, " called ");
  33.              if (called && (!named || called < named))
  34.                  named = called;
  35.  
  36.              if (tmp && (!named || tmp < named)) /* found an "of" */
  37.                  insert_the = TRUE;
  38.              /* stupid special case: lacks "of" but needs "the" */
  39.              else if (!named && (l = strlen(str)) >= 31
  40.                       && !strcmp(&str[l - 31],
  41.                                  "Platinum Yendorian Express Card"))
  42.                  insert_the = TRUE;
  43.          }
  44.      }
  45.      if (insert_the)
  46.          Strcpy(buf, "the ");
  47.      else
  48.          buf[0] = '\0';
  49.      Strcat(buf, str);
  50.  
  51.      return buf;
  52.  }
  53.  

The

  1.  char *
  2.  The(str)
  3.  const char *str;
  4.  {
  5.      char *tmp = the(str);
  6.  
  7.      *tmp = highc(*tmp);
  8.      return tmp;
  9.  }
  10.  

aobjnam

  1.  /* returns "count cxname(otmp)" or just cxname(otmp) if count == 1 */
  2.  char *
  3.  aobjnam(otmp, verb)
  4.  struct obj *otmp;
  5.  const char *verb;
  6.  {
  7.      char prefix[PREFIX];
  8.      char *bp = cxname(otmp);
  9.  
  10.      if (otmp->quan != 1L) {
  11.          Sprintf(prefix, "%ld ", otmp->quan);
  12.          bp = strprepend(bp, prefix);
  13.      }
  14.      if (verb) {
  15.          Strcat(bp, " ");
  16.          Strcat(bp, otense(otmp, verb));
  17.      }
  18.      return bp;
  19.  }
  20.  

yobjnam

  1.  /* combine yname and aobjnam eg "your count cxname(otmp)" */
  2.  char *
  3.  yobjnam(obj, verb)
  4.  struct obj *obj;
  5.  const char *verb;
  6.  {
  7.      char *s = aobjnam(obj, verb);
  8.  
  9.      /* leave off "your" for most of your artifacts, but prepend
  10.       * "your" for unique objects and "foo of bar" quest artifacts */
  11.      if (!carried(obj) || !obj_is_pname(obj)
  12.          || obj->oartifact >= ART_ORB_OF_DETECTION) {
  13.          char *outbuf = shk_your(nextobuf(), obj);
  14.          int space_left = BUFSZ - 1 - strlen(outbuf);
  15.  
  16.          s = strncat(outbuf, s, space_left);
  17.      }
  18.      return s;
  19.  }
  20.  

Yobjnam2

  1.  /* combine Yname2 and aobjnam eg "Your count cxname(otmp)" */
  2.  char *
  3.  Yobjnam2(obj, verb)
  4.  struct obj *obj;
  5.  const char *verb;
  6.  {
  7.      register char *s = yobjnam(obj, verb);
  8.  
  9.      *s = highc(*s);
  10.      return s;
  11.  }
  12.  

Tobjnam

  1.  /* like aobjnam, but prepend "The", not count, and use xname */
  2.  char *
  3.  Tobjnam(otmp, verb)
  4.  struct obj *otmp;
  5.  const char *verb;
  6.  {
  7.      char *bp = The(xname(otmp));
  8.  
  9.      if (verb) {
  10.          Strcat(bp, " ");
  11.          Strcat(bp, otense(otmp, verb));
  12.      }
  13.      return bp;
  14.  }
  15.  

Doname2

  1.  /* capitalized variant of doname() */
  2.  char *
  3.  Doname2(obj)
  4.  struct obj *obj;
  5.  {
  6.      char *s = doname(obj);
  7.  
  8.      *s = highc(*s);
  9.      return s;
  10.  }
  11.  

yname

  1.  /* returns "[your ]xname(obj)" or "Foobar's xname(obj)" or "the xname(obj)" */
  2.  char *
  3.  yname(obj)
  4.  struct obj *obj;
  5.  {
  6.      char *s = cxname(obj);
  7.  
  8.      /* leave off "your" for most of your artifacts, but prepend
  9.       * "your" for unique objects and "foo of bar" quest artifacts */
  10.      if (!carried(obj) || !obj_is_pname(obj)
  11.          || obj->oartifact >= ART_ORB_OF_DETECTION) {
  12.          char *outbuf = shk_your(nextobuf(), obj);
  13.          int space_left = BUFSZ - 1 - strlen(outbuf);
  14.  
  15.          s = strncat(outbuf, s, space_left);
  16.      }
  17.  
  18.      return s;
  19.  }
  20.  

Yname2

  1.  /* capitalized variant of yname() */
  2.  char *
  3.  Yname2(obj)
  4.  struct obj *obj;
  5.  {
  6.      char *s = yname(obj);
  7.  
  8.      *s = highc(*s);
  9.      return s;
  10.  }
  11.  

ysimple_name

  1.  /* returns "your minimal_xname(obj)"
  2.   * or "Foobar's minimal_xname(obj)"
  3.   * or "the minimal_xname(obj)"
  4.   */
  5.  char *
  6.  ysimple_name(obj)
  7.  struct obj *obj;
  8.  {
  9.      char *outbuf = nextobuf();
  10.      char *s = shk_your(outbuf, obj); /* assert( s == outbuf ); */
  11.      int space_left = BUFSZ - 1 - strlen(s);
  12.  
  13.      return strncat(s, minimal_xname(obj), space_left);
  14.  }
  15.  

Ysimple_name2

  1.  /* capitalized variant of ysimple_name() */
  2.  char *
  3.  Ysimple_name2(obj)
  4.  struct obj *obj;
  5.  {
  6.      char *s = ysimple_name(obj);
  7.  
  8.      *s = highc(*s);
  9.      return s;
  10.  }
  11.  

simpleonames

  1.  /* "scroll" or "scrolls" */
  2.  char *
  3.  simpleonames(obj)
  4.  struct obj *obj;
  5.  {
  6.      char *simpleoname = minimal_xname(obj);
  7.  
  8.      if (obj->quan != 1L)
  9.          simpleoname = makeplural(simpleoname);
  10.      return simpleoname;
  11.  }
  12.  

ansimpleoname

  1.  /* "a scroll" or "scrolls"; "a silver bell" or "the Bell of Opening" */
  2.  char *
  3.  ansimpleoname(obj)
  4.  struct obj *obj;
  5.  {
  6.      char *simpleoname = simpleonames(obj);
  7.      int otyp = obj->otyp;
  8.  
  9.      /* prefix with "the" if a unique item, or a fake one imitating same,
  10.         has been formatted with its actual name (we let typename() handle
  11.         any `known' and `dknown' checking necessary) */
  12.      if (otyp == FAKE_AMULET_OF_YENDOR)
  13.          otyp = AMULET_OF_YENDOR;
  14.      if (objects[otyp].oc_unique
  15.          && !strcmp(simpleoname, OBJ_NAME(objects[otyp])))
  16.          return the(simpleoname);
  17.  
  18.      /* simpleoname is singular if quan==1, plural otherwise */
  19.      if (obj->quan == 1L)
  20.          simpleoname = an(simpleoname);
  21.      return simpleoname;
  22.  }
  23.  

thesimpleoname

  1.  /* "the scroll" or "the scrolls" */
  2.  char *
  3.  thesimpleoname(obj)
  4.  struct obj *obj;
  5.  {
  6.      char *simpleoname = simpleonames(obj);
  7.  
  8.      return the(simpleoname);
  9.  }
  10.  

bare_artifactname

  1.  /* artifact's name without any object type or known/dknown/&c feedback */
  2.  char *
  3.  bare_artifactname(obj)
  4.  struct obj *obj;
  5.  {
  6.      char *outbuf;
  7.  
  8.      if (obj->oartifact) {
  9.          outbuf = nextobuf();
  10.          Strcpy(outbuf, artiname(obj->oartifact));
  11.          if (!strncmp(outbuf, "The ", 4))
  12.              outbuf[0] = lowc(outbuf[0]);
  13.      } else {
  14.          outbuf = xname(obj);
  15.      }
  16.      return outbuf;
  17.  }
  18.  


  1.  static const char *wrp[] = {
  2.      "wand",   "ring",      "potion",     "scroll", "gem",
  3.      "amulet", "spellbook", "spell book",
  4.      /* for non-specific wishes */
  5.      "weapon", "armor",     "tool",       "food",   "comestible",
  6.  };
  7.  static const char wrpsym[] = { WAND_CLASS,   RING_CLASS,   POTION_CLASS,
  8.                                 SCROLL_CLASS, GEM_CLASS,    AMULET_CLASS,
  9.                                 SPBOOK_CLASS, SPBOOK_CLASS, WEAPON_CLASS,
  10.                                 ARMOR_CLASS,  TOOL_CLASS,   FOOD_CLASS,
  11.                                 FOOD_CLASS };
  12.  

otense

  1.  /* return form of the verb (input plural) if xname(otmp) were the subject */
  2.  char *
  3.  otense(otmp, verb)
  4.  struct obj *otmp;
  5.  const char *verb;
  6.  {
  7.      char *buf;
  8.  
  9.      /*
  10.       * verb is given in plural (without trailing s).  Return as input
  11.       * if the result of xname(otmp) would be plural.  Don't bother
  12.       * recomputing xname(otmp) at this time.
  13.       */
  14.      if (!is_plural(otmp))
  15.          return vtense((char *) 0, verb);
  16.  
  17.      buf = nextobuf();
  18.      Strcpy(buf, verb);
  19.      return buf;
  20.  }
  21.  


  1.  /* various singular words that vtense would otherwise categorize as plural;
  2.     also used by makesingular() to catch some special cases */
  3.  static const char *const special_subjs[] = {
  4.      "erinys",  "manes", /* this one is ambiguous */
  5.      "Cyclops", "Hippocrates",     "Pelias",    "aklys",
  6.      "amnesia", "detect monsters", "paralysis", "shape changers",
  7.      "nemesis", 0
  8.      /* note: "detect monsters" and "shape changers" are normally
  9.         caught via "<something>(s) of <whatever>", but they can be
  10.         wished for using the shorter form, so we include them here
  11.         to accommodate usage by makesingular during wishing */
  12.  };
  13.  

vtense

  1.  /* return form of the verb (input plural) for present tense 3rd person subj */
  2.  char *
  3.  vtense(subj, verb)
  4.  register const char *subj;
  5.  register const char *verb;
  6.  {
  7.      char *buf = nextobuf(), *bspot;
  8.      int len, ltmp;
  9.      const char *sp, *spot;
  10.      const char *const *spec;
  11.  
  12.      /*
  13.       * verb is given in plural (without trailing s).  Return as input
  14.       * if subj appears to be plural.  Add special cases as necessary.
  15.       * Many hard cases can already be handled by using otense() instead.
  16.       * If this gets much bigger, consider decomposing makeplural.
  17.       * Note: monster names are not expected here (except before corpse).
  18.       *
  19.       * Special case: allow null sobj to get the singular 3rd person
  20.       * present tense form so we don't duplicate this code elsewhere.
  21.       */
  22.      if (subj) {
  23.          if (!strncmpi(subj, "a ", 2) || !strncmpi(subj, "an ", 3))
  24.              goto sing;
  25.          spot = (const char *) 0;
  26.          for (sp = subj; (sp = index(sp, ' ')) != 0; ++sp) {
  27.              if (!strncmpi(sp, " of ", 4) || !strncmpi(sp, " from ", 6)
  28.                  || !strncmpi(sp, " called ", 8) || !strncmpi(sp, " named ", 7)
  29.                  || !strncmpi(sp, " labeled ", 9)) {
  30.                  if (sp != subj)
  31.                      spot = sp - 1;
  32.                  break;
  33.              }
  34.          }
  35.          len = (int) strlen(subj);
  36.          if (!spot)
  37.              spot = subj + len - 1;
  38.  
  39.          /*
  40.           * plural: anything that ends in 's', but not '*us' or '*ss'.
  41.           * Guess at a few other special cases that makeplural creates.
  42.           */
  43.          if ((lowc(*spot) == 's' && spot != subj
  44.               && !index("us", lowc(*(spot - 1))))
  45.              || !BSTRNCMPI(subj, spot - 3, "eeth", 4)
  46.              || !BSTRNCMPI(subj, spot - 3, "feet", 4)
  47.              || !BSTRNCMPI(subj, spot - 1, "ia", 2)
  48.              || !BSTRNCMPI(subj, spot - 1, "ae", 2)) {
  49.              /* check for special cases to avoid false matches */
  50.              len = (int) (spot - subj) + 1;
  51.              for (spec = special_subjs; *spec; spec++) {
  52.                  ltmp = strlen(*spec);
  53.                  if (len == ltmp && !strncmpi(*spec, subj, len))
  54.                      goto sing;
  55.                  /* also check for <prefix><space><special_subj>
  56.                     to catch things like "the invisible erinys" */
  57.                  if (len > ltmp && *(spot - ltmp) == ' '
  58.                      && !strncmpi(*spec, spot - ltmp + 1, ltmp))
  59.                      goto sing;
  60.              }
  61.  
  62.              return strcpy(buf, verb);
  63.          }
  64.          /*
  65.           * 3rd person plural doesn't end in telltale 's';
  66.           * 2nd person singular behaves as if plural.
  67.           */
  68.          if (!strcmpi(subj, "they") || !strcmpi(subj, "you"))
  69.              return strcpy(buf, verb);
  70.      }
  71.  
  72.  sing:
  73.      Strcpy(buf, verb);
  74.      len = (int) strlen(buf);
  75.      bspot = buf + len - 1;
  76.  
  77.      if (!strcmpi(buf, "are")) {
  78.          Strcasecpy(buf, "is");
  79.      } else if (!strcmpi(buf, "have")) {
  80.          Strcasecpy(bspot - 1, "s");
  81.      } else if (index("zxs", lowc(*bspot))
  82.                 || (len >= 2 && lowc(*bspot) == 'h'
  83.                     && index("cs", lowc(*(bspot - 1))))
  84.                 || (len == 2 && lowc(*bspot) == 'o')) {
  85.          /* Ends in z, x, s, ch, sh; add an "es" */
  86.          Strcasecpy(bspot + 1, "es");
  87.      } else if (lowc(*bspot) == 'y' && !index(vowels, lowc(*(bspot - 1)))) {
  88.          /* like "y" case in makeplural */
  89.          Strcasecpy(bspot, "ies");
  90.      } else {
  91.          Strcasecpy(bspot + 1, "s");
  92.      }
  93.  
  94.      return buf;
  95.  }
  96.  


  1.  struct sing_plur {
  2.      const char *sing, *plur;
  3.  };
  4.  
  5.  /* word pairs that don't fit into formula-based transformations;
  6.     also some suffices which have very few--often one--matches or
  7.     which aren't systematically reversible (knives, staves) */
  8.  static struct sing_plur one_off[] = {
  9.      { "child",
  10.        "children" },      /* (for wise guys who give their food funny names) */
  11.      { "cubus", "cubi" }, /* in-/suc-cubus */
  12.      { "culus", "culi" }, /* homunculus */
  13.      { "djinni", "djinn" },
  14.      { "erinys", "erinyes" },
  15.      { "foot", "feet" },
  16.      { "fungus", "fungi" },
  17.      { "knife", "knives" },
  18.      { "labrum", "labra" }, /* candelabrum */
  19.      { "louse", "lice" },
  20.      { "mouse", "mice" },
  21.      { "mumak", "mumakil" },
  22.      { "nemesis", "nemeses" },
  23.      { "rtex", "rtices" }, /* vortex */
  24.      { "tooth", "teeth" },
  25.      { "staff", "staves" },
  26.      { 0, 0 }
  27.  };
  28.  
  29.  static const char *const as_is[] = {
  30.      /* makesingular() leaves these plural due to how they're used */
  31.      "boots",   "shoes",     "gloves",    "lenses",   "scales",
  32.      "eyes",    "gauntlets", "iron bars",
  33.      /* both singular and plural are spelled the same */
  34.      "deer",    "fish",      "tuna",      "yaki",     "-hai",
  35.      "krill",   "manes",     "ninja",     "sheep",    "ronin",
  36.      "roshi",   "shito",     "tengu",     "ki-rin",   "Nazgul",
  37.      "gunyoki", "piranha",   "samurai",   "shuriken", 0,
  38.      /* Note:  "fish" and "piranha" are collective plurals, suitable
  39.         for "wiped out all <foo>".  For "3 <foo>", they should be
  40.         "fishes" and "piranhas" instead.  We settle for collective
  41.         variant instead of attempting to support both. */
  42.  };
  43.  

singplur_lookup

  1.  /* singularize/pluralize decisions common to both makesingular & makeplural
  2.   */
  3.  STATIC_OVL boolean
  4.  singplur_lookup(basestr, endstring, to_plural, alt_as_is)
  5.  char *basestr, *endstring;    /* base string, pointer to eos(string) */
  6.  boolean to_plural;            /* true => makeplural, false => makesingular */
  7.  const char *const *alt_as_is; /* another set like as_is[] */
  8.  {
  9.      const struct sing_plur *sp;
  10.      const char *same, *other, *const *as;
  11.      int al;
  12.  
  13.      for (as = as_is; *as; ++as) {
  14.          al = (int) strlen(*as);
  15.          if (!BSTRCMPI(basestr, endstring - al, *as))
  16.              return TRUE;
  17.      }
  18.      if (alt_as_is) {
  19.          for (as = alt_as_is; *as; ++as) {
  20.              al = (int) strlen(*as);
  21.              if (!BSTRCMPI(basestr, endstring - al, *as))
  22.                  return TRUE;
  23.          }
  24.      }
  25.  
  26.      for (sp = one_off; sp->sing; sp++) {
  27.          /* check whether endstring already matches */
  28.          same = to_plural ? sp->plur : sp->sing;
  29.          al = (int) strlen(same);
  30.          if (!BSTRCMPI(basestr, endstring - al, same))
  31.              return TRUE; /* use as-is */
  32.          /* check whether it matches the inverse; if so, transform it */
  33.          other = to_plural ? sp->sing : sp->plur;
  34.          al = (int) strlen(other);
  35.          if (!BSTRCMPI(basestr, endstring - al, other)) {
  36.              Strcasecpy(endstring - al, same);
  37.              return TRUE; /* one_off[] transformation */
  38.          }
  39.      }
  40.      return FALSE;
  41.  }
  42.  

singplur_compound

  1.  /* searches for common compounds, ex. lump of royal jelly */
  2.  STATIC_OVL char *
  3.  singplur_compound(str)
  4.  char *str;
  5.  {
  6.      /* if new entries are added, be sure to keep compound_start[] in sync */
  7.      static const char *const compounds[] =
  8.          {
  9.            " of ",     " labeled ", " called ",
  10.            " named ",  " above", /* lurkers above */
  11.            " versus ", " from ",    " in ",
  12.            " on ",     " a la ",    " with", /* " with "? */
  13.            " de ",     " d'",       " du ",
  14.            "-in-",     "-at-",      0
  15.          }, /* list of first characters for all compounds[] entries */
  16.          compound_start[] = " -";
  17.  
  18.      const char *const *cmpd;
  19.      char *p;
  20.  
  21.      for (p = str; *p; ++p) {
  22.          /* substring starting at p can only match if *p is found
  23.             within compound_start[] */
  24.          if (!index(compound_start, *p))
  25.              continue;
  26.  
  27.          /* check current substring against all words in the compound[] list */
  28.          for (cmpd = compounds; *cmpd; ++cmpd)
  29.              if (!strncmpi(p, *cmpd, (int) strlen(*cmpd)))
  30.                  return p;
  31.      }
  32.      /* wasn't recognized as a compound phrase */
  33.      return 0;
  34.  }
  35.  

makeplural

  1.  /* Plural routine; chiefly used for user-defined fruits.  We have to try to
  2.   * account for everything reasonable the player has; something unreasonable
  3.   * can still break the code.  However, it's still a lot more accurate than
  4.   * "just add an s at the end", which Rogue uses...
  5.   *
  6.   * Also used for plural monster names ("Wiped out all homunculi." or the
  7.   * vanquished monsters list) and body parts.  A lot of unique monsters have
  8.   * names which get mangled by makeplural and/or makesingular.  They're not
  9.   * genocidable, and vanquished-mon handling does its own special casing
  10.   * (for uniques who've been revived and re-killed), so we don't bother
  11.   * trying to get those right here.
  12.   *
  13.   * Also misused by muse.c to convert 1st person present verbs to 2nd person.
  14.   * 3.6.0: made case-insensitive.
  15.   */
  16.  char *
  17.  makeplural(oldstr)
  18.  const char *oldstr;
  19.  {
  20.      register char *spot;
  21.      char lo_c, *str = nextobuf();
  22.      const char *excess = (char *) 0;
  23.      int len;
  24.  
  25.      if (oldstr)
  26.          while (*oldstr == ' ')
  27.              oldstr++;
  28.      if (!oldstr || !*oldstr) {
  29.          impossible("plural of null?");
  30.          Strcpy(str, "s");
  31.          return str;
  32.      }
  33.      Strcpy(str, oldstr);
  34.  
  35.      /*
  36.       * Skip changing "pair of" to "pairs of".  According to Webster, usual
  37.       * English usage is use pairs for humans, e.g. 3 pairs of dancers,
  38.       * and pair for objects and non-humans, e.g. 3 pair of boots.  We don't
  39.       * refer to pairs of humans in this game so just skip to the bottom.
  40.       */
  41.      if (!strncmpi(str, "pair of ", 8))
  42.          goto bottom;
  43.  
  44.      /* look for "foo of bar" so that we can focus on "foo" */
  45.      if ((spot = singplur_compound(str)) != 0) {
  46.          excess = oldstr + (int) (spot - str);
  47.          *spot = '\0';
  48.      } else
  49.          spot = eos(str);
  50.  
  51.      spot--;
  52.      while (spot > str && *spot == ' ')
  53.          spot--; /* Strip blanks from end */
  54.      *(spot + 1) = 0;
  55.      /* Now spot is the last character of the string */
  56.  
  57.      len = strlen(str);
  58.  
  59.      /* Single letters */
  60.      if (len == 1 || !letter(*spot)) {
  61.          Strcpy(spot + 1, "'s");
  62.          goto bottom;
  63.      }
  64.  
  65.      /* dispense with some words which don't need pluralization */
  66.      {
  67.          static const char *const already_plural[] = {
  68.              "ae",  /* algae, larvae, &c */
  69.              "men", /* also catches women, watchmen */
  70.              "matzot", 0,
  71.          };
  72.  
  73.          /* spot+1: synch up with makesingular's usage */
  74.          if (singplur_lookup(str, spot + 1, TRUE, already_plural))
  75.              goto bottom;
  76.  
  77.          /* more of same, but not suitable for blanket loop checking */
  78.          if ((len == 2 && !strcmpi(str, "ya"))
  79.              || (len >= 3 && !strcmpi(spot - 2, " ya")))
  80.              goto bottom;
  81.      }
  82.  
  83.      /* man/men ("Wiped out all cavemen.") */
  84.      if (len >= 3 && !strcmpi(spot - 2, "man")
  85.          /* exclude shamans and humans */
  86.          && (len < 6 || strcmpi(spot - 5, "shaman"))
  87.          && (len < 5 || strcmpi(spot - 4, "human"))) {
  88.          Strcasecpy(spot - 1, "en");
  89.          goto bottom;
  90.      }
  91.      if (lowc(*spot) == 'f') { /* (staff handled via one_off[]) */
  92.          lo_c = lowc(*(spot - 1));
  93.          if (len >= 3 && !strcmpi(spot - 2, "erf")) {
  94.              /* avoid "nerf" -> "nerves", "serf" -> "serves" */
  95.              ; /* fall through to default (append 's') */
  96.          } else if (index("lr", lo_c) || index(vowels, lo_c)) {
  97.              /* [aeioulr]f to [aeioulr]ves */
  98.              Strcasecpy(spot, "ves");
  99.              goto bottom;
  100.          }
  101.      }
  102.      /* ium/ia (mycelia, baluchitheria) */
  103.      if (len >= 3 && !strcmpi(spot - 2, "ium")) {
  104.          Strcasecpy(spot - 2, "ia");
  105.          goto bottom;
  106.      }
  107.      /* algae, larvae, hyphae (another fungus part) */
  108.      if ((len >= 4 && !strcmpi(spot - 3, "alga"))
  109.          || (len >= 5
  110.              && (!strcmpi(spot - 4, "hypha") || !strcmpi(spot - 4, "larva")))
  111.          || (len >= 6 && !strcmpi(spot - 5, "amoeba"))
  112.          || (len >= 8 && (!strcmpi(spot - 7, "vertebra")))) {
  113.          /* a to ae */
  114.          Strcasecpy(spot + 1, "e");
  115.          goto bottom;
  116.      }
  117.      /* fungus/fungi, homunculus/homunculi, but buses, lotuses, wumpuses */
  118.      if (len > 3 && !strcmpi(spot - 1, "us")
  119.          && !((len >= 5 && !strcmpi(spot - 4, "lotus"))
  120.               || (len >= 6 && !strcmpi(spot - 5, "wumpus")))) {
  121.          Strcasecpy(spot - 1, "i");
  122.          goto bottom;
  123.      }
  124.      /* sis/ses (nemesis) */
  125.      if (len >= 3 && !strcmpi(spot - 2, "sis")) {
  126.          Strcasecpy(spot - 1, "es");
  127.          goto bottom;
  128.      }
  129.      /* matzoh/matzot, possible food name */
  130.      if (len >= 6
  131.          && (!strcmpi(spot - 5, "matzoh") || !strcmpi(spot - 5, "matzah"))) {
  132.          Strcasecpy(spot - 1, "ot"); /* oh/ah -> ot */
  133.          goto bottom;
  134.      }
  135.      if (len >= 5
  136.          && (!strcmpi(spot - 4, "matzo") || !strcmpi(spot - 4, "matza"))) {
  137.          Strcasecpy(spot, "ot"); /* o/a -> ot */
  138.          goto bottom;
  139.      }
  140.  
  141.      /* note: -eau/-eaux (gateau, bordeau...) */
  142.      /* note: ox/oxen, VAX/VAXen, goose/geese */
  143.  
  144.      lo_c = lowc(*spot);
  145.  
  146.      /* Ends in z, x, s, ch, sh; add an "es" */
  147.      if (index("zxs", lo_c)
  148.          || (len >= 2 && lo_c == 'h' && index("cs", lowc(*(spot - 1))))
  149.          /* Kludge to get "tomatoes" and "potatoes" right */
  150.          || (len >= 4 && !strcmpi(spot - 2, "ato"))
  151.          || (len >= 5 && !strcmpi(spot - 4, "dingo"))) {
  152.          Strcasecpy(spot + 1, "es"); /* append es */
  153.          goto bottom;
  154.      }
  155.      /* Ends in y preceded by consonant (note: also "qu") change to "ies" */
  156.      if (lo_c == 'y' && !index(vowels, lowc(*(spot - 1)))) {
  157.          Strcasecpy(spot, "ies"); /* y -> ies */
  158.          goto bottom;
  159.      }
  160.      /* Default: append an 's' */
  161.      Strcasecpy(spot + 1, "s");
  162.  
  163.  bottom:
  164.      if (excess)
  165.          Strcat(str, excess);
  166.      return str;
  167.  }
  168.  

makesingular

  1.  /*
  2.   * Singularize a string the user typed in; this helps reduce the complexity
  3.   * of readobjnam, and is also used in pager.c to singularize the string
  4.   * for which help is sought.
  5.   *
  6.   * "Manes" is ambiguous: monster type (keep s), or horse body part (drop s)?
  7.   * Its inclusion in as_is[]/special_subj[] makes it get treated as the former.
  8.   *
  9.   * A lot of unique monsters have names ending in s; plural, or singular
  10.   * from plural, doesn't make much sense for them so we don't bother trying.
  11.   * 3.6.0: made case-insensitive.
  12.   */
  13.  char *
  14.  makesingular(oldstr)
  15.  const char *oldstr;
  16.  {
  17.      register char *p, *bp;
  18.      const char *excess = 0;
  19.      char *str = nextobuf();
  20.  
  21.      if (oldstr)
  22.          while (*oldstr == ' ')
  23.              oldstr++;
  24.      if (!oldstr || !*oldstr) {
  25.          impossible("singular of null?");
  26.          str[0] = '\0';
  27.          return str;
  28.      }
  29.  
  30.      bp = strcpy(str, oldstr);
  31.  
  32.      /* check for "foo of bar" so that we can focus on "foo" */
  33.      if ((p = singplur_compound(bp)) != 0) {
  34.          excess = oldstr + (int) (p - bp);
  35.          *p = '\0';
  36.      } else
  37.          p = eos(bp);
  38.  
  39.      /* dispense with some words which don't need singularization */
  40.      if (singplur_lookup(bp, p, FALSE, special_subjs))
  41.          goto bottom;
  42.  
  43.      /* remove -s or -es (boxes) or -ies (rubies) */
  44.      if (p >= bp + 1 && lowc(p[-1]) == 's') {
  45.          if (p >= bp + 2 && lowc(p[-2]) == 'e') {
  46.              if (p >= bp + 3 && lowc(p[-3]) == 'i') { /* "ies" */
  47.                  if (!BSTRCMPI(bp, p - 7, "cookies")
  48.                      || !BSTRCMPI(bp, p - 4, "pies")
  49.                      || !BSTRCMPI(bp, p - 5, "mbies") /* zombie */
  50.                      || !BSTRCMPI(bp, p - 5, "yries")) /* valkyrie */
  51.                      goto mins;
  52.                  Strcasecpy(p - 3, "y"); /* ies -> y */
  53.                  goto bottom;
  54.              }
  55.              /* wolves, but f to ves isn't fully reversible */
  56.              if (p - 4 >= bp && (index("lr", lowc(*(p - 4)))
  57.                                  || index(vowels, lowc(*(p - 4))))
  58.                  && !BSTRCMPI(bp, p - 3, "ves")) {
  59.                  if (!BSTRCMPI(bp, p - 6, "cloves")
  60.                      || !BSTRCMPI(bp, p - 6, "nerves"))
  61.                      goto mins;
  62.                  Strcasecpy(p - 3, "f"); /* ves -> f */
  63.                  goto bottom;
  64.              }
  65.              /* note: nurses, axes but boxes, wumpuses */
  66.              if (!BSTRCMPI(bp, p - 4, "eses")
  67.                  || !BSTRCMPI(bp, p - 4, "oxes") /* boxes, foxes */
  68.                  || !BSTRCMPI(bp, p - 4, "nxes") /* lynxes */
  69.                  || !BSTRCMPI(bp, p - 4, "ches")
  70.                  || !BSTRCMPI(bp, p - 4, "uses") /* lotuses */
  71.                  || !BSTRCMPI(bp, p - 4, "sses") /* priestesses */
  72.                  || !BSTRCMPI(bp, p - 5, "atoes") /* tomatoes */
  73.                  || !BSTRCMPI(bp, p - 7, "dingoes")
  74.                  || !BSTRCMPI(bp, p - 7, "Aleaxes")) {
  75.                  *(p - 2) = '\0'; /* drop es */
  76.                  goto bottom;
  77.              } /* else fall through to mins */
  78.  
  79.              /* ends in 's' but not 'es' */
  80.          } else if (!BSTRCMPI(bp, p - 2, "us")) { /* lotus, fungus... */
  81.              if (BSTRCMPI(bp, p - 6, "tengus") /* but not these... */
  82.                  && BSTRCMPI(bp, p - 7, "hezrous"))
  83.                  goto bottom;
  84.          } else if (!BSTRCMPI(bp, p - 2, "ss")
  85.                     || !BSTRCMPI(bp, p - 5, " lens")
  86.                     || (p - 4 == bp && !strcmpi(p - 4, "lens"))) {
  87.              goto bottom;
  88.          }
  89.      mins:
  90.          *(p - 1) = '\0'; /* drop s */
  91.  
  92.      } else { /* input doesn't end in 's' */
  93.  
  94.          if (!BSTRCMPI(bp, p - 3, "men")) {
  95.              Strcasecpy(p - 2, "an");
  96.              goto bottom;
  97.          }
  98.          /* matzot -> matzo, algae -> alga */
  99.          if (!BSTRCMPI(bp, p - 6, "matzot") || !BSTRCMPI(bp, p - 2, "ae")) {
  100.              *(p - 1) = '\0'; /* drop t/e */
  101.              goto bottom;
  102.          }
  103.          /* balactheria -> balactherium */
  104.          if (p - 4 >= bp && !strcmpi(p - 2, "ia")
  105.              && index("lr", lowc(*(p - 3))) && lowc(*(p - 4)) == 'e') {
  106.              Strcasecpy(p - 1, "um"); /* a -> um */
  107.          }
  108.  
  109.          /* here we cannot find the plural suffix */
  110.      }
  111.  
  112.  bottom:
  113.      /* if we stripped off a suffix (" of bar" from "foo of bar"),
  114.         put it back now [strcat() isn't actually 100% safe here...] */
  115.      if (excess)
  116.          Strcat(bp, excess);
  117.  
  118.      return bp;
  119.  }
  120.  

wishymatch

  1.  /* compare user string against object name string using fuzzy matching */
  2.  STATIC_OVL boolean
  3.  wishymatch(u_str, o_str, retry_inverted)
  4.  const char *u_str;      /* from user, so might be variant spelling */
  5.  const char *o_str;      /* from objects[], so is in canonical form */
  6.  boolean retry_inverted; /* optional extra "of" handling */
  7.  {
  8.      static NEARDATA const char detect_SP[] = "detect ",
  9.                                 SP_detection[] = " detection";
  10.      char *p, buf[BUFSZ];
  11.  
  12.      /* ignore spaces & hyphens and upper/lower case when comparing */
  13.      if (fuzzymatch(u_str, o_str, " -", TRUE))
  14.          return TRUE;
  15.  
  16.      if (retry_inverted) {
  17.          const char *u_of, *o_of;
  18.  
  19.          /* when just one of the strings is in the form "foo of bar",
  20.             convert it into "bar foo" and perform another comparison */
  21.          u_of = strstri(u_str, " of ");
  22.          o_of = strstri(o_str, " of ");
  23.          if (u_of && !o_of) {
  24.              Strcpy(buf, u_of + 4);
  25.              p = eos(strcat(buf, " "));
  26.              while (u_str < u_of)
  27.                  *p++ = *u_str++;
  28.              *p = '\0';
  29.              return fuzzymatch(buf, o_str, " -", TRUE);
  30.          } else if (o_of && !u_of) {
  31.              Strcpy(buf, o_of + 4);
  32.              p = eos(strcat(buf, " "));
  33.              while (o_str < o_of)
  34.                  *p++ = *o_str++;
  35.              *p = '\0';
  36.              return fuzzymatch(u_str, buf, " -", TRUE);
  37.          }
  38.      }
  39.  
  40.      /* [note: if something like "elven speed boots" ever gets added, these
  41.         special cases should be changed to call wishymatch() recursively in
  42.         order to get the "of" inversion handling] */
  43.      if (!strncmp(o_str, "dwarvish ", 9)) {
  44.          if (!strncmpi(u_str, "dwarven ", 8))
  45.              return fuzzymatch(u_str + 8, o_str + 9, " -", TRUE);
  46.      } else if (!strncmp(o_str, "elven ", 6)) {
  47.          if (!strncmpi(u_str, "elvish ", 7))
  48.              return fuzzymatch(u_str + 7, o_str + 6, " -", TRUE);
  49.          else if (!strncmpi(u_str, "elfin ", 6))
  50.              return fuzzymatch(u_str + 6, o_str + 6, " -", TRUE);
  51.      } else if (!strncmp(o_str, detect_SP, sizeof detect_SP - 1)) {
  52.          /* check for "detect <foo>" vs "<foo> detection" */
  53.          if ((p = strstri(u_str, SP_detection)) != 0
  54.              && !*(p + sizeof SP_detection - 1)) {
  55.              /* convert "<foo> detection" into "detect <foo>" */
  56.              *p = '\0';
  57.              Strcat(strcpy(buf, detect_SP), u_str);
  58.              /* "detect monster" -> "detect monsters" */
  59.              if (!strcmpi(u_str, "monster"))
  60.                  Strcat(buf, "s");
  61.              *p = ' ';
  62.              return fuzzymatch(buf, o_str, " -", TRUE);
  63.          }
  64.      } else if (strstri(o_str, SP_detection)) {
  65.          /* and the inverse, "<foo> detection" vs "detect <foo>" */
  66.          if (!strncmpi(u_str, detect_SP, sizeof detect_SP - 1)) {
  67.              /* convert "detect <foo>s" into "<foo> detection" */
  68.              p = makesingular(u_str + sizeof detect_SP - 1);
  69.              Strcat(strcpy(buf, p), SP_detection);
  70.              /* caller may be looping through objects[], so avoid
  71.                 churning through all the obufs */
  72.              releaseobuf(p);
  73.              return fuzzymatch(buf, o_str, " -", TRUE);
  74.          }
  75.      } else if (strstri(o_str, "ability")) {
  76.          /* when presented with "foo of bar", makesingular() used to
  77.             singularize both foo & bar, but now only does so for foo */
  78.          /* catch "{potion(s),ring} of {gain,restore,sustain} abilities" */
  79.          if ((p = strstri(u_str, "abilities")) != 0
  80.              && !*(p + sizeof "abilities" - 1)) {
  81.              (void) strncpy(buf, u_str, (unsigned) (p - u_str));
  82.              Strcpy(buf + (p - u_str), "ability");
  83.              return fuzzymatch(buf, o_str, " -", TRUE);
  84.          }
  85.      } else if (!strcmp(o_str, "aluminum")) {
  86.          /* this special case doesn't really fit anywhere else... */
  87.          /* (note that " wand" will have been stripped off by now) */
  88.          if (!strcmpi(u_str, "aluminium"))
  89.              return fuzzymatch(u_str + 9, o_str + 8, " -", TRUE);
  90.      }
  91.  
  92.      return FALSE;
  93.  }
  94.  


  1.  struct o_range {
  2.      const char *name, oclass;
  3.      int f_o_range, l_o_range;
  4.  };
  5.  
  6.  /* wishable subranges of objects */
  7.  STATIC_OVL NEARDATA const struct o_range o_ranges[] = {
  8.      { "bag", TOOL_CLASS, SACK, BAG_OF_TRICKS },
  9.      { "lamp", TOOL_CLASS, OIL_LAMP, MAGIC_LAMP },
  10.      { "candle", TOOL_CLASS, TALLOW_CANDLE, WAX_CANDLE },
  11.      { "horn", TOOL_CLASS, TOOLED_HORN, HORN_OF_PLENTY },
  12.      { "shield", ARMOR_CLASS, SMALL_SHIELD, SHIELD_OF_REFLECTION },
  13.      { "hat", ARMOR_CLASS, FEDORA, DUNCE_CAP },
  14.      { "helm", ARMOR_CLASS, ELVEN_LEATHER_HELM, HELM_OF_TELEPATHY },
  15.      { "gloves", ARMOR_CLASS, LEATHER_GLOVES, GAUNTLETS_OF_DEXTERITY },
  16.      { "gauntlets", ARMOR_CLASS, LEATHER_GLOVES, GAUNTLETS_OF_DEXTERITY },
  17.      { "boots", ARMOR_CLASS, LOW_BOOTS, LEVITATION_BOOTS },
  18.      { "shoes", ARMOR_CLASS, LOW_BOOTS, IRON_SHOES },
  19.      { "cloak", ARMOR_CLASS, MUMMY_WRAPPING, CLOAK_OF_DISPLACEMENT },
  20.      { "shirt", ARMOR_CLASS, HAWAIIAN_SHIRT, T_SHIRT },
  21.      { "dragon scales", ARMOR_CLASS, GRAY_DRAGON_SCALES,
  22.        YELLOW_DRAGON_SCALES },
  23.      { "dragon scale mail", ARMOR_CLASS, GRAY_DRAGON_SCALE_MAIL,
  24.        YELLOW_DRAGON_SCALE_MAIL },
  25.      { "sword", WEAPON_CLASS, SHORT_SWORD, KATANA },
  26.      { "venom", VENOM_CLASS, BLINDING_VENOM, ACID_VENOM },
  27.      { "gray stone", GEM_CLASS, LUCKSTONE, FLINT },
  28.      { "grey stone", GEM_CLASS, LUCKSTONE, FLINT },
  29.  };
  30.  


  1.  /* alternate spellings; if the difference is only the presence or
  2.     absence of spaces and/or hyphens (such as "pickaxe" vs "pick axe"
  3.     vs "pick-axe") then there is no need for inclusion in this list;
  4.     likewise for ``"of" inversions'' ("boots of speed" vs "speed boots") */
  5.  struct alt_spellings {
  6.      const char *sp;
  7.      int ob;
  8.  } spellings[] = {
  9.      { "pickax", PICK_AXE },
  10.      { "whip", BULLWHIP },
  11.      { "saber", SILVER_SABER },
  12.      { "silver sabre", SILVER_SABER },
  13.      { "smooth shield", SHIELD_OF_REFLECTION },
  14.      { "grey dragon scale mail", GRAY_DRAGON_SCALE_MAIL },
  15.      { "grey dragon scales", GRAY_DRAGON_SCALES },
  16.      { "iron ball", HEAVY_IRON_BALL },
  17.      { "lantern", BRASS_LANTERN },
  18.      { "mattock", DWARVISH_MATTOCK },
  19.      { "amulet of poison resistance", AMULET_VERSUS_POISON },
  20.      { "potion of sleep", POT_SLEEPING },
  21.      { "stone", ROCK },
  22.      { "camera", EXPENSIVE_CAMERA },
  23.      { "tee shirt", T_SHIRT },
  24.      { "can", TIN },
  25.      { "can opener", TIN_OPENER },
  26.      { "kelp", KELP_FROND },
  27.      { "eucalyptus", EUCALYPTUS_LEAF },
  28.      { "royal jelly", LUMP_OF_ROYAL_JELLY },
  29.      { "lembas", LEMBAS_WAFER },
  30.      { "marker", MAGIC_MARKER },
  31.      { "hook", GRAPPLING_HOOK },
  32.      { "grappling iron", GRAPPLING_HOOK },
  33.      { "grapnel", GRAPPLING_HOOK },
  34.      { "grapple", GRAPPLING_HOOK },
  35.      /* normally we wouldn't have to worry about unnecessary <space>, but
  36.         " stone" will get stripped off, preventing a wishymatch; that actually
  37.         lets "flint stone" be a match, so we also accept bogus "flintstone" */
  38.      { "luck stone", LUCKSTONE },
  39.      { "load stone", LOADSTONE },
  40.      { "touch stone", TOUCHSTONE },
  41.      { "flintstone", FLINT },
  42.      { (const char *) 0, 0 },
  43.  };
  44.  

rnd_otyp_by_wpnskill

  1.  short
  2.  rnd_otyp_by_wpnskill(skill)
  3.  schar skill;
  4.  {
  5.      int i, n = 0;
  6.      short otyp = STRANGE_OBJECT;
  7.      for (i = bases[WEAPON_CLASS];
  8.           i < NUM_OBJECTS && objects[i].oc_class == WEAPON_CLASS; i++)
  9.          if (objects[i].oc_skill == skill) {
  10.              n++;
  11.              otyp = i;
  12.          }
  13.      if (n > 0) {
  14.          n = rn2(n);
  15.          for (i = bases[WEAPON_CLASS];
  16.               i < NUM_OBJECTS && objects[i].oc_class == WEAPON_CLASS; i++)
  17.              if (objects[i].oc_skill == skill)
  18.                  if (--n < 0)
  19.                      return i;
  20.      }
  21.      return otyp;
  22.  }
  23.  

readobjnam

  1.  /*
  2.   * Return something wished for.  Specifying a null pointer for
  3.   * the user request string results in a random object.  Otherwise,
  4.   * if asking explicitly for "nothing" (or "nil") return no_wish;
  5.   * if not an object return &zeroobj; if an error (no matching object),
  6.   * return null.
  7.   */
  8.  struct obj *
  9.  readobjnam(bp, no_wish)
  10.  register char *bp;
  11.  struct obj *no_wish;
  12.  {
  13.      register char *p;
  14.      register int i;
  15.      register struct obj *otmp;
  16.      int cnt, spe, spesgn, typ, very, rechrg;
  17.      int blessed, uncursed, iscursed, ispoisoned, isgreased;
  18.      int eroded, eroded2, erodeproof;
  19.      int halfeaten, mntmp, contents;
  20.      int islit, unlabeled, ishistoric, isdiluted, trapped;
  21.      int tmp, tinv, tvariety;
  22.      int wetness;
  23.      struct fruit *f;
  24.      int ftype = context.current_fruit;
  25.      char fruitbuf[BUFSZ];
  26.      /* Fruits may not mess up the ability to wish for real objects (since
  27.       * you can leave a fruit in a bones file and it will be added to
  28.       * another person's game), so they must be checked for last, after
  29.       * stripping all the possible prefixes and seeing if there's a real
  30.       * name in there.  So we have to save the full original name.  However,
  31.       * it's still possible to do things like "uncursed burnt Alaska",
  32.       * or worse yet, "2 burned 5 course meals", so we need to loop to
  33.       * strip off the prefixes again, this time stripping only the ones
  34.       * possible on food.
  35.       * We could get even more detailed so as to allow food names with
  36.       * prefixes that _are_ possible on food, so you could wish for
  37.       * "2 3 alarm chilis".  Currently this isn't allowed; options.c
  38.       * automatically sticks 'candied' in front of such names.
  39.       */
  40.      char oclass;
  41.      char *un, *dn, *actualn;
  42.      const char *name = 0;
  43.  
  44.      cnt = spe = spesgn = typ = very = rechrg = blessed = uncursed = iscursed =
  45.          ispoisoned = isgreased = eroded = eroded2 = erodeproof = halfeaten =
  46.              islit = unlabeled = ishistoric = isdiluted = trapped = 0;
  47.      tvariety = RANDOM_TIN;
  48.      mntmp = NON_PM;
  49.  #define UNDEFINED 0
  50.  #define EMPTY 1
  51.  #define SPINACH 2
  52.      contents = UNDEFINED;
  53.      oclass = 0;
  54.      actualn = dn = un = 0;
  55.      wetness = 0;
  56.  
  57.      if (!bp)
  58.          goto any;
  59.      /* first, remove extra whitespace they may have typed */
  60.      (void) mungspaces(bp);
  61.      /* allow wishing for "nothing" to preserve wishless conduct...
  62.         [now requires "wand of nothing" if that's what was really wanted] */
  63.      if (!strcmpi(bp, "nothing") || !strcmpi(bp, "nil")
  64.          || !strcmpi(bp, "none"))
  65.          return no_wish;
  66.      /* save the [nearly] unmodified choice string */
  67.      Strcpy(fruitbuf, bp);
  68.  
  69.      for (;;) {
  70.          register int l;
  71.  
  72.          if (!bp || !*bp)
  73.              goto any;
  74.          if (!strncmpi(bp, "an ", l = 3) || !strncmpi(bp, "a ", l = 2)) {
  75.              cnt = 1;
  76.          } else if (!strncmpi(bp, "the ", l = 4)) {
  77.              ; /* just increment `bp' by `l' below */
  78.          } else if (!cnt && digit(*bp) && strcmp(bp, "0")) {
  79.              cnt = atoi(bp);
  80.              while (digit(*bp))
  81.                  bp++;
  82.              while (*bp == ' ')
  83.                  bp++;
  84.              l = 0;
  85.          } else if (*bp == '+' || *bp == '-') {
  86.              spesgn = (*bp++ == '+') ? 1 : -1;
  87.              spe = atoi(bp);
  88.              while (digit(*bp))
  89.                  bp++;
  90.              while (*bp == ' ')
  91.                  bp++;
  92.              l = 0;
  93.          } else if (!strncmpi(bp, "blessed ", l = 8)
  94.                     || !strncmpi(bp, "holy ", l = 5)) {
  95.              blessed = 1;
  96.          } else if (!strncmpi(bp, "moist ", l = 6)
  97.                     || !strncmpi(bp, "wet ", l = 4)) {
  98.              if (!strncmpi(bp, "wet ", 4))
  99.                  wetness = rn2(3) + 3;
  100.              else
  101.                  wetness = rnd(2);
  102.          } else if (!strncmpi(bp, "cursed ", l = 7)
  103.                     || !strncmpi(bp, "unholy ", l = 7)) {
  104.              iscursed = 1;
  105.          } else if (!strncmpi(bp, "uncursed ", l = 9)) {
  106.              uncursed = 1;
  107.          } else if (!strncmpi(bp, "rustproof ", l = 10)
  108.                     || !strncmpi(bp, "erodeproof ", l = 11)
  109.                     || !strncmpi(bp, "corrodeproof ", l = 13)
  110.                     || !strncmpi(bp, "fixed ", l = 6)
  111.                     || !strncmpi(bp, "fireproof ", l = 10)
  112.                     || !strncmpi(bp, "rotproof ", l = 9)) {
  113.              erodeproof = 1;
  114.          } else if (!strncmpi(bp, "lit ", l = 4)
  115.                     || !strncmpi(bp, "burning ", l = 8)) {
  116.              islit = 1;
  117.          } else if (!strncmpi(bp, "unlit ", l = 6)
  118.                     || !strncmpi(bp, "extinguished ", l = 13)) {
  119.              islit = 0;
  120.              /* "unlabeled" and "blank" are synonymous */
  121.          } else if (!strncmpi(bp, "unlabeled ", l = 10)
  122.                     || !strncmpi(bp, "unlabelled ", l = 11)
  123.                     || !strncmpi(bp, "blank ", l = 6)) {
  124.              unlabeled = 1;
  125.          } else if (!strncmpi(bp, "poisoned ", l = 9)) {
  126.              ispoisoned = 1;
  127.              /* "trapped" recognized but not honored outside wizard mode */
  128.          } else if (!strncmpi(bp, "trapped ", l = 8)) {
  129.              trapped = 0; /* undo any previous "untrapped" */
  130.              if (wizard)
  131.                  trapped = 1;
  132.          } else if (!strncmpi(bp, "untrapped ", l = 10)) {
  133.              trapped = 2; /* not trapped */
  134.          } else if (!strncmpi(bp, "greased ", l = 8)) {
  135.              isgreased = 1;
  136.          } else if (!strncmpi(bp, "very ", l = 5)) {
  137.              /* very rusted very heavy iron ball */
  138.              very = 1;
  139.          } else if (!strncmpi(bp, "thoroughly ", l = 11)) {
  140.              very = 2;
  141.          } else if (!strncmpi(bp, "rusty ", l = 6)
  142.                     || !strncmpi(bp, "rusted ", l = 7)
  143.                     || !strncmpi(bp, "burnt ", l = 6)
  144.                     || !strncmpi(bp, "burned ", l = 7)) {
  145.              eroded = 1 + very;
  146.              very = 0;
  147.          } else if (!strncmpi(bp, "corroded ", l = 9)
  148.                     || !strncmpi(bp, "rotted ", l = 7)) {
  149.              eroded2 = 1 + very;
  150.              very = 0;
  151.          } else if (!strncmpi(bp, "partly eaten ", l = 13)
  152.                     || !strncmpi(bp, "partially eaten ", l = 16)) {
  153.              halfeaten = 1;
  154.          } else if (!strncmpi(bp, "historic ", l = 9)) {
  155.              ishistoric = 1;
  156.          } else if (!strncmpi(bp, "diluted ", l = 8)) {
  157.              isdiluted = 1;
  158.          } else if (!strncmpi(bp, "empty ", l = 6)) {
  159.              contents = EMPTY;
  160.          } else
  161.              break;
  162.          bp += l;
  163.      }
  164.      if (!cnt)
  165.          cnt = 1; /* %% what with "gems" etc. ? */
  166.      if (strlen(bp) > 1 && (p = rindex(bp, '(')) != 0) {
  167.          boolean keeptrailingchars = TRUE;
  168.  
  169.          p[(p > bp && p[-1] == ' ') ? -1 : 0] = '\0'; /*terminate bp */
  170.          ++p; /* advance past '(' */
  171.          if (!strncmpi(p, "lit)", 4)) {
  172.              islit = 1;
  173.              p += 4 - 1; /* point at ')' */
  174.          } else {
  175.              spe = atoi(p);
  176.              while (digit(*p))
  177.                  p++;
  178.              if (*p == ':') {
  179.                  p++;
  180.                  rechrg = spe;
  181.                  spe = atoi(p);
  182.                  while (digit(*p))
  183.                      p++;
  184.              }
  185.              if (*p != ')') {
  186.                  spe = rechrg = 0;
  187.                  /* mis-matched parentheses; rest of string will be ignored
  188.                   * [probably we should restore everything back to '('
  189.                   * instead since it might be part of "named ..."]
  190.                   */
  191.                  keeptrailingchars = FALSE;
  192.              } else {
  193.                  spesgn = 1;
  194.              }
  195.          }
  196.          if (keeptrailingchars) {
  197.              char *pp = eos(bp);
  198.  
  199.              /* 'pp' points at 'pb's terminating '\0',
  200.                 'p' points at ')' and will be incremented past it */
  201.              do {
  202.                  *pp++ = *++p;
  203.              } while (*p);
  204.          }
  205.      }
  206.      /*
  207.       * otmp->spe is type schar, so we don't want spe to be any bigger or
  208.       * smaller.  Also, spe should always be positive --some cheaters may
  209.       * try to confuse atoi().
  210.       */
  211.      if (spe < 0) {
  212.          spesgn = -1; /* cheaters get what they deserve */
  213.          spe = abs(spe);
  214.      }
  215.      if (spe > SCHAR_LIM)
  216.          spe = SCHAR_LIM;
  217.      if (rechrg < 0 || rechrg > 7)
  218.          rechrg = 7; /* recharge_limit */
  219.  
  220.      /* now we have the actual name, as delivered by xname, say
  221.       *  green potions called whisky
  222.       *  scrolls labeled "QWERTY"
  223.       *  egg
  224.       *  fortune cookies
  225.       *  very heavy iron ball named hoei
  226.       *  wand of wishing
  227.       *  elven cloak
  228.       */
  229.      if ((p = strstri(bp, " named ")) != 0) {
  230.          *p = 0;
  231.          name = p + 7;
  232.      }
  233.      if ((p = strstri(bp, " called ")) != 0) {
  234.          *p = 0;
  235.          un = p + 8;
  236.          /* "helmet called telepathy" is not "helmet" (a specific type)
  237.           * "shield called reflection" is not "shield" (a general type)
  238.           */
  239.          for (i = 0; i < SIZE(o_ranges); i++)
  240.              if (!strcmpi(bp, o_ranges[i].name)) {
  241.                  oclass = o_ranges[i].oclass;
  242.                  goto srch;
  243.              }
  244.      }
  245.      if ((p = strstri(bp, " labeled ")) != 0) {
  246.          *p = 0;
  247.          dn = p + 9;
  248.      } else if ((p = strstri(bp, " labelled ")) != 0) {
  249.          *p = 0;
  250.          dn = p + 10;
  251.      }
  252.      if ((p = strstri(bp, " of spinach")) != 0) {
  253.          *p = 0;
  254.          contents = SPINACH;
  255.      }
  256.  
  257.      /*
  258.      Skip over "pair of ", "pairs of", "set of" and "sets of".
  259.  
  260.      Accept "3 pair of boots" as well as "3 pairs of boots". It is valid
  261.      English either way.  See makeplural() for more on pair/pairs.
  262.  
  263.      We should only double count if the object in question is not
  264.      referred to as a "pair of".  E.g. We should double if the player
  265.      types "pair of spears", but not if the player types "pair of
  266.      lenses".  Luckily (?) all objects that are referred to as pairs
  267.      -- boots, gloves, and lenses -- are also not mergable, so cnt is
  268.      ignored anyway.
  269.      */
  270.      if (!strncmpi(bp, "pair of ", 8)) {
  271.          bp += 8;
  272.          cnt *= 2;
  273.      } else if (cnt > 1 && !strncmpi(bp, "pairs of ", 9)) {
  274.          bp += 9;
  275.          cnt *= 2;
  276.      } else if (!strncmpi(bp, "set of ", 7)) {
  277.          bp += 7;
  278.      } else if (!strncmpi(bp, "sets of ", 8)) {
  279.          bp += 8;
  280.      }
  281.  
  282.      /* intercept pudding globs here; they're a valid wish target,
  283.       * but we need them to not get treated like a corpse.
  284.       *
  285.       * also don't let player wish for multiple globs.
  286.       */
  287.      if ((p = strstri(bp, "glob of ")) != 0
  288.          || (p = strstri(bp, "globs of ")) != 0) {
  289.          int globoffset = (*(p + 4) == 's') ? 9 : 8;
  290.          if ((mntmp = name_to_mon(p + globoffset)) >= PM_GRAY_OOZE
  291.              && mntmp <= PM_BLACK_PUDDING) {
  292.              mntmp = NON_PM; /* lie to ourselves */
  293.              cnt = 0;        /* force only one */
  294.          }
  295.      } else {
  296.          /*
  297.           * Find corpse type using "of" (figurine of an orc, tin of orc meat)
  298.           * Don't check if it's a wand or spellbook.
  299.           * (avoid "wand/finger of death" confusion).
  300.           */
  301.          if (!strstri(bp, "wand ") && !strstri(bp, "spellbook ")
  302.              && !strstri(bp, "finger ")) {
  303.              if (((p = strstri(bp, "tin of ")) != 0)
  304.                  && (tmp = tin_variety_txt(p + 7, &tinv))
  305.                  && (mntmp = name_to_mon(p + 7 + tmp)) >= LOW_PM) {
  306.                  *(p + 3) = 0;
  307.                  tvariety = tinv;
  308.              } else if ((p = strstri(bp, " of ")) != 0
  309.                         && (mntmp = name_to_mon(p + 4)) >= LOW_PM)
  310.                  *p = 0;
  311.          }
  312.      }
  313.      /* Find corpse type w/o "of" (red dragon scale mail, yeti corpse) */
  314.      if (strncmpi(bp, "samurai sword", 13))   /* not the "samurai" monster! */
  315.          if (strncmpi(bp, "wizard lock", 11)) /* not the "wizard" monster! */
  316.              if (strncmpi(bp, "ninja-to", 8)) /* not the "ninja" rank */
  317.                  if (strncmpi(bp, "master key",
  318.                               10)) /* not the "master" rank */
  319.                      if (strncmpi(bp, "magenta", 7)) /* not the "mage" rank */
  320.                          if (mntmp < LOW_PM && strlen(bp) > 2
  321.                              && (mntmp = name_to_mon(bp)) >= LOW_PM) {
  322.                              int mntmptoo,
  323.                                  mntmplen; /* double check for rank title */
  324.                              char *obp = bp;
  325.                              mntmptoo = title_to_mon(bp, (int *) 0, &mntmplen);
  326.                              bp += mntmp != mntmptoo
  327.                                        ? (int) strlen(mons[mntmp].mname)
  328.                                        : mntmplen;
  329.                              if (*bp == ' ')
  330.                                  bp++;
  331.                              else if (!strncmpi(bp, "s ", 2))
  332.                                  bp += 2;
  333.                              else if (!strncmpi(bp, "es ", 3))
  334.                                  bp += 3;
  335.                              else if (!*bp && !actualn && !dn && !un
  336.                                       && !oclass) {
  337.                                  /* no referent; they don't really mean a
  338.                                   * monster type */
  339.                                  bp = obp;
  340.                                  mntmp = NON_PM;
  341.                              }
  342.                          }
  343.  
  344.      /* first change to singular if necessary */
  345.      if (*bp) {
  346.          char *sng = makesingular(bp);
  347.          if (strcmp(bp, sng)) {
  348.              if (cnt == 1)
  349.                  cnt = 2;
  350.              Strcpy(bp, sng);
  351.          }
  352.      }
  353.  
  354.      /* Alternate spellings (pick-ax, silver sabre, &c) */
  355.      {
  356.          struct alt_spellings *as = spellings;
  357.  
  358.          while (as->sp) {
  359.              if (fuzzymatch(bp, as->sp, " -", TRUE)) {
  360.                  typ = as->ob;
  361.                  goto typfnd;
  362.              }
  363.              as++;
  364.          }
  365.          /* can't use spellings list for this one due to shuffling */
  366.          if (!strncmpi(bp, "grey spell", 10))
  367.              *(bp + 2) = 'a';
  368.  
  369.          if ((p = strstri(bp, "armour")) != 0) {
  370.              /* skip past "armo", then copy remainder beyond "u" */
  371.              p += 4;
  372.              while ((*p = *(p + 1)) != '\0')
  373.                  ++p; /* self terminating */
  374.          }
  375.      }
  376.  
  377.      /* dragon scales - assumes order of dragons */
  378.      if (!strcmpi(bp, "scales") && mntmp >= PM_GRAY_DRAGON
  379.          && mntmp <= PM_YELLOW_DRAGON) {
  380.          typ = GRAY_DRAGON_SCALES + mntmp - PM_GRAY_DRAGON;
  381.          mntmp = NON_PM; /* no monster */
  382.          goto typfnd;
  383.      }
  384.  
  385.      p = eos(bp);
  386.      if (!BSTRCMPI(bp, p - 10, "holy water")) {
  387.          typ = POT_WATER;
  388.          if ((p - bp) >= 12 && *(p - 12) == 'u')
  389.              iscursed = 1; /* unholy water */
  390.          else
  391.              blessed = 1;
  392.          goto typfnd;
  393.      }
  394.      if (unlabeled && !BSTRCMPI(bp, p - 6, "scroll")) {
  395.          typ = SCR_BLANK_PAPER;
  396.          goto typfnd;
  397.      }
  398.      if (unlabeled && !BSTRCMPI(bp, p - 9, "spellbook")) {
  399.          typ = SPE_BLANK_PAPER;
  400.          goto typfnd;
  401.      }
  402.      /*
  403.       * NOTE: Gold pieces are handled as objects nowadays, and therefore
  404.       * this section should probably be reconsidered as well as the entire
  405.       * gold/money concept.  Maybe we want to add other monetary units as
  406.       * well in the future. (TH)
  407.       */
  408.      if (!BSTRCMPI(bp, p - 10, "gold piece") || !BSTRCMPI(bp, p - 7, "zorkmid")
  409.          || !strcmpi(bp, "gold") || !strcmpi(bp, "money")
  410.          || !strcmpi(bp, "coin") || *bp == GOLD_SYM) {
  411.          if (cnt > 5000 && !wizard)
  412.              cnt = 5000;
  413.          else if (cnt < 1)
  414.              cnt = 1;
  415.          otmp = mksobj(GOLD_PIECE, FALSE, FALSE);
  416.          otmp->quan = (long) cnt;
  417.          otmp->owt = weight(otmp);
  418.          context.botl = 1;
  419.          return otmp;
  420.      }
  421.  
  422.      /* check for single character object class code ("/" for wand, &c) */
  423.      if (strlen(bp) == 1 && (i = def_char_to_objclass(*bp)) < MAXOCLASSES
  424.          && i > ILLOBJ_CLASS && (i != VENOM_CLASS || wizard)) {
  425.          oclass = i;
  426.          goto any;
  427.      }
  428.  
  429.      /* Search for class names: XXXXX potion, scroll of XXXXX.  Avoid */
  430.      /* false hits on, e.g., rings for "ring mail". */
  431.      if (strncmpi(bp, "enchant ", 8) && strncmpi(bp, "destroy ", 8)
  432.          && strncmpi(bp, "detect food", 11)
  433.          && strncmpi(bp, "food detection", 14) && strncmpi(bp, "ring mail", 9)
  434.          && strncmpi(bp, "studded leather armor", 21)
  435.          && strncmpi(bp, "leather armor", 13)
  436.          && strncmpi(bp, "tooled horn", 11) && strncmpi(bp, "food ration", 11)
  437.          && strncmpi(bp, "meat ring", 9))
  438.          for (i = 0; i < (int) (sizeof wrpsym); i++) {
  439.              register int j = strlen(wrp[i]);
  440.              if (!strncmpi(bp, wrp[i], j)) {
  441.                  oclass = wrpsym[i];
  442.                  if (oclass != AMULET_CLASS) {
  443.                      bp += j;
  444.                      if (!strncmpi(bp, " of ", 4))
  445.                          actualn = bp + 4;
  446.                      /* else if(*bp) ?? */
  447.                  } else
  448.                      actualn = bp;
  449.                  goto srch;
  450.              }
  451.              if (!BSTRCMPI(bp, p - j, wrp[i])) {
  452.                  oclass = wrpsym[i];
  453.                  p -= j;
  454.                  *p = 0;
  455.                  if (p > bp && p[-1] == ' ')
  456.                      p[-1] = 0;
  457.                  actualn = dn = bp;
  458.                  goto srch;
  459.              }
  460.          }
  461.  
  462.      /* Wishing in wizard mode can create traps and furniture.
  463.       * Part I:  distinguish between trap and object for the two
  464.       * types of traps which have corresponding objects:  bear trap
  465.       * and land mine.  "beartrap" (object) and "bear trap" (trap)
  466.       * have a difference in spelling which we used to exploit by
  467.       * adding a special case in wishymatch(), but "land mine" is
  468.       * spelled the same either way so needs different handing.
  469.       * Since we need something else for land mine, we've dropped
  470.       * the bear trap hack so that both are handled exactly the
  471.       * same.  To get an armed trap instead of a disarmed object,
  472.       * the player can prefix either the object name or the trap
  473.       * name with "trapped " (which ordinarily applies to chests
  474.       * and tins), or append something--anything at all except for
  475.       * " object", but " trap" is suggested--to either the trap
  476.       * name or the object name.
  477.       */
  478.      if (wizard && (!strncmpi(bp, "bear", 4) || !strncmpi(bp, "land", 4))) {
  479.          boolean beartrap = (lowc(*bp) == 'b');
  480.          char *zp = bp + 4; /* skip "bear"/"land" */
  481.  
  482.          if (*zp == ' ')
  483.              ++zp; /* embedded space is optional */
  484.          if (!strncmpi(zp, beartrap ? "trap" : "mine", 4)) {
  485.              zp += 4;
  486.              if (trapped == 2 || !strcmpi(zp, " object")) {
  487.                  /* "untrapped <foo>" or "<foo> object" */
  488.                  typ = beartrap ? BEARTRAP : LAND_MINE;
  489.                  goto typfnd;
  490.              } else if (trapped == 1 || *zp != '\0') {
  491.                  /* "trapped <foo>" or "<foo> trap" (actually "<foo>*") */
  492.                  int idx = trap_to_defsym(beartrap ? BEAR_TRAP : LANDMINE);
  493.  
  494.                  /* use canonical trap spelling, skip object matching */
  495.                  Strcpy(bp, defsyms[idx].explanation);
  496.                  goto wiztrap;
  497.              }
  498.              /* [no prefix or suffix; we're going to end up matching
  499.                 the object name and getting a disarmed trap object] */
  500.          }
  501.      }
  502.  
  503.  retry:
  504.      /* "grey stone" check must be before general "stone" */
  505.      for (i = 0; i < SIZE(o_ranges); i++)
  506.          if (!strcmpi(bp, o_ranges[i].name)) {
  507.              typ = rnd_class(o_ranges[i].f_o_range, o_ranges[i].l_o_range);
  508.              goto typfnd;
  509.          }
  510.  
  511.      if (!BSTRCMPI(bp, p - 6, " stone") || !BSTRCMPI(bp, p - 4, " gem")) {
  512.          p[!strcmpi(p - 4, " gem") ? -4 : -6] = '\0';
  513.          oclass = GEM_CLASS;
  514.          dn = actualn = bp;
  515.          goto srch;
  516.      } else if (!strcmpi(bp, "looking glass")) {
  517.          ; /* avoid false hit on "* glass" */
  518.      } else if (!BSTRCMPI(bp, p - 6, " glass") || !strcmpi(bp, "glass")) {
  519.          register char *g = bp;
  520.          if (strstri(g, "broken"))
  521.              return (struct obj *) 0;
  522.          if (!strncmpi(g, "worthless ", 10))
  523.              g += 10;
  524.          if (!strncmpi(g, "piece of ", 9))
  525.              g += 9;
  526.          if (!strncmpi(g, "colored ", 8))
  527.              g += 8;
  528.          else if (!strncmpi(g, "coloured ", 9))
  529.              g += 9;
  530.          if (!strcmpi(g, "glass")) { /* choose random color */
  531.              /* 9 different kinds */
  532.              typ = LAST_GEM + rnd(9);
  533.              if (objects[typ].oc_class == GEM_CLASS)
  534.                  goto typfnd;
  535.              else
  536.                  typ = 0; /* somebody changed objects[]? punt */
  537.          } else { /* try to construct canonical form */
  538.              char tbuf[BUFSZ];
  539.  
  540.              Strcpy(tbuf, "worthless piece of ");
  541.              Strcat(tbuf, g); /* assume it starts with the color */
  542.              Strcpy(bp, tbuf);
  543.          }
  544.      }
  545.  
  546.      actualn = bp;
  547.      if (!dn)
  548.          dn = actualn; /* ex. "skull cap" */
  549.  srch:
  550.      /* check real names of gems first */
  551.      if (!oclass && actualn) {
  552.          for (i = bases[GEM_CLASS]; i <= LAST_GEM; i++) {
  553.              register const char *zn;
  554.  
  555.              if ((zn = OBJ_NAME(objects[i])) && !strcmpi(actualn, zn)) {
  556.                  typ = i;
  557.                  goto typfnd;
  558.              }
  559.          }
  560.      }
  561.      i = oclass ? bases[(int) oclass] : 1;
  562.      while (i < NUM_OBJECTS && (!oclass || objects[i].oc_class == oclass)) {
  563.          register const char *zn;
  564.  
  565.          if (actualn && (zn = OBJ_NAME(objects[i])) != 0
  566.              && wishymatch(actualn, zn, TRUE)) {
  567.              typ = i;
  568.              goto typfnd;
  569.          }
  570.          if (dn && (zn = OBJ_DESCR(objects[i])) != 0
  571.              && wishymatch(dn, zn, FALSE)) {
  572.              /* don't match extra descriptions (w/o real name) */
  573.              if (!OBJ_NAME(objects[i]))
  574.                  return (struct obj *) 0;
  575.              typ = i;
  576.              goto typfnd;
  577.          }
  578.          if (un && (zn = objects[i].oc_uname) != 0
  579.              && wishymatch(un, zn, FALSE)) {
  580.              typ = i;
  581.              goto typfnd;
  582.          }
  583.          i++;
  584.      }
  585.      if (actualn) {
  586.          struct Jitem *j = Japanese_items;
  587.  
  588.          while (j->item) {
  589.              if (actualn && !strcmpi(actualn, j->name)) {
  590.                  typ = j->item;
  591.                  goto typfnd;
  592.              }
  593.              j++;
  594.          }
  595.      }
  596.      /* if we've stripped off "armor" and failed to match anything
  597.         in objects[], append "mail" and try again to catch misnamed
  598.         requests like "plate armor" and "yellow dragon scale armor" */
  599.      if (oclass == ARMOR_CLASS && !strstri(bp, "mail")) {
  600.          /* modifying bp's string is ok; we're about to resort
  601.             to random armor if this also fails to match anything */
  602.          Strcat(bp, " mail");
  603.          goto retry;
  604.      }
  605.      if (!strcmpi(bp, "spinach")) {
  606.          contents = SPINACH;
  607.          typ = TIN;
  608.          goto typfnd;
  609.      }
  610.      /* Note: not strcmpi.  2 fruits, one capital, one not, are possible.
  611.         Also not strncmp.  We used to ignore trailing text with it, but
  612.         that resulted in "grapefruit" matching "grape" if the latter came
  613.         earlier than the former in the fruit list. */
  614.      {
  615.          char *fp;
  616.          int l, cntf;
  617.          int blessedf, iscursedf, uncursedf, halfeatenf;
  618.  
  619.          blessedf = iscursedf = uncursedf = halfeatenf = 0;
  620.          cntf = 0;
  621.  
  622.          fp = fruitbuf;
  623.          for (;;) {
  624.              if (!fp || !*fp)
  625.                  break;
  626.              if (!strncmpi(fp, "an ", l = 3) || !strncmpi(fp, "a ", l = 2)) {
  627.                  cntf = 1;
  628.              } else if (!cntf && digit(*fp)) {
  629.                  cntf = atoi(fp);
  630.                  while (digit(*fp))
  631.                      fp++;
  632.                  while (*fp == ' ')
  633.                      fp++;
  634.                  l = 0;
  635.              } else if (!strncmpi(fp, "blessed ", l = 8)) {
  636.                  blessedf = 1;
  637.              } else if (!strncmpi(fp, "cursed ", l = 7)) {
  638.                  iscursedf = 1;
  639.              } else if (!strncmpi(fp, "uncursed ", l = 9)) {
  640.                  uncursedf = 1;
  641.              } else if (!strncmpi(fp, "partly eaten ", l = 13)
  642.                         || !strncmpi(fp, "partially eaten ", l = 16)) {
  643.                  halfeatenf = 1;
  644.              } else
  645.                  break;
  646.              fp += l;
  647.          }
  648.  
  649.          for (f = ffruit; f; f = f->nextf) {
  650.              /* match type: 0=none, 1=exact, 2=singular, 3=plural */
  651.              int ftyp = 0;
  652.  
  653.              if (!strcmp(fp, f->fname))
  654.                  ftyp = 1;
  655.              else if (!strcmp(fp, makesingular(f->fname)))
  656.                  ftyp = 2;
  657.              else if (!strcmp(fp, makeplural(f->fname)))
  658.                  ftyp = 3;
  659.              if (ftyp) {
  660.                  typ = SLIME_MOLD;
  661.                  blessed = blessedf;
  662.                  iscursed = iscursedf;
  663.                  uncursed = uncursedf;
  664.                  halfeaten = halfeatenf;
  665.                  /* adjust count if user explicitly asked for
  666.                     singular amount (can't happen unless fruit
  667.                     has been given an already pluralized name)
  668.                     or for plural amount */
  669.                  if (ftyp == 2 && !cntf)
  670.                      cntf = 1;
  671.                  else if (ftyp == 3 && !cntf)
  672.                      cntf = 2;
  673.                  cnt = cntf;
  674.                  ftype = f->fid;
  675.                  goto typfnd;
  676.              }
  677.          }
  678.      }
  679.  
  680.      if (!oclass && actualn) {
  681.          short objtyp;
  682.  
  683.          /* Perhaps it's an artifact specified by name, not type */
  684.          name = artifact_name(actualn, &objtyp);
  685.          if (name) {
  686.              typ = objtyp;
  687.              goto typfnd;
  688.          }
  689.      }
  690.  /* Let wizards wish for traps and furniture.
  691.   * Must come after objects check so wizards can still wish for
  692.   * trap objects like beartraps.
  693.   * Disallow such topology tweaks for WIZKIT startup wishes.
  694.   */
  695.  wiztrap:
  696.      if (wizard && !program_state.wizkit_wishing) {
  697.          struct rm *lev;
  698.          int trap, x = u.ux, y = u.uy;
  699.  
  700.          for (trap = NO_TRAP + 1; trap < TRAPNUM; trap++) {
  701.              struct trap *t;
  702.              const char *tname;
  703.  
  704.              tname = defsyms[trap_to_defsym(trap)].explanation;
  705.              if (strncmpi(tname, bp, strlen(tname)))
  706.                  continue;
  707.              /* found it; avoid stupid mistakes */
  708.              if ((trap == TRAPDOOR || trap == HOLE) && !Can_fall_thru(&u.uz))
  709.                  trap = ROCKTRAP;
  710.              if ((t = maketrap(x, y, trap)) != 0) {
  711.                  trap = t->ttyp;
  712.                  tname = defsyms[trap_to_defsym(trap)].explanation;
  713.                  pline("%s%s.", An(tname),
  714.                        (trap != MAGIC_PORTAL) ? "" : " to nowhere");
  715.              } else
  716.                  pline("Creation of %s failed.", an(tname));
  717.              return &zeroobj;
  718.          }
  719.  
  720.          /* furniture and terrain */
  721.          lev = &levl[x][y];
  722.          p = eos(bp);
  723.          if (!BSTRCMPI(bp, p - 8, "fountain")) {
  724.              lev->typ = FOUNTAIN;
  725.              level.flags.nfountains++;
  726.              if (!strncmpi(bp, "magic ", 6))
  727.                  lev->blessedftn = 1;
  728.              pline("A %sfountain.", lev->blessedftn ? "magic " : "");
  729.              newsym(x, y);
  730.              return &zeroobj;
  731.          }
  732.          if (!BSTRCMPI(bp, p - 6, "throne")) {
  733.              lev->typ = THRONE;
  734.              pline("A throne.");
  735.              newsym(x, y);
  736.              return &zeroobj;
  737.          }
  738.          if (!BSTRCMPI(bp, p - 4, "sink")) {
  739.              lev->typ = SINK;
  740.              level.flags.nsinks++;
  741.              pline("A sink.");
  742.              newsym(x, y);
  743.              return &zeroobj;
  744.          }
  745.          /* ("water" matches "potion of water" rather than terrain) */
  746.          if (!BSTRCMPI(bp, p - 4, "pool") || !BSTRCMPI(bp, p - 4, "moat")) {
  747.              lev->typ = !BSTRCMPI(bp, p - 4, "pool") ? POOL : MOAT;
  748.              del_engr_at(x, y);
  749.              pline("A %s.", (lev->typ == POOL) ? "pool" : "moat");
  750.              /* Must manually make kelp! */
  751.              water_damage_chain(level.objects[x][y], TRUE);
  752.              newsym(x, y);
  753.              return &zeroobj;
  754.          }
  755.          if (!BSTRCMPI(bp, p - 4, "lava")) { /* also matches "molten lava" */
  756.              lev->typ = LAVAPOOL;
  757.              del_engr_at(x, y);
  758.              pline("A pool of molten lava.");
  759.              if (!(Levitation || Flying))
  760.                  (void) lava_effects();
  761.              newsym(x, y);
  762.              return &zeroobj;
  763.          }
  764.  
  765.          if (!BSTRCMPI(bp, p - 5, "altar")) {
  766.              aligntyp al;
  767.  
  768.              lev->typ = ALTAR;
  769.              if (!strncmpi(bp, "chaotic ", 8))
  770.                  al = A_CHAOTIC;
  771.              else if (!strncmpi(bp, "neutral ", 8))
  772.                  al = A_NEUTRAL;
  773.              else if (!strncmpi(bp, "lawful ", 7))
  774.                  al = A_LAWFUL;
  775.              else if (!strncmpi(bp, "unaligned ", 10))
  776.                  al = A_NONE;
  777.              else /* -1 - A_CHAOTIC, 0 - A_NEUTRAL, 1 - A_LAWFUL */
  778.                  al = (!rn2(6)) ? A_NONE : rn2((int) A_LAWFUL + 2) - 1;
  779.              lev->altarmask = Align2amask(al);
  780.              pline("%s altar.", An(align_str(al)));
  781.              newsym(x, y);
  782.              return &zeroobj;
  783.          }
  784.  
  785.          if (!BSTRCMPI(bp, p - 5, "grave")
  786.              || !BSTRCMPI(bp, p - 9, "headstone")) {
  787.              make_grave(x, y, (char *) 0);
  788.              pline("%s.", IS_GRAVE(lev->typ) ? "A grave"
  789.                                              : "Can't place a grave here");
  790.              newsym(x, y);
  791.              return &zeroobj;
  792.          }
  793.  
  794.          if (!BSTRCMPI(bp, p - 4, "tree")) {
  795.              lev->typ = TREE;
  796.              pline("A tree.");
  797.              newsym(x, y);
  798.              block_point(x, y);
  799.              return &zeroobj;
  800.          }
  801.  
  802.          if (!BSTRCMPI(bp, p - 4, "bars")) {
  803.              lev->typ = IRONBARS;
  804.              pline("Iron bars.");
  805.              newsym(x, y);
  806.              return &zeroobj;
  807.          }
  808.      }
  809.  
  810.      if (!oclass && !typ) {
  811.          if (!strncmpi(bp, "polearm", 7)) {
  812.              typ = rnd_otyp_by_wpnskill(P_POLEARMS);
  813.              goto typfnd;
  814.          } else if (!strncmpi(bp, "hammer", 6)) {
  815.              typ = rnd_otyp_by_wpnskill(P_HAMMER);
  816.              goto typfnd;
  817.          }
  818.      }
  819.  
  820.      if (!oclass)
  821.          return ((struct obj *) 0);
  822.  any:
  823.      if (!oclass)
  824.          oclass = wrpsym[rn2((int) sizeof(wrpsym))];
  825.  typfnd:
  826.      if (typ)
  827.          oclass = objects[typ].oc_class;
  828.  
  829.      /* handle some objects that are only allowed in wizard mode */
  830.      if (typ && !wizard) {
  831.          switch (typ) {
  832.          case AMULET_OF_YENDOR:
  833.              typ = FAKE_AMULET_OF_YENDOR;
  834.              break;
  835.          case CANDELABRUM_OF_INVOCATION:
  836.              typ = rnd_class(TALLOW_CANDLE, WAX_CANDLE);
  837.              break;
  838.          case BELL_OF_OPENING:
  839.              typ = BELL;
  840.              break;
  841.          case SPE_BOOK_OF_THE_DEAD:
  842.              typ = SPE_BLANK_PAPER;
  843.              break;
  844.          case MAGIC_LAMP:
  845.              typ = OIL_LAMP;
  846.              break;
  847.          default:
  848.              /* catch any other non-wishable objects (venom) */
  849.              if (objects[typ].oc_nowish)
  850.                  return (struct obj *) 0;
  851.              break;
  852.          }
  853.      }
  854.  
  855.      /*
  856.       * Create the object, then fine-tune it.
  857.       */
  858.      otmp = typ ? mksobj(typ, TRUE, FALSE) : mkobj(oclass, FALSE);
  859.      typ = otmp->otyp, oclass = otmp->oclass; /* what we actually got */
  860.  
  861.      if (islit && (typ == OIL_LAMP || typ == MAGIC_LAMP || typ == BRASS_LANTERN
  862.                    || Is_candle(otmp) || typ == POT_OIL)) {
  863.          place_object(otmp, u.ux, u.uy); /* make it viable light source */
  864.          begin_burn(otmp, FALSE);
  865.          obj_extract_self(otmp); /* now release it for caller's use */
  866.      }
  867.  
  868.      /* if player specified a reasonable count, maybe honor it */
  869.      if (cnt > 0 && objects[typ].oc_merge
  870.          && (wizard || cnt < rnd(6) || (cnt <= 7 && Is_candle(otmp))
  871.              || (cnt <= 20 && ((oclass == WEAPON_CLASS && is_ammo(otmp))
  872.                                || typ == ROCK || is_missile(otmp)))))
  873.          otmp->quan = (long) cnt;
  874.  
  875.      if (oclass == VENOM_CLASS)
  876.          otmp->spe = 1;
  877.  
  878.      if (spesgn == 0) {
  879.          spe = otmp->spe;
  880.      } else if (wizard) {
  881.          ; /* no alteration to spe */
  882.      } else if (oclass == ARMOR_CLASS || oclass == WEAPON_CLASS
  883.                 || is_weptool(otmp)
  884.                 || (oclass == RING_CLASS && objects[typ].oc_charged)) {
  885.          if (spe > rnd(5) && spe > otmp->spe)
  886.              spe = 0;
  887.          if (spe > 2 && Luck < 0)
  888.              spesgn = -1;
  889.      } else {
  890.          if (oclass == WAND_CLASS) {
  891.              if (spe > 1 && spesgn == -1)
  892.                  spe = 1;
  893.          } else {
  894.              if (spe > 0 && spesgn == -1)
  895.                  spe = 0;
  896.          }
  897.          if (spe > otmp->spe)
  898.              spe = otmp->spe;
  899.      }
  900.  
  901.      if (spesgn == -1)
  902.          spe = -spe;
  903.  
  904.      /* set otmp->spe.  This may, or may not, use spe... */
  905.      switch (typ) {
  906.      case TIN:
  907.          if (contents == EMPTY) {
  908.              otmp->corpsenm = NON_PM;
  909.              otmp->spe = 0;
  910.          } else if (contents == SPINACH) {
  911.              otmp->corpsenm = NON_PM;
  912.              otmp->spe = 1;
  913.          }
  914.          break;
  915.      case TOWEL:
  916.          if (wetness)
  917.              otmp->spe = wetness;
  918.          break;
  919.      case SLIME_MOLD:
  920.          otmp->spe = ftype;
  921.      /* Fall through */
  922.      case SKELETON_KEY:
  923.      case CHEST:
  924.      case LARGE_BOX:
  925.      case HEAVY_IRON_BALL:
  926.      case IRON_CHAIN:
  927.      case STATUE:
  928.          /* otmp->cobj already done in mksobj() */
  929.          break;
  930.  #ifdef MAIL
  931.      case SCR_MAIL:
  932.          otmp->spe = 1;
  933.          break;
  934.  #endif
  935.      case WAN_WISHING:
  936.          if (!wizard) {
  937.              otmp->spe = (rn2(10) ? -1 : 0);
  938.              break;
  939.          }
  940.      /* fall through, if wizard */
  941.      default:
  942.          otmp->spe = spe;
  943.      }
  944.  
  945.      /* set otmp->corpsenm or dragon scale [mail] */
  946.      if (mntmp >= LOW_PM) {
  947.          if (mntmp == PM_LONG_WORM_TAIL)
  948.              mntmp = PM_LONG_WORM;
  949.  
  950.          switch (typ) {
  951.          case TIN:
  952.              otmp->spe = 0; /* No spinach */
  953.              if (dead_species(mntmp, FALSE)) {
  954.                  otmp->corpsenm = NON_PM; /* it's empty */
  955.              } else if (!(mons[mntmp].geno & G_UNIQ)
  956.                         && !(mvitals[mntmp].mvflags & G_NOCORPSE)
  957.                         && mons[mntmp].cnutrit != 0) {
  958.                  otmp->corpsenm = mntmp;
  959.              }
  960.              break;
  961.          case CORPSE:
  962.              if (!(mons[mntmp].geno & G_UNIQ)
  963.                  && !(mvitals[mntmp].mvflags & G_NOCORPSE)) {
  964.                  if (mons[mntmp].msound == MS_GUARDIAN)
  965.                      mntmp = genus(mntmp, 1);
  966.                  set_corpsenm(otmp, mntmp);
  967.              }
  968.              break;
  969.          case FIGURINE:
  970.              if (!(mons[mntmp].geno & G_UNIQ) && !is_human(&mons[mntmp])
  971.  #ifdef MAIL
  972.                  && mntmp != PM_MAIL_DAEMON
  973.  #endif
  974.                  )
  975.                  otmp->corpsenm = mntmp;
  976.              break;
  977.          case EGG:
  978.              mntmp = can_be_hatched(mntmp);
  979.              /* this also sets hatch timer if appropriate */
  980.              set_corpsenm(otmp, mntmp);
  981.              break;
  982.          case STATUE:
  983.              otmp->corpsenm = mntmp;
  984.              if (Has_contents(otmp) && verysmall(&mons[mntmp]))
  985.                  delete_contents(otmp); /* no spellbook */
  986.              otmp->spe = ishistoric ? STATUE_HISTORIC : 0;
  987.              break;
  988.          case SCALE_MAIL:
  989.              /* Dragon mail - depends on the order of objects & dragons. */
  990.              if (mntmp >= PM_GRAY_DRAGON && mntmp <= PM_YELLOW_DRAGON)
  991.                  otmp->otyp = GRAY_DRAGON_SCALE_MAIL + mntmp - PM_GRAY_DRAGON;
  992.              break;
  993.          }
  994.      }
  995.  
  996.      /* set blessed/cursed -- setting the fields directly is safe
  997.       * since weight() is called below and addinv() will take care
  998.       * of luck */
  999.      if (iscursed) {
  1000.          curse(otmp);
  1001.      } else if (uncursed) {
  1002.          otmp->blessed = 0;
  1003.          otmp->cursed = (Luck < 0 && !wizard);
  1004.      } else if (blessed) {
  1005.          otmp->blessed = (Luck >= 0 || wizard);
  1006.          otmp->cursed = (Luck < 0 && !wizard);
  1007.      } else if (spesgn < 0) {
  1008.          curse(otmp);
  1009.      }
  1010.  
  1011.      /* set eroded */
  1012.      if (is_damageable(otmp) || otmp->otyp == CRYSKNIFE) {
  1013.          if (eroded && (is_flammable(otmp) || is_rustprone(otmp)))
  1014.              otmp->oeroded = eroded;
  1015.          if (eroded2 && (is_corrodeable(otmp) || is_rottable(otmp)))
  1016.              otmp->oeroded2 = eroded2;
  1017.  
  1018.          /* set erodeproof */
  1019.          if (erodeproof && !eroded && !eroded2)
  1020.              otmp->oerodeproof = (Luck >= 0 || wizard);
  1021.      }
  1022.  
  1023.      /* set otmp->recharged */
  1024.      if (oclass == WAND_CLASS) {
  1025.          /* prevent wishing abuse */
  1026.          if (otmp->otyp == WAN_WISHING && !wizard)
  1027.              rechrg = 1;
  1028.          otmp->recharged = (unsigned) rechrg;
  1029.      }
  1030.  
  1031.      /* set poisoned */
  1032.      if (ispoisoned) {
  1033.          if (is_poisonable(otmp))
  1034.              otmp->opoisoned = (Luck >= 0);
  1035.          else if (oclass == FOOD_CLASS)
  1036.              /* try to taint by making it as old as possible */
  1037.              otmp->age = 1L;
  1038.      }
  1039.      /* and [un]trapped */
  1040.      if (trapped) {
  1041.          if (Is_box(otmp) || typ == TIN)
  1042.              otmp->otrapped = (trapped == 1);
  1043.      }
  1044.  
  1045.      if (isgreased)
  1046.          otmp->greased = 1;
  1047.  
  1048.      if (isdiluted && otmp->oclass == POTION_CLASS && otmp->otyp != POT_WATER)
  1049.          otmp->odiluted = 1;
  1050.  
  1051.      /* set tin variety */
  1052.      if (otmp->otyp == TIN && tvariety >= 0 && (rn2(4) || wizard))
  1053.          set_tin_variety(otmp, tvariety);
  1054.  
  1055.      if (name) {
  1056.          const char *aname;
  1057.          short objtyp;
  1058.  
  1059.          /* an artifact name might need capitalization fixing */
  1060.          aname = artifact_name(name, &objtyp);
  1061.          if (aname && objtyp == otmp->otyp)
  1062.              name = aname;
  1063.  
  1064.          /* 3.6.0 tribute - fix up novel */
  1065.          if (otmp->otyp == SPE_NOVEL) {
  1066.              const char *novelname;
  1067.  
  1068.              novelname = lookup_novel(name, &otmp->novelidx);
  1069.              if (novelname)
  1070.                  name = novelname;
  1071.          }
  1072.  
  1073.          otmp = oname(otmp, name);
  1074.          if (otmp->oartifact) {
  1075.              otmp->quan = 1L;
  1076.              u.uconduct.wisharti++; /* KMH, conduct */
  1077.          }
  1078.      }
  1079.  
  1080.      /* more wishing abuse: don't allow wishing for certain artifacts */
  1081.      /* and make them pay; charge them for the wish anyway! */
  1082.      if ((is_quest_artifact(otmp)
  1083.           || (otmp->oartifact && rn2(nartifact_exist()) > 1)) && !wizard) {
  1084.          artifact_exists(otmp, safe_oname(otmp), FALSE);
  1085.          obfree(otmp, (struct obj *) 0);
  1086.          otmp = &zeroobj;
  1087.          pline("For a moment, you feel %s in your %s, but it disappears!",
  1088.                something, makeplural(body_part(HAND)));
  1089.      }
  1090.  
  1091.      if (halfeaten && otmp->oclass == FOOD_CLASS) {
  1092.          if (otmp->otyp == CORPSE)
  1093.              otmp->oeaten = mons[otmp->corpsenm].cnutrit;
  1094.          else
  1095.              otmp->oeaten = objects[otmp->otyp].oc_nutrition;
  1096.          /* (do this adjustment before setting up object's weight) */
  1097.          consume_oeaten(otmp, 1);
  1098.      }
  1099.      otmp->owt = weight(otmp);
  1100.      if (very && otmp->otyp == HEAVY_IRON_BALL)
  1101.          otmp->owt += 160;
  1102.  
  1103.      return otmp;
  1104.  }
  1105.  

rnd_class

  1.  int
  2.  rnd_class(first, last)
  3.  int first, last;
  4.  {
  5.      int i, x, sum = 0;
  6.  
  7.      if (first == last)
  8.          return first;
  9.      for (i = first; i <= last; i++)
  10.          sum += objects[i].oc_prob;
  11.      if (!sum) /* all zero */
  12.          return first + rn2(last - first + 1);
  13.      x = rnd(sum);
  14.      for (i = first; i <= last; i++)
  15.          if (objects[i].oc_prob && (x -= objects[i].oc_prob) <= 0)
  16.              return i;
  17.      return 0;
  18.  }
  19.  

Japanese_item_name

  1.  STATIC_OVL const char *
  2.  Japanese_item_name(i)
  3.  int i;
  4.  {
  5.      struct Jitem *j = Japanese_items;
  6.  
  7.      while (j->item) {
  8.          if (i == j->item)
  9.              return j->name;
  10.          j++;
  11.      }
  12.      return (const char *) 0;
  13.  }
  14.  

suit_simple_name

  1.  const char *
  2.  suit_simple_name(suit)
  3.  struct obj *suit;
  4.  {
  5.      const char *suitnm, *esuitp;
  6.  
  7.      if (Is_dragon_mail(suit))
  8.          return "dragon mail"; /* <color> dragon scale mail */
  9.      else if (Is_dragon_scales(suit))
  10.          return "dragon scales";
  11.      suitnm = OBJ_NAME(objects[suit->otyp]);
  12.      esuitp = eos((char *) suitnm);
  13.      if (strlen(suitnm) > 5 && !strcmp(esuitp - 5, " mail"))
  14.          return "mail"; /* most suits fall into this category */
  15.      else if (strlen(suitnm) > 7 && !strcmp(esuitp - 7, " jacket"))
  16.          return "jacket"; /* leather jacket */
  17.      /* suit is lame but armor is ambiguous and body armor is absurd */
  18.      return "suit";
  19.  }
  20.  

cloak_simple_name

  1.  const char *
  2.  cloak_simple_name(cloak)
  3.  struct obj *cloak;
  4.  {
  5.      if (cloak) {
  6.          switch (cloak->otyp) {
  7.          case ROBE:
  8.              return "robe";
  9.          case MUMMY_WRAPPING:
  10.              return "wrapping";
  11.          case ALCHEMY_SMOCK:
  12.              return (objects[cloak->otyp].oc_name_known && cloak->dknown)
  13.                         ? "smock"
  14.                         : "apron";
  15.          default:
  16.              break;
  17.          }
  18.      }
  19.      return "cloak";
  20.  }
  21.  

helm_simple_name

  1.  /* helm vs hat for messages */
  2.  const char *
  3.  helm_simple_name(helmet)
  4.  struct obj *helmet;
  5.  {
  6.      /*
  7.       *  There is some wiggle room here; the result has been chosen
  8.       *  for consistency with the "protected by hard helmet" messages
  9.       *  given for various bonks on the head:  headgear that provides
  10.       *  such protection is a "helm", that which doesn't is a "hat".
  11.       *
  12.       *      elven leather helm / leather hat    -> hat
  13.       *      dwarvish iron helm / hard hat       -> helm
  14.       *  The rest are completely straightforward:
  15.       *      fedora, cornuthaum, dunce cap       -> hat
  16.       *      all other types of helmets          -> helm
  17.       */
  18.      return (helmet && !is_metallic(helmet)) ? "hat" : "helm";
  19.  }
  20.  

mimic_obj_name

  1.  const char *
  2.  mimic_obj_name(mtmp)
  3.  struct monst *mtmp;
  4.  {
  5.      if (mtmp->m_ap_type == M_AP_OBJECT
  6.          && mtmp->mappearance != STRANGE_OBJECT) {
  7.          int idx = objects[mtmp->mappearance].oc_descr_idx;
  8.          if (mtmp->mappearance == GOLD_PIECE)
  9.              return "gold";
  10.          return obj_descr[idx].oc_name;
  11.      }
  12.      return "whatcha-may-callit";
  13.  }
  14.  

safe_qbuf

  1.  /*
  2.   * Construct a query prompt string, based around an object name, which is
  3.   * guaranteed to fit within [QBUFSZ].  Takes an optional prefix, three
  4.   * choices for filling in the middle (two object formatting functions and a
  5.   * last resort literal which should be very short), and an optional suffix.
  6.   */
  7.  char *
  8.  safe_qbuf(qbuf, qprefix, qsuffix, obj, func, altfunc, lastR)
  9.  char *qbuf; /* output buffer */
  10.  const char *qprefix, *qsuffix;
  11.  struct obj *obj;
  12.  char *FDECL((*func), (OBJ_P)), *FDECL((*altfunc), (OBJ_P));
  13.  const char *lastR;
  14.  {
  15.      char *bufp, *endp;
  16.      /* convert size_t (or int for ancient systems) to ordinary unsigned */
  17.      unsigned len, lenlimit,
  18.          len_qpfx = (unsigned) (qprefix ? strlen(qprefix) : 0),
  19.          len_qsfx = (unsigned) (qsuffix ? strlen(qsuffix) : 0),
  20.          len_lastR = (unsigned) strlen(lastR);
  21.  
  22.      lenlimit = QBUFSZ - 1;
  23.      endp = qbuf + lenlimit;
  24.      /* sanity check, aimed mainly at paniclog (it's conceivable for
  25.         the result of short_oname() to be shorter than the length of
  26.         the last resort string, but we ignore that possibility here) */
  27.      if (len_qpfx > lenlimit)
  28.          impossible("safe_qbuf: prefix too long (%u characters).", len_qpfx);
  29.      else if (len_qpfx + len_qsfx > lenlimit)
  30.          impossible("safe_qbuf: suffix too long (%u + %u characters).",
  31.                     len_qpfx, len_qsfx);
  32.      else if (len_qpfx + len_lastR + len_qsfx > lenlimit)
  33.          impossible("safe_qbuf: filler too long (%u + %u + %u characters).",
  34.                     len_qpfx, len_lastR, len_qsfx);
  35.  
  36.      /* the output buffer might be the same as the prefix if caller
  37.         has already partially filled it */
  38.      if (qbuf == qprefix) {
  39.          /* prefix is already in the buffer */
  40.          *endp = '\0';
  41.      } else if (qprefix) {
  42.          /* put prefix into the buffer */
  43.          (void) strncpy(qbuf, qprefix, lenlimit);
  44.          *endp = '\0';
  45.      } else {
  46.          /* no prefix; output buffer starts out empty */
  47.          qbuf[0] = '\0';
  48.      }
  49.      len = (unsigned) strlen(qbuf);
  50.  
  51.      if (len + len_lastR + len_qsfx > lenlimit) {
  52.          /* too long; skip formatting, last resort output is truncated */
  53.          if (len < lenlimit) {
  54.              (void) strncpy(&qbuf[len], lastR, lenlimit - len);
  55.              *endp = '\0';
  56.              len = (unsigned) strlen(qbuf);
  57.              if (qsuffix && len < lenlimit) {
  58.                  (void) strncpy(&qbuf[len], qsuffix, lenlimit - len);
  59.                  *endp = '\0';
  60.                  /* len = (unsigned) strlen(qbuf); */
  61.              }
  62.          }
  63.      } else {
  64.          /* suffix and last resort are guaranteed to fit */
  65.          len += len_qsfx; /* include the pending suffix */
  66.          /* format the object */
  67.          bufp = short_oname(obj, func, altfunc, lenlimit - len);
  68.          if (len + strlen(bufp) <= lenlimit)
  69.              Strcat(qbuf, bufp); /* formatted name fits */
  70.          else
  71.              Strcat(qbuf, lastR); /* use last resort */
  72.          releaseobuf(bufp);
  73.  
  74.          if (qsuffix)
  75.              Strcat(qbuf, qsuffix);
  76.      }
  77.      /* assert( strlen(qbuf) < QBUFSZ ); */
  78.      return qbuf;
  79.  }
  80.  
  81.  /*objnam.c*/