]> granicus.if.org Git - nethack/commitdiff
Merge branch 'master' into derek-farming
authorDerek S. Ray <derekray@gmail.com>
Wed, 25 Mar 2015 21:23:59 +0000 (17:23 -0400)
committerDerek S. Ray <derekray@gmail.com>
Wed, 25 Mar 2015 21:23:59 +0000 (17:23 -0400)
* master: (49 commits)
  Fix 'fetch' syntax, include sparkly 'git log' trick
  Show object symbols in menu headings
  Fix a memory leak on termination.
  ...

Conflicts:
include/obj.h
src/do.c
src/files.c
src/hack.c
src/invent.c
src/mkobj.c
src/mon.c
src/objnam.c

1  2 
include/extern.h
include/obj.h
src/do.c
src/files.c
src/hack.c
src/invent.c
src/mkobj.c
src/mon.c
src/objnam.c

Simple merge
diff --cc include/obj.h
Simple merge
diff --cc src/do.c
index 620afe8624cd7a97bb10cb3207141b9827a76ed4,b387752d64a1b5566b4fd9eef9e27274a439806e..1fe18d7c5d206f72ee5a8e1767c5bf604ab78bb2
+++ b/src/do.c
@@@ -225,24 -217,29 +225,29 @@@ const char *verb
  
  void
  doaltarobj(obj)  /* obj is an object dropped on an altar */
 -      register struct obj *obj;
 +    register struct obj *obj;
  {
 -      if (Blind)
 -              return;
 +    if (Blind)
 +        return;
  
-     /* KMH, conduct */
-     u.uconduct.gnostic++;
+       if (obj->oclass != COIN_CLASS) {
+           /* KMH, conduct */
+           u.uconduct.gnostic++;
+       } else {
+           /* coins don't have bless/curse status */
+           obj->blessed = obj->cursed = 0;
+       }
  
-     if ((obj->blessed || obj->cursed) && obj->oclass != COIN_CLASS) {
+       if (obj->blessed || obj->cursed) {
 -              There("is %s flash as %s %s the altar.",
 -                      an(hcolor(obj->blessed ? NH_AMBER : NH_BLACK)),
 -                      doname(obj), otense(obj, "hit"));
 -              if (!Hallucination) obj->bknown = 1;
 -      } else {
 -              pline("%s %s on the altar.", Doname2(obj),
 -                      otense(obj, "land"));
 +        There("is %s flash as %s %s the altar.",
 +            an(hcolor(obj->blessed ? NH_AMBER : NH_BLACK)),
 +            doname(obj), otense(obj, "hit"));
 +        if (!Hallucination) obj->bknown = 1;
 +    } else {
 +        pline("%s %s on the altar.", Doname2(obj),
 +            otense(obj, "land"));
-         obj->bknown = 1;
+               if (obj->oclass != COIN_CLASS) obj->bknown = 1;
 -      }
 +    }
  }
  
  STATIC_OVL
@@@ -993,414 -990,418 +998,418 @@@ goto_level(newlevel, at_stairs, falling
  d_level *newlevel;
  boolean at_stairs, falling, portal;
  {
 -      int fd, l_idx;
 -      xchar new_ledger;
 -      boolean cant_go_back,
 -              up = (depth(newlevel) < depth(&u.uz)),
 -              newdungeon = (u.uz.dnum != newlevel->dnum),
 -              was_in_W_tower = In_W_tower(u.ux, u.uy, &u.uz),
 -              familiar = FALSE;
 -      boolean new = FALSE;    /* made a new level? */
 -      struct monst *mtmp;
 -      char whynot[BUFSZ];
 -
 -      if (dunlev(newlevel) > dunlevs_in_dungeon(newlevel))
 -              newlevel->dlevel = dunlevs_in_dungeon(newlevel);
 -      if (newdungeon && In_endgame(newlevel)) { /* 1st Endgame Level !!! */
 -              if (!u.uhave.amulet) return;    /* must have the Amulet */
 -              if (!wizard)    /* wizard ^V can bypass Earth level */
 -              assign_level(newlevel, &earth_level);   /* (redundant) */
 -      }
 -      new_ledger = ledger_no(newlevel);
 -      if (new_ledger <= 0)
 -              done(ESCAPED);  /* in fact < 0 is impossible */
 +    int fd, l_idx;
 +    xchar new_ledger;
 +    boolean cant_go_back,
 +        up = (depth(newlevel) < depth(&u.uz)),
 +        newdungeon = (u.uz.dnum != newlevel->dnum),
 +        was_in_W_tower = In_W_tower(u.ux, u.uy, &u.uz),
 +        familiar = FALSE;
 +    boolean new = FALSE;      /* made a new level? */
 +    struct monst *mtmp;
 +    char whynot[BUFSZ];
 +
 +    if (dunlev(newlevel) > dunlevs_in_dungeon(newlevel))
 +        newlevel->dlevel = dunlevs_in_dungeon(newlevel);
 +    if (newdungeon && In_endgame(newlevel)) { /* 1st Endgame Level !!! */
 +        if (!u.uhave.amulet) return;  /* must have the Amulet */
 +        if (!wizard)  /* wizard ^V can bypass Earth level */
 +        assign_level(newlevel, &earth_level); /* (redundant) */
 +    }
 +    new_ledger = ledger_no(newlevel);
 +    if (new_ledger <= 0)
 +        done(ESCAPED);        /* in fact < 0 is impossible */
  
 -      /* If you have the amulet and are trying to get out of Gehennom, going
 -       * up a set of stairs sometimes does some very strange things!
 +    /* If you have the amulet and are trying to get out of Gehennom, going
 +     * up a set of stairs sometimes does some very strange things!
-      * Biased against law and towards chaos, but not nearly as strongly
-      * as it used to be (prior to 3.2.0).
-      * Odds:      old                             new
-      *        "up"    L      N      C         "up"    L      N      C
-      *         +1   75.0   75.0   75.0         +1   75.0   75.0   75.0
-      *          0    0.0   12.5   25.0          0    6.25   8.33  12.5
-      *         -1    8.33   4.17   0.0         -1    6.25   8.33  12.5
-      *         -2    8.33   4.17   0.0         -2    6.25   8.33   0.0
-      *         -3    8.33   4.17   0.0         -3    6.25   0.0    0.0
+        * Biased against law and towards chaos. (The chance to be sent
+        * down multiple levels when attempting to go up are significantly
+        * less than the corresponding comment in older versions indicated
+        * due to overlooking the effect of the call to assign_rnd_lvl().)
+        *
+        * Odds for making it to the next level up, or of being sent down:
+        *      "up"    L      N      C
+        *       +1   75.0   75.0   75.0
+        *        0    6.25   8.33  12.5
+        *       -1   11.46  12.50  12.5
+        *       -2    5.21   4.17   0.0
+        *       -3    2.08   0.0    0.0
 -       */
 -      if (Inhell && up && u.uhave.amulet && !newdungeon && !portal &&
 -                              (dunlev(&u.uz) < dunlevs_in_dungeon(&u.uz)-3)) {
 -              if (!rn2(4)) {
 -                  int odds = 3 + (int)u.ualign.type,          /* 2..4 */
 -                      diff = odds <= 1 ? 0 : rn2(odds);       /* paranoia */
 -
 -                  if (diff != 0) {
 -                      assign_rnd_level(newlevel, &u.uz, diff);
 -                      /* if inside the tower, stay inside */
 -                      if (was_in_W_tower &&
 -                          !On_W_tower_level(newlevel)) diff = 0;
 -                  }
 -                  if (diff == 0)
 -                      assign_level(newlevel, &u.uz);
 -
 -                  new_ledger = ledger_no(newlevel);
 -
 -                  pline("A mysterious force momentarily surrounds you...");
 -                  if (on_level(newlevel, &u.uz)) {
 -                      (void) safe_teleds(FALSE);
 -                      (void) next_to_u();
 -                      return;
 -                  } else
 -                      at_stairs = at_ladder = FALSE;
 -              }
 -      }
 +     */
 +    if (Inhell && up && u.uhave.amulet && !newdungeon && !portal &&
 +                (dunlev(&u.uz) < dunlevs_in_dungeon(&u.uz)-3)) {
 +        if (!rn2(4)) {
 +            int odds = 3 + (int)u.ualign.type,                /* 2..4 */
 +            diff = odds <= 1 ? 0 : rn2(odds); /* paranoia */
 +
 +            if (diff != 0) {
 +            assign_rnd_level(newlevel, &u.uz, diff);
 +            /* if inside the tower, stay inside */
 +            if (was_in_W_tower &&
 +                !On_W_tower_level(newlevel)) diff = 0;
 +            }
 +            if (diff == 0)
 +            assign_level(newlevel, &u.uz);
 +
 +            new_ledger = ledger_no(newlevel);
 +
 +            pline("A mysterious force momentarily surrounds you...");
 +            if (on_level(newlevel, &u.uz)) {
 +            (void) safe_teleds(FALSE);
 +            (void) next_to_u();
 +            return;
 +            } else
 +            at_stairs = at_ladder = FALSE;
 +        }
 +    }
  
 -      /* Prevent the player from going past the first quest level unless
 -       * (s)he has been given the go-ahead by the leader.
 -       */
 -      if (on_level(&u.uz, &qstart_level) && !newdungeon && !ok_to_quest()) {
 -              pline("A mysterious force prevents you from descending.");
 -              return;
 -      }
 +    /* Prevent the player from going past the first quest level unless
 +     * (s)he has been given the go-ahead by the leader.
 +     */
 +    if (on_level(&u.uz, &qstart_level) && !newdungeon && !ok_to_quest()) {
 +        pline("A mysterious force prevents you from descending.");
 +        return;
 +    }
  
 -      if (on_level(newlevel, &u.uz)) return;          /* this can happen */
 -
 -      /* tethered movement makes level change while trapped feasible */
 -      if (u.utrap && u.utraptype == TT_BURIEDBALL)
 -          buried_ball_to_punishment(); /* (before we save/leave old level) */
 -
 -      fd = currentlevel_rewrite();
 -      if (fd < 0) return;
 -
 -      if (falling) /* assuming this is only trap door or hole */
 -          impact_drop((struct obj *)0, u.ux, u.uy, newlevel->dlevel);
 -
 -      check_special_room(TRUE);               /* probably was a trap door */
 -      if (Punished) unplacebc();
 -      u.utrap = 0;                            /* needed in level_tele */
 -      fill_pit(u.ux, u.uy);
 -      u.ustuck = 0;                           /* idem */
 -      u.uinwater = 0;
 -      u.uundetected = 0;      /* not hidden, even if means are available */
 -      keepdogs(FALSE);
 -      if (u.uswallow)                         /* idem */
 -              u.uswldtim = u.uswallow = 0;
 -      recalc_mapseen(); /* recalculate map overview before we leave the level */
 -      /*
 -       *  We no longer see anything on the level.  Make sure that this
 -       *  follows u.uswallow set to null since uswallow overrides all
 -       *  normal vision.
 -       */
 -      vision_recalc(2);
 -
 -      /*
 -       * Save the level we're leaving.  If we're entering the endgame,
 -       * we can get rid of all existing levels because they cannot be
 -       * reached any more.  We still need to use savelev()'s cleanup
 -       * for the level being left, to recover dynamic memory in use and
 -       * to avoid dangling timers and light sources.
 -       */
 -      cant_go_back = (newdungeon && In_endgame(newlevel));
 -      if (!cant_go_back) {
 -          update_mlstmv();    /* current monsters are becoming inactive */
 -          bufon(fd);          /* use buffered output */
 -      }
 -      savelev(fd, ledger_no(&u.uz),
 -              cant_go_back ? FREE_SAVE : (WRITE_SAVE | FREE_SAVE));
 -      bclose(fd);
 -      if (cant_go_back) {
 -          /* discard unreachable levels; keep #0 */
 -          for (l_idx = maxledgerno(); l_idx > 0; --l_idx)
 -              delete_levelfile(l_idx);
 -          /* mark #overview data for all dungeon branches as uninteresting */
 -          for (l_idx = 0; l_idx < n_dgns; ++l_idx)
 -              remdun_mapseen(l_idx);
 -      }
 +    if (on_level(newlevel, &u.uz)) return;            /* this can happen */
 +
 +    /* tethered movement makes level change while trapped feasible */
 +    if (u.utrap && u.utraptype == TT_BURIEDBALL)
 +        buried_ball_to_punishment(); /* (before we save/leave old level) */
 +
 +    fd = currentlevel_rewrite();
 +    if (fd < 0) return;
 +
 +    if (falling) /* assuming this is only trap door or hole */
 +        impact_drop((struct obj *)0, u.ux, u.uy, newlevel->dlevel);
 +
 +    check_special_room(TRUE);         /* probably was a trap door */
 +    if (Punished) unplacebc();
 +    u.utrap = 0;                              /* needed in level_tele */
 +    fill_pit(u.ux, u.uy);
 +    u.ustuck = 0;                             /* idem */
 +    u.uinwater = 0;
 +    u.uundetected = 0;        /* not hidden, even if means are available */
 +    keepdogs(FALSE);
 +    if (u.uswallow)                           /* idem */
 +        u.uswldtim = u.uswallow = 0;
 +    recalc_mapseen(); /* recalculate map overview before we leave the level */
 +    /*
 +     *  We no longer see anything on the level.  Make sure that this
 +     *  follows u.uswallow set to null since uswallow overrides all
 +     *  normal vision.
 +     */
 +    vision_recalc(2);
 +
 +    /*
 +     * Save the level we're leaving.  If we're entering the endgame,
 +     * we can get rid of all existing levels because they cannot be
 +     * reached any more.  We still need to use savelev()'s cleanup
 +     * for the level being left, to recover dynamic memory in use and
 +     * to avoid dangling timers and light sources.
 +     */
 +    cant_go_back = (newdungeon && In_endgame(newlevel));
 +    if (!cant_go_back) {
 +        update_mlstmv();      /* current monsters are becoming inactive */
 +        bufon(fd);            /* use buffered output */
 +    }
 +    savelev(fd, ledger_no(&u.uz),
 +        cant_go_back ? FREE_SAVE : (WRITE_SAVE | FREE_SAVE));
 +    bclose(fd);
 +    if (cant_go_back) {
 +        /* discard unreachable levels; keep #0 */
 +        for (l_idx = maxledgerno(); l_idx > 0; --l_idx)
 +        delete_levelfile(l_idx);
 +        /* mark #overview data for all dungeon branches as uninteresting */
 +        for (l_idx = 0; l_idx < n_dgns; ++l_idx)
 +        remdun_mapseen(l_idx);
 +    }
  
 -      if (Is_rogue_level(newlevel) || Is_rogue_level(&u.uz))
 -              assign_graphics(Is_rogue_level(newlevel) ? ROGUESET : PRIMARY);
 +    if (Is_rogue_level(newlevel) || Is_rogue_level(&u.uz))
 +        assign_graphics(Is_rogue_level(newlevel) ? ROGUESET : PRIMARY);
  #ifdef USE_TILES
 -      substitute_tiles(newlevel);
 +    substitute_tiles(newlevel);
  #endif
 -      /* record this level transition as a potential seen branch unless using
 -       * some non-standard means of transportation (level teleport).
 -       */
 -      if ((at_stairs || falling || portal) && (u.uz.dnum != newlevel->dnum))
 -              recbranch_mapseen(&u.uz, newlevel);
 -      assign_level(&u.uz0, &u.uz);
 -      assign_level(&u.uz, newlevel);
 -      assign_level(&u.utolev, newlevel);
 -      u.utotype = 0;
 -      if (dunlev_reached(&u.uz) < dunlev(&u.uz))
 -              dunlev_reached(&u.uz) = dunlev(&u.uz);
 -      reset_rndmonst(NON_PM);   /* u.uz change affects monster generation */
 -
 -      /* set default level change destination areas */
 -      /* the special level code may override these */
 -      (void) memset((genericptr_t) &updest, 0, sizeof updest);
 -      (void) memset((genericptr_t) &dndest, 0, sizeof dndest);
 -
 -      if (!(level_info[new_ledger].flags & LFILE_EXISTS)) {
 -              /* entering this level for first time; make it now */
 -              if (level_info[new_ledger].flags & (FORGOTTEN|VISITED)) {
 -                  impossible("goto_level: returning to discarded level?");
 -                  level_info[new_ledger].flags &= ~(FORGOTTEN|VISITED);
 -              }
 -              mklev();
 -              new = TRUE;     /* made the level */
 -      } else {
 -              /* returning to previously visited level; reload it */
 -              fd = open_levelfile(new_ledger, whynot);
 -              if (fd < 0) {
 -                      pline1(whynot);
 -                      pline("Probably someone removed it.");
 -                      Strcpy(killer.name, whynot);
 -                      done(TRICKED);
 -                      /* we'll reach here if running in wizard mode */
 -                      error("Cannot continue this game.");
 -              }
 -              minit();        /* ZEROCOMP */
 -              getlev(fd, hackpid, new_ledger, FALSE);
 -              (void) close(fd);
 -              oinit(); /* reassign level dependent obj probabilities */
 -      }
 -      /* do this prior to level-change pline messages */
 -      vision_reset();         /* clear old level's line-of-sight */
 -      vision_full_recalc = 0; /* don't let that reenable vision yet */
 -      flush_screen(-1);       /* ensure all map flushes are postponed */
 -
 -      if (portal && !In_endgame(&u.uz)) {
 -          /* find the portal on the new level */
 -          register struct trap *ttrap;
 -
 -          for (ttrap = ftrap; ttrap; ttrap = ttrap->ntrap)
 -              if (ttrap->ttyp == MAGIC_PORTAL) break;
 -
 -          if (!ttrap) panic("goto_level: no corresponding portal!");
 -          seetrap(ttrap);
 -          u_on_newpos(ttrap->tx, ttrap->ty);
 -      } else if (at_stairs && !In_endgame(&u.uz)) {
 -          if (up) {
 -              if (at_ladder)
 -                  u_on_newpos(xdnladder, ydnladder);
 -              else if (newdungeon)
 -                  u_on_sstairs(1);
 -              else
 -                  u_on_dnstairs();
 -              /* you climb up the {stairs|ladder};
 -                 fly up the stairs; fly up along the ladder */
 -              pline("%s %s up%s the %s.",
 -                    (Punished && !Levitation) ? "With great effort you" :
 -                                                "You",
 -                    Flying ? "fly" : "climb",
 -                    (Flying && at_ladder) ? " along" : "",
 -                    at_ladder ? "ladder" : "stairs");
 -          } else {    /* down */
 -              if (at_ladder)
 -                  u_on_newpos(xupladder, yupladder);
 -              else if (newdungeon)
 -                  u_on_sstairs(0);
 -              else
 -                  u_on_upstairs();
 -              if (!u.dz) {
 -                  ;   /* stayed on same level? (no transit effects) */
 -              } else if (Flying) {
 -                  if (flags.verbose)
 -                      You("fly down %s.",
 -                          at_ladder ? "along the ladder" : "the stairs");
 -              } else if (near_capacity() > UNENCUMBERED ||
 -                      Punished || Fumbling) {
 -                  You("fall down the %s.", at_ladder ? "ladder" : "stairs");
 -                  if (Punished) {
 -                      drag_down();
 -                      if (carried(uball)) {
 -                          if (uwep == uball)
 -                              setuwep((struct obj *)0);
 -                          if (uswapwep == uball)
 -                              setuswapwep((struct obj *)0);
 -                          if (uquiver == uball)
 -                              setuqwep((struct obj *)0);
 -                          freeinv(uball);
 -                      }
 -                  }
 -                  /* falling off steed has its own losehp() call */
 -                  if (u.usteed)
 -                      dismount_steed(DISMOUNT_FELL);
 -                  else
 -                      losehp(Maybe_Half_Phys(rnd(3)),
 -                             at_ladder ? "falling off a ladder" :
 -                                         "tumbling down a flight of stairs",
 -                             KILLED_BY);
 -                  selftouch("Falling, you");
 -              } else {        /* ordinary descent */
 -                  if (flags.verbose)
 -                      You("%s.", at_ladder ? "climb down the ladder" :
 -                                             "descend the stairs");
 -              }
 -          }
 -      } else {        /* trap door or level_tele or In_endgame */
 -          u_on_rndspot((up ? 1 : 0) | (was_in_W_tower ? 2 : 0));
 -          if (falling) {
 -              if (Punished) ballfall();
 -              selftouch("Falling, you");
 -          }
 -      }
 +    /* record this level transition as a potential seen branch unless using
 +     * some non-standard means of transportation (level teleport).
 +     */
 +    if ((at_stairs || falling || portal) && (u.uz.dnum != newlevel->dnum))
 +        recbranch_mapseen(&u.uz, newlevel);
 +    assign_level(&u.uz0, &u.uz);
 +    assign_level(&u.uz, newlevel);
 +    assign_level(&u.utolev, newlevel);
 +    u.utotype = 0;
 +    if (dunlev_reached(&u.uz) < dunlev(&u.uz))
 +        dunlev_reached(&u.uz) = dunlev(&u.uz);
 +    reset_rndmonst(NON_PM);   /* u.uz change affects monster generation */
 +
 +    /* set default level change destination areas */
 +    /* the special level code may override these */
 +    (void) memset((genericptr_t) &updest, 0, sizeof updest);
 +    (void) memset((genericptr_t) &dndest, 0, sizeof dndest);
 +
 +    if (!(level_info[new_ledger].flags & LFILE_EXISTS)) {
 +        /* entering this level for first time; make it now */
 +        if (level_info[new_ledger].flags & (FORGOTTEN|VISITED)) {
 +            impossible("goto_level: returning to discarded level?");
 +            level_info[new_ledger].flags &= ~(FORGOTTEN|VISITED);
 +        }
 +        mklev();
 +        new = TRUE;   /* made the level */
 +    } else {
 +        /* returning to previously visited level; reload it */
 +        fd = open_levelfile(new_ledger, whynot);
 +        if (fd < 0) {
 +            pline1(whynot);
 +            pline("Probably someone removed it.");
 +            Strcpy(killer.name, whynot);
 +            done(TRICKED);
 +            /* we'll reach here if running in wizard mode */
 +            error("Cannot continue this game.");
 +        }
 +        minit();      /* ZEROCOMP */
 +        getlev(fd, hackpid, new_ledger, FALSE);
 +        (void) close(fd);
 +        oinit(); /* reassign level dependent obj probabilities */
 +    }
 +    /* do this prior to level-change pline messages */
 +    vision_reset();           /* clear old level's line-of-sight */
 +    vision_full_recalc = 0;   /* don't let that reenable vision yet */
 +    flush_screen(-1); /* ensure all map flushes are postponed */
 +
 +    if (portal && !In_endgame(&u.uz)) {
 +        /* find the portal on the new level */
 +        register struct trap *ttrap;
 +
 +        for (ttrap = ftrap; ttrap; ttrap = ttrap->ntrap)
 +        if (ttrap->ttyp == MAGIC_PORTAL) break;
 +
 +        if (!ttrap) panic("goto_level: no corresponding portal!");
 +        seetrap(ttrap);
 +        u_on_newpos(ttrap->tx, ttrap->ty);
 +    } else if (at_stairs && !In_endgame(&u.uz)) {
 +        if (up) {
 +        if (at_ladder)
 +            u_on_newpos(xdnladder, ydnladder);
 +        else if (newdungeon)
 +            u_on_sstairs(1);
 +        else
 +            u_on_dnstairs();
 +        /* you climb up the {stairs|ladder};
 +           fly up the stairs; fly up along the ladder */
 +        pline("%s %s up%s the %s.",
 +              (Punished && !Levitation) ? "With great effort you" :
 +                          "You",
 +              Flying ? "fly" : "climb",
 +              (Flying && at_ladder) ? " along" : "",
 +              at_ladder ? "ladder" : "stairs");
 +        } else {      /* down */
 +        if (at_ladder)
 +            u_on_newpos(xupladder, yupladder);
 +        else if (newdungeon)
 +            u_on_sstairs(0);
 +        else
 +            u_on_upstairs();
 +        if (!u.dz) {
 +            ; /* stayed on same level? (no transit effects) */
 +        } else if (Flying) {
 +            if (flags.verbose)
 +            You("fly down %s.",
 +                at_ladder ? "along the ladder" : "the stairs");
 +        } else if (near_capacity() > UNENCUMBERED ||
 +            Punished || Fumbling) {
 +            You("fall down the %s.", at_ladder ? "ladder" : "stairs");
 +            if (Punished) {
 +            drag_down();
 +            if (carried(uball)) {
 +                if (uwep == uball)
 +                setuwep((struct obj *)0);
 +                if (uswapwep == uball)
 +                setuswapwep((struct obj *)0);
 +                if (uquiver == uball)
 +                setuqwep((struct obj *)0);
 +                freeinv(uball);
 +            }
 +            }
 +            /* falling off steed has its own losehp() call */
 +            if (u.usteed)
 +            dismount_steed(DISMOUNT_FELL);
 +            else
 +            losehp(Maybe_Half_Phys(rnd(3)),
 +                   at_ladder ? "falling off a ladder" :
 +                       "tumbling down a flight of stairs",
 +                   KILLED_BY);
 +            selftouch("Falling, you");
 +        } else {      /* ordinary descent */
 +            if (flags.verbose)
 +            You("%s.", at_ladder ? "climb down the ladder" :
 +                           "descend the stairs");
 +        }
 +        }
 +    } else {  /* trap door or level_tele or In_endgame */
 +        u_on_rndspot((up ? 1 : 0) | (was_in_W_tower ? 2 : 0));
 +        if (falling) {
 +        if (Punished) ballfall();
 +        selftouch("Falling, you");
 +        }
 +    }
  
 -      if (Punished) placebc();
 -      obj_delivery(FALSE);
 -      losedogs();
 -      kill_genocided_monsters();  /* for those wiped out while in limbo */
 -      /*
 -       * Expire all timers that have gone off while away.  Must be
 -       * after migrating monsters and objects are delivered
 -       * (losedogs and obj_delivery).
 -       */
 -      run_timers();
 -
 -      initrack();
 -
 -      if ((mtmp = m_at(u.ux, u.uy)) != 0 && mtmp != u.usteed) {
 -          /* There's a monster at your target destination; it might be one
 -             which accompanied you--see mon_arrive(dogmove.c)--or perhaps
 -             it was already here.  Randomly move you to an adjacent spot
 -             or else the monster to any nearby location.  Prior to 3.3.0
 -             the latter was done unconditionally. */
 -          coord cc;
 -
 -          if (!rn2(2) &&
 -                  enexto(&cc, u.ux, u.uy, youmonst.data) &&
 -                  distu(cc.x, cc.y) <= 2)
 -              u_on_newpos(cc.x, cc.y);        /*[maybe give message here?]*/
 -          else
 -              mnexto(mtmp);
 -
 -          if ((mtmp = m_at(u.ux, u.uy)) != 0) {
 -              /* there was an unconditional impossible("mnearto failed")
 -                 here, but it's not impossible and we're prepared to cope
 -                 with the situation, so only say something when debugging */
 -              if (wizard) pline("(monster in hero's way)");
 -              if (!rloc(mtmp, TRUE))
 -                  /* no room to move it; send it away, to return later */
 -                  migrate_to_level(mtmp, ledger_no(&u.uz),
 -                                   MIGR_RANDOM, (coord *)0);
 -          }
 -      }
 +    if (Punished) placebc();
 +    obj_delivery(FALSE);
 +    losedogs();
 +    kill_genocided_monsters();  /* for those wiped out while in limbo */
 +    /*
 +     * Expire all timers that have gone off while away.  Must be
 +     * after migrating monsters and objects are delivered
 +     * (losedogs and obj_delivery).
 +     */
 +    run_timers();
 +
 +    initrack();
 +
 +    if ((mtmp = m_at(u.ux, u.uy)) != 0 && mtmp != u.usteed) {
 +        /* There's a monster at your target destination; it might be one
 +           which accompanied you--see mon_arrive(dogmove.c)--or perhaps
 +           it was already here.  Randomly move you to an adjacent spot
 +           or else the monster to any nearby location.  Prior to 3.3.0
 +           the latter was done unconditionally. */
 +        coord cc;
 +
 +        if (!rn2(2) &&
 +            enexto(&cc, u.ux, u.uy, youmonst.data) &&
 +            distu(cc.x, cc.y) <= 2)
 +        u_on_newpos(cc.x, cc.y);      /*[maybe give message here?]*/
 +        else
 +        mnexto(mtmp);
 +
 +        if ((mtmp = m_at(u.ux, u.uy)) != 0) {
 +        /* there was an unconditional impossible("mnearto failed")
 +           here, but it's not impossible and we're prepared to cope
 +           with the situation, so only say something when debugging */
 +        if (wizard) pline("(monster in hero's way)");
 +        if (!rloc(mtmp, TRUE))
 +            /* no room to move it; send it away, to return later */
 +            migrate_to_level(mtmp, ledger_no(&u.uz),
 +                     MIGR_RANDOM, (coord *)0);
 +        }
 +    }
  
 -      /* initial movement of bubbles just before vision_recalc */
 -      if (Is_waterlevel(&u.uz))
 -              movebubbles();
 +    /* initial movement of bubbles just before vision_recalc */
 +    if (Is_waterlevel(&u.uz))
 +        movebubbles();
  
 -      if (level_info[new_ledger].flags & FORGOTTEN) {
 -          forget_map(ALL_MAP);        /* forget the map */
 -          forget_traps();             /* forget all traps too */
 -          familiar = TRUE;
 -          level_info[new_ledger].flags &= ~FORGOTTEN;
 -      }
 +    if (level_info[new_ledger].flags & FORGOTTEN) {
 +        forget_map(ALL_MAP);  /* forget the map */
 +        forget_traps();               /* forget all traps too */
 +        familiar = TRUE;
 +        level_info[new_ledger].flags &= ~FORGOTTEN;
 +    }
  
 -      /* Reset the screen. */
 -      vision_reset();         /* reset the blockages */
 -      docrt();                /* does a full vision recalc */
 -      flush_screen(-1);
 +    /* Reset the screen. */
 +    vision_reset();           /* reset the blockages */
 +    docrt();          /* does a full vision recalc */
 +    flush_screen(-1);
  
 -      /*
 -       *  Move all plines beyond the screen reset.
 -       */
 +    /*
 +     *  Move all plines beyond the screen reset.
 +     */
  
 -      /* special levels can have a custom arrival message */
 -      deliver_splev_message();
 +    /* special levels can have a custom arrival message */
 +    deliver_splev_message();
  
 -      /* give room entrance message, if any */
 -      check_special_room(FALSE);
 +    /* give room entrance message, if any */
 +    check_special_room(FALSE);
  
 -      /* deliver objects traveling with player */
 -      obj_delivery(TRUE);
 +    /* deliver objects traveling with player */
 +    obj_delivery(TRUE);
  
 -      /* Check whether we just entered Gehennom. */
 -      if (!In_hell(&u.uz0) && Inhell) {
 -          if (Is_valley(&u.uz)) {
 -              You("arrive at the Valley of the Dead...");
 -              pline_The("odor of burnt flesh and decay pervades the air.");
 +    /* Check whether we just entered Gehennom. */
 +    if (!In_hell(&u.uz0) && Inhell) {
 +        if (Is_valley(&u.uz)) {
 +        You("arrive at the Valley of the Dead...");
 +        pline_The("odor of burnt flesh and decay pervades the air.");
  #ifdef MICRO
 -              display_nhwindow(WIN_MESSAGE, FALSE);
 +        display_nhwindow(WIN_MESSAGE, FALSE);
  #endif
 -              You_hear("groans and moans everywhere.");
 -          } else pline("It is hot here.  You smell smoke...");
 +        You_hear("groans and moans everywhere.");
 +        } else pline("It is hot here.  You smell smoke...");
+           u.uachieve.enter_gehennom = 1;
 -      }
 -      /* in case we've managed to bypass the Valley's stairway down */
 -      if (Inhell && !Is_valley(&u.uz)) u.uevent.gehennom_entered = 1;
 -
 -      if (familiar) {
 -          static const char * const fam_msgs[4] = {
 -              "You have a sense of deja vu.",
 -              "You feel like you've been here before.",
 -              "This place %s familiar...",
 -              0       /* no message */
 -          };
 -          static const char * const halu_fam_msgs[4] = {
 -              "Whoa!  Everything %s different.",
 -              "You are surrounded by twisty little passages, all alike.",
 -              "Gee, this %s like uncle Conan's place...",
 -              0       /* no message */
 -          };
 -          const char *mesg;
 -          char buf[BUFSZ];
 -          int which = rn2(4);
 -
 -          if (Hallucination)
 -              mesg = halu_fam_msgs[which];
 -          else
 -              mesg = fam_msgs[which];
 -          if (mesg && index(mesg, '%')) {
 -              Sprintf(buf, mesg, !Blind ? "looks" : "seems");
 -              mesg = buf;
 -          }
 -          if (mesg) pline1(mesg);
 -      }
 +    }
 +    /* in case we've managed to bypass the Valley's stairway down */
 +    if (Inhell && !Is_valley(&u.uz)) u.uevent.gehennom_entered = 1;
 +
 +    if (familiar) {
 +        static const char * const fam_msgs[4] = {
 +        "You have a sense of deja vu.",
 +        "You feel like you've been here before.",
 +        "This place %s familiar...",
 +        0     /* no message */
 +        };
 +        static const char * const halu_fam_msgs[4] = {
 +        "Whoa!  Everything %s different.",
 +        "You are surrounded by twisty little passages, all alike.",
 +        "Gee, this %s like uncle Conan's place...",
 +        0     /* no message */
 +        };
 +        const char *mesg;
 +        char buf[BUFSZ];
 +        int which = rn2(4);
 +
 +        if (Hallucination)
 +        mesg = halu_fam_msgs[which];
 +        else
 +        mesg = fam_msgs[which];
 +        if (mesg && index(mesg, '%')) {
 +        Sprintf(buf, mesg, !Blind ? "looks" : "seems");
 +        mesg = buf;
 +        }
 +        if (mesg) pline1(mesg);
 +    }
  
 -      /* special location arrival messages/events */
 -      if (In_endgame(&u.uz)) {
 -          if (new && on_level(&u.uz, &astral_level))
 -              final_level();  /* guardian angel,&c */
 -          else if (newdungeon && u.uhave.amulet)
 -              resurrect();    /* force confrontation with Wizard */
 -      } else if (In_quest(&u.uz)) {
 -          onquest();          /* might be reaching locate|goal level */
 -      } else if (In_V_tower(&u.uz)) {
 -          if (newdungeon && In_hell(&u.uz0))
 -              pline_The("heat and smoke are gone.");
 -      } else if (Is_knox(&u.uz)) {
 -          /* alarm stops working once Croesus has died */
 -          if (new || !mvitals[PM_CROESUS].died) {
 -              You("have penetrated a high security area!");
 -              pline("An alarm sounds!");
 -              for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
 -                  if (DEADMONSTER(mtmp)) continue;
 -                  mtmp->msleeping = 0;
 -              }
 -          }
 -      } else {
 -          if (new && Is_rogue_level(&u.uz))
 -              You("enter what seems to be an older, more primitive world.");
 -          /* main dungeon message from your quest leader */
 -          if (!In_quest(&u.uz0) && at_dgn_entrance("The Quest") &&
 -                  !(u.uevent.qcompleted || u.uevent.qexpelled ||
 -                    quest_status.leader_is_dead)) {
 -              if (!u.uevent.qcalled) {
 -                  u.uevent.qcalled = 1;
 -                  com_pager(2);   /* main "leader needs help" message */
 -              } else {            /* reminder message */
 -                  com_pager(Role_if(PM_ROGUE) ? 4 : 3);
 -              }
 -          }
 -      }
 +    /* special location arrival messages/events */
 +    if (In_endgame(&u.uz)) {
 +        if (new && on_level(&u.uz, &astral_level))
 +        final_level();        /* guardian angel,&c */
 +        else if (newdungeon && u.uhave.amulet)
 +        resurrect();  /* force confrontation with Wizard */
 +    } else if (In_quest(&u.uz)) {
 +        onquest();            /* might be reaching locate|goal level */
 +    } else if (In_V_tower(&u.uz)) {
 +        if (newdungeon && In_hell(&u.uz0))
 +        pline_The("heat and smoke are gone.");
 +    } else if (Is_knox(&u.uz)) {
 +        /* alarm stops working once Croesus has died */
 +        if (new || !mvitals[PM_CROESUS].died) {
 +        You("have penetrated a high security area!");
 +        pline("An alarm sounds!");
 +        for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
 +            if (DEADMONSTER(mtmp)) continue;
 +            mtmp->msleeping = 0;
 +        }
 +        }
 +    } else {
 +        if (new && Is_rogue_level(&u.uz))
 +        You("enter what seems to be an older, more primitive world.");
 +        /* main dungeon message from your quest leader */
 +        if (!In_quest(&u.uz0) && at_dgn_entrance("The Quest") &&
 +            !(u.uevent.qcompleted || u.uevent.qexpelled ||
 +              quest_status.leader_is_dead)) {
 +        if (!u.uevent.qcalled) {
 +            u.uevent.qcalled = 1;
 +            com_pager(2);   /* main "leader needs help" message */
 +        } else {          /* reminder message */
 +            com_pager(Role_if(PM_ROGUE) ? 4 : 3);
 +        }
 +        }
 +    }
  
 -      assign_level(&u.uz0, &u.uz); /* reset u.uz0 */
 +    assign_level(&u.uz0, &u.uz); /* reset u.uz0 */
  #ifdef INSURANCE
 -      save_currentstate();
 +    save_currentstate();
  #endif
  
 -      /* assume this will always return TRUE when changing level */
 -      (void) in_out_region(u.ux, u.uy);
 -      (void) pickup(1);
 +    /* assume this will always return TRUE when changing level */
 +    (void) in_out_region(u.ux, u.uy);
 +    (void) pickup(1);
  }
  
  STATIC_OVL void
diff --cc src/files.c
index 4ddeda27d74eb0d1a47a5b9155caa569b07d5410,ace417e7dd67f6a0f33f3123e9745ef4cafa78f0..86688beb2203b764967ab36837e8bbe871cfaf47
@@@ -1872,38 -1883,19 +1883,45 @@@ int src
        }
  
  #if defined(MICRO) || defined(MAC) || defined(__BEOS__) || defined(WIN32)
 -      lastconfigfile[BUFSZ-1] = '\0';
 +# if defined(WIN32) || defined(MSDOS)
 +    /* user's home directory should be where we look first here, too */
 +    envp = nh_getenv("USERPROFILE");
 +# endif
++/*
++ * TODO: uncomment this when you figure out where to put it
+       (void) strncpy(lastconfigfile,
+                       fqname(configfile, CONFIGPREFIX, 0), BUFSZ-1);
++      lastconfigfile[BUFSZ-1] = '\0'; 
+       if ((fp = fopenp(lastconfigfile, "r")) != (FILE *)0) {
++*/
 +# ifdef WIN32
 +    if (!envp) {
 +        Strcpy(tmp_config, configfile);
 +    } else {
 +        Sprintf(tmp_config, "%s\\%s", envp, configfile);
 +    }
 +    /* try the home directory first, then the output of fqname() will
 +     * pick up the current dir */
 +    if ((fp = fopenp(tmp_config, "r")) != (FILE *)0)
 +        return(fp);
 +    if ((fp = fopenp(fqname(configfile, CONFIGPREFIX, 0), "r")) != (FILE *)0)
                return(fp);
 -      }
 +# else
 +    if ((fp = fopenp(fqname(configfile, CONFIGPREFIX, 0), "r")) != (FILE *)0)
 +        return(fp);
 +# endif
  # ifdef MSDOS
 -      (void) strncpy(lastconfigfile,
 -                      fqname(backward_compat_configfile, CONFIGPREFIX, 0),
 -                      BUFSZ-1);
 -      lastconfigfile[BUFSZ-1] = '\0';
 -      else if ((fp = fopenp(lastconfigfile, "r")) != (FILE *)0)
 +    else {
 +        if (!envp) {
 +            Strcpy(tmp_config, backward_compat_configfile);
 +        } else {
 +            Sprintf(tmp_config, "%s\\%s", envp, backward_compat_configfile);
 +        }
 +        if ((fp = fopenp(tmp_config, "r")) != (FILE *)0)
                return(fp);
 +        if ((fp = fopenp(fqname(configfile, CONFIGPREFIX, 0), "r")) != (FILE *)0)
 +            return(fp);
 +    }
  # endif
  #else
        /* constructed full path names don't need fqname() */
diff --cc src/hack.c
index 12e7c4e2d0b59d61d312b3693fa9f6286fb509d6,7f15af6eeaff611a7ed095e504cea7b4722592cd..1eb9eac97016105c6ad9c306716bb33f361ca5f9
@@@ -626,90 -626,95 +626,95 @@@ int mode
       *  Check for physical obstacles.  First, the place we are going.
       */
      if (IS_ROCK(tmpr->typ) || tmpr->typ == IRONBARS) {
 -      if (Blind && mode == DO_MOVE) feel_location(x,y);
 -      if (Passes_walls && may_passwall(x,y)) {
 -          ;   /* do nothing */
 -      } else if (tmpr->typ == IRONBARS) {
 +    if (Blind && mode == DO_MOVE) feel_location(x,y);
 +    if (Passes_walls && may_passwall(x,y)) {
 +        ;     /* do nothing */
 +    } else if (tmpr->typ == IRONBARS) {
-         if (!(Passes_walls || passes_bars(youmonst.data)))
+           if (!(Passes_walls || passes_bars(youmonst.data))) {
+               if (iflags.mention_walls)
+                   You("cannot pass through the bars.");
 -              return FALSE;
 +        return FALSE;
+           }
 -      } else if (tunnels(youmonst.data) && !needspick(youmonst.data)) {
 -          /* Eat the rock. */
 -          if (mode == DO_MOVE && still_chewing(x,y)) return FALSE;
 -      } else if (flags.autodig && !context.run && !context.nopick &&
 -                 uwep && is_pick(uwep)) {
 -      /* MRKR: Automatic digging when wielding the appropriate tool */
 -          if (mode == DO_MOVE)
 -              (void) use_pick_axe2(uwep);
 -          return FALSE;
 -      } else {
 -          if (mode == DO_MOVE) {
 -              if (Is_stronghold(&u.uz) && is_db_wall(x,y))
 -                  pline_The("drawbridge is up!");
 -              /* sokoban restriction stays even after puzzle is solved */
 +    } else if (tunnels(youmonst.data) && !needspick(youmonst.data)) {
 +        /* Eat the rock. */
 +        if (mode == DO_MOVE && still_chewing(x,y)) return FALSE;
 +    } else if (flags.autodig && !context.run && !context.nopick &&
 +           uwep && is_pick(uwep)) {
 +    /* MRKR: Automatic digging when wielding the appropriate tool */
 +        if (mode == DO_MOVE)
 +        (void) use_pick_axe2(uwep);
 +        return FALSE;
 +    } else {
 +        if (mode == DO_MOVE) {
 +        if (Is_stronghold(&u.uz) && is_db_wall(x,y))
 +            pline_The("drawbridge is up!");
 +        /* sokoban restriction stays even after puzzle is solved */
-         if (Passes_walls && !may_passwall(x,y) && In_sokoban(&u.uz))
+               else if (Passes_walls && !may_passwall(x,y) && In_sokoban(&u.uz))
 -                  pline_The("Sokoban walls resist your ability.");
 +            pline_The("Sokoban walls resist your ability.");
+               else if (iflags.mention_walls)
+                   pline("It's a wall.");
 -          }
 -          return FALSE;
 -      }
 +        }
 +        return FALSE;
 +    }
      } else if (IS_DOOR(tmpr->typ)) {
 -      if (closed_door(x,y)) {
 -          if (Blind && mode == DO_MOVE) feel_location(x,y);
 -          if (Passes_walls)
 -              ;       /* do nothing */
 -          else if (can_ooze(&youmonst)) {
 -              if (mode == DO_MOVE) You("ooze under the door.");
 -          } else if (tunnels(youmonst.data) && !needspick(youmonst.data)) {
 -              /* Eat the door. */
 -              if (mode == DO_MOVE && still_chewing(x,y)) return FALSE;
 -          } else {
 -              if (mode == DO_MOVE) {
 -                  if (amorphous(youmonst.data))
 -                      You("try to ooze under the door, but can't squeeze your possessions through.");
 -                  else if (x == ux || y == uy) {
 -                      if (Blind || Stunned || ACURR(A_DEX) < 10 || Fumbling) {
 -                          if (u.usteed) {
 -                              You_cant("lead %s through that closed door.",
 -                                       y_monnam(u.usteed));
 -                          } else {
 -                              pline("Ouch!  You bump into a door.");
 -                              exercise(A_DEX, FALSE);
 -                          }
 -                      } else pline("That door is closed.");
 -                  }
 -              } else if (mode == TEST_TRAV) goto testdiag;
 -              return FALSE;
 -          }
 -      } else {
 -      testdiag:
 -          if (dx && dy && !Passes_walls
 -              && (!doorless_door(x, y) || block_door(x, y))) {
 -              /* Diagonal moves into a door are not allowed. */
 -              if (Blind && mode == DO_MOVE)
 -                  feel_location(x,y);
 -              return FALSE;
 -          }
 -      }
 +    if (closed_door(x,y)) {
 +        if (Blind && mode == DO_MOVE) feel_location(x,y);
 +        if (Passes_walls)
 +        ;     /* do nothing */
 +        else if (can_ooze(&youmonst)) {
 +        if (mode == DO_MOVE) You("ooze under the door.");
 +        } else if (tunnels(youmonst.data) && !needspick(youmonst.data)) {
 +        /* Eat the door. */
 +        if (mode == DO_MOVE && still_chewing(x,y)) return FALSE;
 +        } else {
 +        if (mode == DO_MOVE) {
 +            if (amorphous(youmonst.data))
 +            You("try to ooze under the door, but can't squeeze your possessions through.");
 +            else if (x == ux || y == uy) {
 +            if (Blind || Stunned || ACURR(A_DEX) < 10 || Fumbling) {
 +                if (u.usteed) {
 +                You_cant("lead %s through that closed door.",
 +                         y_monnam(u.usteed));
 +                } else {
 +                    pline("Ouch!  You bump into a door.");
 +                    exercise(A_DEX, FALSE);
 +                }
 +            } else pline("That door is closed.");
 +            }
 +        } else if (mode == TEST_TRAV) goto testdiag;
 +        return FALSE;
 +        }
 +    } else {
 +    testdiag:
 +        if (dx && dy && !Passes_walls
 +        && (!doorless_door(x, y) || block_door(x, y))) {
 +        /* Diagonal moves into a door are not allowed. */
 +        if (Blind && mode == DO_MOVE)
 +            feel_location(x,y);
 +        return FALSE;
 +        }
 +    }
      }
      if (dx && dy
 -          && bad_rock(youmonst.data,ux,y) && bad_rock(youmonst.data,x,uy)) {
 -      /* Move at a diagonal. */
 -      switch (cant_squeeze_thru(&youmonst)) {
 -      case 3:
 -          if (mode == DO_MOVE) You("cannot pass that way.");
 -          return FALSE;
 -      case 2:
 -          if (mode == DO_MOVE) You("are carrying too much to get through.");
 -          return FALSE;
 -      case 1:
 -          if (mode == DO_MOVE) Your("body is too large to fit through.");
 -          return FALSE;
 -      default:
 -          break;      /* can squeeze through */
 -      }
 +        && bad_rock(youmonst.data,ux,y) && bad_rock(youmonst.data,x,uy)) {
 +    /* Move at a diagonal. */
 +    switch (cant_squeeze_thru(&youmonst)) {
 +    case 3:
 +        if (mode == DO_MOVE) You("cannot pass that way.");
 +        return FALSE;
 +    case 2:
 +        if (mode == DO_MOVE) You("are carrying too much to get through.");
 +        return FALSE;
 +    case 1:
 +        if (mode == DO_MOVE) Your("body is too large to fit through.");
 +        return FALSE;
 +    default:
 +        break;      /* can squeeze through */
 +    }
      } else if (dx && dy && worm_cross(ux, uy, x, y)) {
 -      /* consecutive long worm segments are at <ux,y> and <x,uy> */
 -      if (mode == DO_MOVE) pline("%s is in your way.", Monnam(m_at(ux, y)));
 -      return FALSE;
 +    /* consecutive long worm segments are at <ux,y> and <x,uy> */
 +    if (mode == DO_MOVE) pline("%s is in your way.", Monnam(m_at(ux, y)));
 +    return FALSE;
      }
      /* Pick travel path that does not require crossing a trap.
       * Avoid water and lava using the usual running rules.
diff --cc src/invent.c
index 37b1a15499de05ca44b002ee068000fac2d28d74,e5100ee5e53ca7235ac95028f02de3e11a5b35ea..1a6e7673e2ff832dd8a37f5bef3e8c9dbaa80c51
@@@ -235,29 -235,42 +235,42 @@@ voi
  addinv_core1(obj)
  struct obj *obj;
  {
 -      if (obj->oclass == COIN_CLASS) {
 -              context.botl = 1;
 -      } else if (obj->otyp == AMULET_OF_YENDOR) {
 -              if (u.uhave.amulet) impossible("already have amulet?");
 -              u.uhave.amulet = 1;
 +    if (obj->oclass == COIN_CLASS) {
 +        context.botl = 1;
 +    } else if (obj->otyp == AMULET_OF_YENDOR) {
 +        if (u.uhave.amulet) impossible("already have amulet?");
 +        u.uhave.amulet = 1;
+               u.uachieve.amulet = 1;
 -      } else if (obj->otyp == CANDELABRUM_OF_INVOCATION) {
 -              if (u.uhave.menorah) impossible("already have candelabrum?");
 -              u.uhave.menorah = 1;
 +    } else if (obj->otyp == CANDELABRUM_OF_INVOCATION) {
 +        if (u.uhave.menorah) impossible("already have candelabrum?");
 +        u.uhave.menorah = 1;
+               u.uachieve.menorah = 1;
 -      } else if (obj->otyp == BELL_OF_OPENING) {
 -              if (u.uhave.bell) impossible("already have silver bell?");
 -              u.uhave.bell = 1;
 +    } else if (obj->otyp == BELL_OF_OPENING) {
 +        if (u.uhave.bell) impossible("already have silver bell?");
 +        u.uhave.bell = 1;
+               u.uachieve.bell = 1;
 -      } else if (obj->otyp == SPE_BOOK_OF_THE_DEAD) {
 -              if (u.uhave.book) impossible("already have the book?");
 -              u.uhave.book = 1;
 +    } else if (obj->otyp == SPE_BOOK_OF_THE_DEAD) {
 +        if (u.uhave.book) impossible("already have the book?");
 +        u.uhave.book = 1;
+               u.uachieve.book = 1;
 -      } else if (obj->oartifact) {
 -              if (is_quest_artifact(obj)) {
 -                  if (u.uhave.questart)
 -                      impossible("already have quest artifact?");
 -                  u.uhave.questart = 1;
 -                  artitouch(obj);
 -              }
 -              set_artifact_intrinsic(obj, 1, W_ART);
 -      }
 +    } else if (obj->oartifact) {
 +        if (is_quest_artifact(obj)) {
 +            if (u.uhave.questart)
 +            impossible("already have quest artifact?");
 +            u.uhave.questart = 1;
 +            artitouch(obj);
 +        }
 +        set_artifact_intrinsic(obj, 1, W_ART);
 +    }
+         if(obj->otyp == LUCKSTONE && obj->record_achieve_special) {
+           u.uachieve.mines_luckstone = 1;
+           obj->record_achieve_special = 0;
+         } else if((obj->otyp == AMULET_OF_REFLECTION ||
+                    obj->otyp == BAG_OF_HOLDING) &&
+                   obj->record_achieve_special) {
+           u.uachieve.finish_sokoban = 1;
+           obj->record_achieve_special = 0;
+         }
  }
  
  /*
@@@ -752,313 -753,327 +765,327 @@@ struct obj 
  getobj(let,word)
  register const char *let,*word;
  {
 -      register struct obj *otmp;
 -      register char ilet;
 -      char buf[BUFSZ], qbuf[QBUFSZ];
 -      char lets[BUFSZ], altlets[BUFSZ], *ap;
 -      register int foo = 0;
 -      register char *bp = buf;
 -      xchar allowcnt = 0;     /* 0, 1 or 2 */
 -      struct obj *firstobj = invent;
 -      boolean usegold = FALSE;        /* can't use gold because its illegal */
 -      boolean allowall = FALSE;
 -      boolean allownone = FALSE;
 -      boolean useboulder = FALSE;
 -      xchar foox = 0;
 -      long cnt, prevcnt;
 -      boolean prezero;
 -      long dummymask;
 -
 -      if(*let == ALLOW_COUNT) let++, allowcnt = 1;
 -      if(*let == COIN_CLASS) let++, usegold = TRUE;
 -
 -      /* Equivalent of an "ugly check" for gold */
 -      if (usegold && !strcmp(word, "eat") &&
 -          (!metallivorous(youmonst.data)
 -           || youmonst.data == &mons[PM_RUST_MONSTER]))
 -              usegold = FALSE;
 -
 -      if(*let == ALL_CLASSES) let++, allowall = TRUE;
 -      if(*let == ALLOW_NONE) let++, allownone = TRUE;
 -      /* "ugly check" for reading fortune cookies, part 1 */
 -      /* The normal 'ugly check' keeps the object on the inventory list.
 -       * We don't want to do that for shirts/cookies, so the check for
 -       * them is handled a bit differently (and also requires that we set
 -       * allowall in the caller)
 -       */
 -      if(allowall && !strcmp(word, "read")) allowall = FALSE;
 -
 -      /* another ugly check: show boulders (not statues) */
 -      if(*let == WEAPON_CLASS &&
 -         !strcmp(word, "throw") && throws_rocks(youmonst.data))
 -          useboulder = TRUE;
 -
 -      if(allownone) *bp++ = '-';
 -      if(bp > buf && bp[-1] == '-') *bp++ = ' ';
 -      ap = altlets;
 -
 -      if (!flags.invlet_constant) reassign();
 -
 -      for (otmp = firstobj; otmp; otmp = otmp->nobj) {
 -          if (&bp[foo] == &buf[sizeof buf - 1] ||
 -                  ap == &altlets[sizeof altlets - 1]) {
 -              /* we must have a huge number of NOINVSYM items somehow */
 -              impossible("getobj: inventory overflow");
 -              break;
 -          }
 +    register struct obj *otmp;
 +    register char ilet;
 +    char buf[BUFSZ], qbuf[QBUFSZ];
 +    char lets[BUFSZ], altlets[BUFSZ], *ap;
 +    register int foo = 0;
 +    register char *bp = buf;
 +    xchar allowcnt = 0;       /* 0, 1 or 2 */
 +    struct obj *firstobj = invent;
 +    boolean usegold = FALSE;  /* can't use gold because its illegal */
 +    boolean allowall = FALSE;
 +    boolean allownone = FALSE;
 +    boolean useboulder = FALSE;
 +    xchar foox = 0;
 +    long cnt, prevcnt;
 +    boolean prezero;
 +    long dummymask;
 +
 +    if(*let == ALLOW_COUNT) let++, allowcnt = 1;
 +    if(*let == COIN_CLASS) let++, usegold = TRUE;
 +
 +    /* Equivalent of an "ugly check" for gold */
 +    if (usegold && !strcmp(word, "eat") &&
 +        (!metallivorous(youmonst.data)
 +         || youmonst.data == &mons[PM_RUST_MONSTER]))
 +        usegold = FALSE;
 +
 +    if(*let == ALL_CLASSES) let++, allowall = TRUE;
 +    if(*let == ALLOW_NONE) let++, allownone = TRUE;
 +    /* "ugly check" for reading fortune cookies, part 1 */
 +    /* The normal 'ugly check' keeps the object on the inventory list.
 +     * We don't want to do that for shirts/cookies, so the check for
 +     * them is handled a bit differently (and also requires that we set
 +     * allowall in the caller)
 +     */
 +    if(allowall && !strcmp(word, "read")) allowall = FALSE;
 +
 +    /* another ugly check: show boulders (not statues) */
 +    if(*let == WEAPON_CLASS &&
 +       !strcmp(word, "throw") && throws_rocks(youmonst.data))
 +        useboulder = TRUE;
 +
 +    if(allownone) *bp++ = '-';
 +    if(bp > buf && bp[-1] == '-') *bp++ = ' ';
 +    ap = altlets;
 +
 +    if (!flags.invlet_constant) reassign();
  
 -          if (!*let || index(let, otmp->oclass)
 -              || (usegold && otmp->invlet == GOLD_SYM)
 -              || (useboulder && otmp->otyp == BOULDER)
 -              ) {
 -              register int otyp = otmp->otyp;
 -              bp[foo++] = otmp->invlet;
 -
 -              /* ugly check: remove inappropriate things */
 -              if ((taking_off(word) &&
 -                  !(otmp->owornmask & (W_ARMOR | W_RING | W_AMUL | W_TOOL)))
 -              || (putting_on(word) &&
 -                   (otmp->owornmask & (W_ARMOR | W_RING | W_AMUL | W_TOOL)))
 -                                                      /* already worn */
 +    for (otmp = firstobj; otmp; otmp = otmp->nobj) {
 +        if (&bp[foo] == &buf[sizeof buf - 1] ||
 +            ap == &altlets[sizeof altlets - 1]) {
 +        /* we must have a huge number of NOINVSYM items somehow */
 +        impossible("getobj: inventory overflow");
 +        break;
 +        }
 +
 +        if (!*let || index(let, otmp->oclass)
 +        || (usegold && otmp->invlet == GOLD_SYM)
 +        || (useboulder && otmp->otyp == BOULDER)
 +        ) {
 +        register int otyp = otmp->otyp;
 +        bp[foo++] = otmp->invlet;
 +
 +        /* ugly check: remove inappropriate things */
 +        if ((taking_off(word) &&
 +            !(otmp->owornmask & (W_ARMOR | W_RING | W_AMUL | W_TOOL)))
 +        || (putting_on(word) &&
 +             (otmp->owornmask & (W_ARMOR | W_RING | W_AMUL | W_TOOL)))
 +                            /* already worn */
  #if 0 /* 3.4.1 -- include currently wielded weapon among the choices */
 -              || (!strcmp(word, "wield") &&
 -                  (otmp->owornmask & W_WEP))
 +        || (!strcmp(word, "wield") &&
 +            (otmp->owornmask & W_WEP))
  #endif
 -              || (!strcmp(word, "ready") &&
 -                  (otmp == uwep || (otmp == uswapwep && u.twoweap)))
 -              || ((!strcmp(word, "dip") || !strcmp(word, "grease")) &&
 -                  inaccessible_equipment(otmp, (const char *)0 , FALSE))
 -                  ) {
 -                      foo--;
 -                      foox++;
 -              }
 -
 -              /* Second ugly check; unlike the first it won't trigger an
 -               * "else" in "you don't have anything else to ___".
 -               */
 -              else if ((putting_on(word) &&
 -                  ((otmp->oclass == FOOD_CLASS && otmp->otyp != MEAT_RING) ||
 -                  (otmp->oclass == TOOL_CLASS &&
 -                   otyp != BLINDFOLD && otyp != TOWEL && otyp != LENSES)))
 -              || (!strcmp(word, "wield") &&
 -                  (otmp->oclass == TOOL_CLASS && !is_weptool(otmp)))
 -              || (!strcmp(word, "eat") && !is_edible(otmp))
 -              || (!strcmp(word, "sacrifice") &&
 -                  (otyp != CORPSE &&
 -                   otyp != AMULET_OF_YENDOR && otyp != FAKE_AMULET_OF_YENDOR))
 -              || (!strcmp(word, "write with") &&
 -                  (otmp->oclass == TOOL_CLASS &&
 -                   otyp != MAGIC_MARKER && otyp != TOWEL))
 -              || (!strcmp(word, "tin") &&
 -                  (otyp != CORPSE || !tinnable(otmp)))
 -              || (!strcmp(word, "rub") &&
 -                  ((otmp->oclass == TOOL_CLASS &&
 -                    otyp != OIL_LAMP && otyp != MAGIC_LAMP &&
 -                    otyp != BRASS_LANTERN) ||
 -                   (otmp->oclass == GEM_CLASS && !is_graystone(otmp))))
 -              || ((!strcmp(word, "use or apply") ||
 -                      !strcmp(word, "untrap with")) &&
 -                   /* Picks, axes, pole-weapons, bullwhips */
 -                  ((otmp->oclass == WEAPON_CLASS && !is_pick(otmp) &&
 -                    !is_axe(otmp) && !is_pole(otmp) && otyp != BULLWHIP) ||
 -                   (otmp->oclass == POTION_CLASS &&
 -                   /* only applicable potion is oil, and it will only
 -                      be offered as a choice when already discovered */
 -                   (otyp != POT_OIL || !otmp->dknown ||
 -                    !objects[POT_OIL].oc_name_known)) ||
 -                   (otmp->oclass == FOOD_CLASS &&
 -                    otyp != CREAM_PIE && otyp != EUCALYPTUS_LEAF) ||
 -                   (otmp->oclass == GEM_CLASS && !is_graystone(otmp))))
 -              || (!strcmp(word, "invoke") &&
 -                  (!otmp->oartifact && !objects[otyp].oc_unique &&
 -                   (otyp != FAKE_AMULET_OF_YENDOR || otmp->known) &&
 -                   otyp != CRYSTAL_BALL &&    /* #invoke synonym for apply */
 -                 /* note: presenting the possibility of invoking non-artifact
 -                    mirrors and/or lamps is a simply a cruel deception... */
 -                   otyp != MIRROR && otyp != MAGIC_LAMP &&
 -                   (otyp != OIL_LAMP ||       /* don't list known oil lamp */
 -                    (otmp->dknown && objects[OIL_LAMP].oc_name_known))))
 -              || (!strcmp(word, "untrap with") &&
 -                  (otmp->oclass == TOOL_CLASS && otyp != CAN_OF_GREASE))
 -              || (!strcmp(word, "tip") && !Is_container(otmp) &&
 -                 /* include horn of plenty if sufficiently discovered */
 -                  (otmp->otyp != HORN_OF_PLENTY || !otmp->dknown ||
 -                      !objects[HORN_OF_PLENTY].oc_name_known))
 -              || (!strcmp(word, "charge") && !is_chargeable(otmp))
 -              || (!strcmp(word, "call") && !objtyp_is_callable(otyp))
 -                  )
 -                      foo--;
 -              /* ugly check for unworn armor that can't be worn */
 -              else if ((putting_on(word) && *let == ARMOR_CLASS &&
 -                        !canwearobj(otmp, &dummymask, FALSE))
 -              /* or unsuitable items rubbed on known touchstone */
 -              || (!strncmp(word, "rub on the stone", 16) &&
 -                  *let == GEM_CLASS &&
 -                  otmp->dknown && objects[otyp].oc_name_known)
 -              /* suppress corpses on astral, amulets elsewhere */
 -              || (!strcmp(word, "sacrifice") &&
 -                  /* (!astral && amulet) || (astral && !amulet) */
 -                  (!Is_astralevel(&u.uz) ^ (otmp->oclass != AMULET_CLASS)))
 -              /* suppress container being stashed into */
 -              || (!strcmp(word, "stash") && !ck_bag(otmp))
 -              /* worn armor or accessory covered by cursed worn armor */
 -              || (taking_off(word) &&
 -                  inaccessible_equipment(otmp, (const char *)0, TRUE))
 -                  ) {
 -                      /* acceptable but not listed as likely candidate */
 -                      foo--;
 -                      allowall = TRUE;
 -                      *ap++ = otmp->invlet;
 -              }
 -          } else {
 -
 -              /* "ugly check" for reading fortune cookies, part 2 */
 -              if ((!strcmp(word, "read")
 +        || (!strcmp(word, "ready") &&
 +            (otmp == uwep || (otmp == uswapwep && u.twoweap)))
 +        || ((!strcmp(word, "dip") || !strcmp(word, "grease")) &&
 +            inaccessible_equipment(otmp, (const char *)0 , FALSE))
 +            ) {
 +            foo--;
 +            foox++;
 +        }
 +
 +        /* Second ugly check; unlike the first it won't trigger an
 +         * "else" in "you don't have anything else to ___".
 +         */
 +        else if ((putting_on(word) &&
 +            ((otmp->oclass == FOOD_CLASS && otmp->otyp != MEAT_RING) ||
 +            (otmp->oclass == TOOL_CLASS &&
 +             otyp != BLINDFOLD && otyp != TOWEL && otyp != LENSES)))
 +        || (!strcmp(word, "wield") &&
 +            (otmp->oclass == TOOL_CLASS && !is_weptool(otmp)))
 +        || (!strcmp(word, "eat") && !is_edible(otmp))
 +        || (!strcmp(word, "sacrifice") &&
 +            (otyp != CORPSE &&
 +             otyp != AMULET_OF_YENDOR && otyp != FAKE_AMULET_OF_YENDOR))
 +        || (!strcmp(word, "write with") &&
 +            (otmp->oclass == TOOL_CLASS &&
 +             otyp != MAGIC_MARKER && otyp != TOWEL))
 +        || (!strcmp(word, "tin") &&
 +            (otyp != CORPSE || !tinnable(otmp)))
 +        || (!strcmp(word, "rub") &&
 +            ((otmp->oclass == TOOL_CLASS &&
 +              otyp != OIL_LAMP && otyp != MAGIC_LAMP &&
 +              otyp != BRASS_LANTERN) ||
 +             (otmp->oclass == GEM_CLASS && !is_graystone(otmp))))
 +        || ((!strcmp(word, "use or apply") ||
 +            !strcmp(word, "untrap with")) &&
 +             /* Picks, axes, pole-weapons, bullwhips */
 +            ((otmp->oclass == WEAPON_CLASS && !is_pick(otmp) &&
 +              !is_axe(otmp) && !is_pole(otmp) && otyp != BULLWHIP) ||
 +             (otmp->oclass == POTION_CLASS &&
 +             /* only applicable potion is oil, and it will only
 +            be offered as a choice when already discovered */
 +             (otyp != POT_OIL || !otmp->dknown ||
 +              !objects[POT_OIL].oc_name_known)) ||
 +             (otmp->oclass == FOOD_CLASS &&
 +              otyp != CREAM_PIE && otyp != EUCALYPTUS_LEAF) ||
 +             (otmp->oclass == GEM_CLASS && !is_graystone(otmp))))
 +        || (!strcmp(word, "invoke") &&
 +            (!otmp->oartifact && !objects[otyp].oc_unique &&
 +             (otyp != FAKE_AMULET_OF_YENDOR || otmp->known) &&
 +             otyp != CRYSTAL_BALL &&  /* #invoke synonym for apply */
 +           /* note: presenting the possibility of invoking non-artifact
 +              mirrors and/or lamps is a simply a cruel deception... */
 +             otyp != MIRROR && otyp != MAGIC_LAMP &&
 +             (otyp != OIL_LAMP ||     /* don't list known oil lamp */
 +              (otmp->dknown && objects[OIL_LAMP].oc_name_known))))
 +        || (!strcmp(word, "untrap with") &&
 +            (otmp->oclass == TOOL_CLASS && otyp != CAN_OF_GREASE))
 +        || (!strcmp(word, "tip") && !Is_container(otmp) &&
 +           /* include horn of plenty if sufficiently discovered */
 +            (otmp->otyp != HORN_OF_PLENTY || !otmp->dknown ||
 +            !objects[HORN_OF_PLENTY].oc_name_known))
 +        || (!strcmp(word, "charge") && !is_chargeable(otmp))
 +        || (!strcmp(word, "call") && !objtyp_is_callable(otyp))
 +            )
 +            foo--;
 +        /* ugly check for unworn armor that can't be worn */
 +        else if ((putting_on(word) && *let == ARMOR_CLASS &&
 +              !canwearobj(otmp, &dummymask, FALSE))
 +        /* or unsuitable items rubbed on known touchstone */
 +        || (!strncmp(word, "rub on the stone", 16) &&
 +            *let == GEM_CLASS &&
 +            otmp->dknown && objects[otyp].oc_name_known)
 +        /* suppress corpses on astral, amulets elsewhere */
 +        || (!strcmp(word, "sacrifice") &&
 +            /* (!astral && amulet) || (astral && !amulet) */
 +            (!Is_astralevel(&u.uz) ^ (otmp->oclass != AMULET_CLASS)))
 +        /* suppress container being stashed into */
 +        || (!strcmp(word, "stash") && !ck_bag(otmp))
 +        /* worn armor or accessory covered by cursed worn armor */
 +        || (taking_off(word) &&
 +            inaccessible_equipment(otmp, (const char *)0, TRUE))
 +            ) {
 +            /* acceptable but not listed as likely candidate */
 +            foo--;
 +            allowall = TRUE;
 +            *ap++ = otmp->invlet;
 +        }
 +        } else {
 +
 +        /* "ugly check" for reading fortune cookies, part 2 */
 +        if ((!strcmp(word, "read")
                && (otmp->otyp == FORTUNE_COOKIE || otmp->otyp == T_SHIRT)))
 -                      allowall = TRUE;
 -          }
 -      }
 -      bp[foo] = 0;
 -      if(foo == 0 && bp > buf && bp[-1] == ' ') *--bp = 0;
 -      Strcpy(lets, bp);       /* necessary since we destroy buf */
 -      if(foo > 5)                     /* compactify string */
 -              compactify(bp);
 -      *ap = '\0';
 -
 -      if(!foo && !allowall && !allownone) {
 -              You("don't have anything %sto %s.",
 -                      foox ? "else " : "", word);
 -              return((struct obj *)0);
 -      }
 -      for(;;) {
 -              cnt = 0;
 -              if (allowcnt == 2) allowcnt = 1;  /* abort previous count */
 -              prezero = FALSE;
 -              if(!buf[0]) {
 -                      Sprintf(qbuf, "What do you want to %s? [*]", word);
 -              } else {
 -                      Sprintf(qbuf, "What do you want to %s? [%s or ?*]",
 -                              word, buf);
 -              }
 -              if (in_doagain)
 -                  ilet = readchar();
 -              else
 -                  ilet = yn_function(qbuf, (char *)0, '\0');
 -              if (digit(ilet) && !allowcnt) {
 -                  pline("No count allowed with this command.");
 -                  continue;
 -              }
 -              if (ilet == '0') prezero = TRUE;
 -              while (digit(ilet)) {
 -                  if (ilet != '?' && ilet != '*') savech(ilet);
 -                  /* accumulate unless cnt has overflowed */
 -                  if (allowcnt < 3) {
 -                      prevcnt = cnt;
 -                      cnt = 10L * cnt + (long)(ilet - '0');
 -                      /* signal presence of cnt */
 -                      allowcnt = (cnt >= prevcnt) ? 2 : 3;
 -                  }
 -                  ilet = readchar();
 -              }
 -              if (allowcnt == 3) {
 -                  /* overflow detected; force cnt to be invalid */
 -                  cnt = -1L;
 -                  allowcnt = 2;
 -              }
 -              if(index(quitchars,ilet)) {
 -                  if(flags.verbose)
 -                      pline1(Never_mind);
 -                  return((struct obj *)0);
 -              }
 -              if(ilet == '-') {
 +            allowall = TRUE;
 +        }
 +    }
 +    bp[foo] = 0;
 +    if(foo == 0 && bp > buf && bp[-1] == ' ') *--bp = 0;
 +    Strcpy(lets, bp); /* necessary since we destroy buf */
 +    if(foo > 5)                       /* compactify string */
 +        compactify(bp);
 +    *ap = '\0';
 +
 +    if(!foo && !allowall && !allownone) {
 +        You("don't have anything %sto %s.",
 +            foox ? "else " : "", word);
 +        return((struct obj *)0);
 +    }
 +    for(;;) {
 +        cnt = 0;
 +        if (allowcnt == 2) allowcnt = 1;  /* abort previous count */
 +        prezero = FALSE;
 +        if(!buf[0]) {
 +            Sprintf(qbuf, "What do you want to %s? [*]", word);
 +        } else {
 +            Sprintf(qbuf, "What do you want to %s? [%s or ?*]",
 +                word, buf);
 +        }
 +        if (in_doagain)
 +            ilet = readchar();
 +        else
 +            ilet = yn_function(qbuf, (char *)0, '\0');
 +        if (digit(ilet) && !allowcnt) {
 +            pline("No count allowed with this command.");
 +            continue;
 +        }
 +        if (ilet == '0') prezero = TRUE;
 +        while (digit(ilet)) {
 +            if (ilet != '?' && ilet != '*') savech(ilet);
 +            /* accumulate unless cnt has overflowed */
 +            if (allowcnt < 3) {
 +            prevcnt = cnt;
 +            cnt = 10L * cnt + (long)(ilet - '0');
 +            /* signal presence of cnt */
 +            allowcnt = (cnt >= prevcnt) ? 2 : 3;
 +            }
 +            ilet = readchar();
 +        }
 +        if (allowcnt == 3) {
 +            /* overflow detected; force cnt to be invalid */
 +            cnt = -1L;
 +            allowcnt = 2;
 +        }
 +        if(index(quitchars,ilet)) {
 +            if(flags.verbose)
 +            pline1(Never_mind);
 +            return((struct obj *)0);
 +        }
 +        if(ilet == '-') {
-             return(allownone ? &zeroobj : (struct obj *) 0);
+                   if (!allownone) {
+                       char *suf = NULL;
+                       strcpy(buf, word);
+                       if ((bp = strstr(buf, " on the ")) != NULL) { /* rub on the stone[s] */
+                           *bp = '\0';
+                           suf = (bp + 1);
+                       }
+                       if ((bp = strstr(buf, " or ")) != NULL) {
+                           *bp = '\0';
+                           bp = (rn2(2) ? buf : (bp + 4));
+                       } else bp = buf;
+                       You("mime %s something%s%s.", ing_suffix(bp),
+                           suf ? " " : "", suf ? suf : "");
+                   }
+                   return(allownone ? &zeroobj : (struct obj *) 0);
 -              }
 -              if(ilet == def_oc_syms[COIN_CLASS].sym) {
 -                      if (!usegold) {
 -                          You("cannot %s gold.", word);
 -                          return(struct obj *)0;
 -                      
 -                      /* Historic note: early Nethack had a bug which was
 -                       * first reported for Larn, where trying to drop 2^32-n
 -                       * gold pieces was allowed, and did interesting things
 -                       * to your money supply.  The LRS is the tax bureau
 -                       * from Larn.
 -                       */
 -                      if (allowcnt == 2 && cnt <= 0) {
 -                          if (cnt < 0 || !prezero)
 -                              pline_The(
 -                "LRS would be very interested to know you have that much.");
 -                          return (struct obj *)0;
 -                      }
 +        }
 +        if(ilet == def_oc_syms[COIN_CLASS].sym) {
 +            if (!usegold) {
 +                You("cannot %s gold.", word);
 +                return(struct obj *)0;
 +            } 
 +            /* Historic note: early Nethack had a bug which was
 +             * first reported for Larn, where trying to drop 2^32-n
 +             * gold pieces was allowed, and did interesting things
 +             * to your money supply.  The LRS is the tax bureau
 +             * from Larn.
 +             */
 +            if (allowcnt == 2 && cnt <= 0) {
 +                if (cnt < 0 || !prezero)
 +                pline_The(
 +          "LRS would be very interested to know you have that much.");
 +                return (struct obj *)0;
 +            }
  
 -              }
 -              if(ilet == '?' || ilet == '*') {
 -                  char *allowed_choices = (ilet == '?') ? lets : (char *)0;
 -                  long ctmp = 0;
 -
 -                  if (ilet == '?' && !*lets && *altlets)
 -                      allowed_choices = altlets;
 -                  ilet = display_pickinv(allowed_choices, TRUE,
 -                                         allowcnt ? &ctmp : (long *)0);
 -                  if(!ilet) continue;
 -                  if (allowcnt && ctmp >= 0) {
 -                      cnt = ctmp;
 -                      if (!cnt) prezero = TRUE;
 -                      allowcnt = 2;
 -                  }
 -                  if(ilet == '\033') {
 -                      if(flags.verbose)
 -                          pline1(Never_mind);
 -                      return((struct obj *)0);
 -                  }
 -                  /* they typed a letter (not a space) at the prompt */
 -              }
 -              if(allowcnt == 2 && !strcmp(word,"throw")) {
 -                  /* permit counts for throwing gold, but don't accept
 -                   * counts for other things since the throw code will
 -                   * split off a single item anyway */
 -                  if (ilet != def_oc_syms[COIN_CLASS].sym)
 -                      allowcnt = 1;
 -                  if(cnt == 0 && prezero) return((struct obj *)0);
 -                  if(cnt > 1) {
 -                      You("can only throw one item at a time.");
 -                      continue;
 -                  }
 -              }
 -              context.botl = 1; /* May have changed the amount of money */
 -              savech(ilet);
 -              for (otmp = invent; otmp; otmp = otmp->nobj)
 -                      if (otmp->invlet == ilet) break;
 -              if(!otmp) {
 -                      You("don't have that object.");
 -                      if (in_doagain) return((struct obj *) 0);
 -                      continue;
 -              } else if (cnt < 0 || otmp->quan < cnt) {
 -                      You("don't have that many!  You have only %ld.",
 -                          otmp->quan);
 -                      if (in_doagain) return((struct obj *) 0);
 -                      continue;
 -              }
 -              break;
 -      }
 -      if(!allowall && let && !index(let,otmp->oclass)
 -         && !(usegold && otmp->oclass == COIN_CLASS)
 -         ) {
 -              silly_thing(word, otmp);
 -              return((struct obj *)0);
 -      }
 -      if(allowcnt == 2) {     /* cnt given */
 -          if(cnt == 0) return (struct obj *)0;
 -          if(cnt != otmp->quan) {
 -              /* don't split a stack of cursed loadstones */
 -              if (splittable(otmp))
 -                  otmp = splitobj(otmp, cnt);
 -              else if (otmp->otyp == LOADSTONE && otmp->cursed)
 -                  /* kludge for canletgo()'s can't-drop-this message */
 -                  otmp->corpsenm = (int) cnt;
 -          }
 -      }
 -      return(otmp);
 +        }
 +        if(ilet == '?' || ilet == '*') {
 +            char *allowed_choices = (ilet == '?') ? lets : (char *)0;
 +            long ctmp = 0;
 +
 +            if (ilet == '?' && !*lets && *altlets)
 +            allowed_choices = altlets;
 +            ilet = display_pickinv(allowed_choices, TRUE,
 +                       allowcnt ? &ctmp : (long *)0);
 +            if(!ilet) continue;
 +            if (allowcnt && ctmp >= 0) {
 +            cnt = ctmp;
 +            if (!cnt) prezero = TRUE;
 +            allowcnt = 2;
 +            }
 +            if(ilet == '\033') {
 +            if(flags.verbose)
 +                pline1(Never_mind);
 +            return((struct obj *)0);
 +            }
 +            /* they typed a letter (not a space) at the prompt */
 +        }
 +        if(allowcnt == 2 && !strcmp(word,"throw")) {
 +            /* permit counts for throwing gold, but don't accept
 +             * counts for other things since the throw code will
 +             * split off a single item anyway */
 +            if (ilet != def_oc_syms[COIN_CLASS].sym)
 +            allowcnt = 1;
 +            if(cnt == 0 && prezero) return((struct obj *)0);
 +            if(cnt > 1) {
 +            You("can only throw one item at a time.");
 +            continue;
 +            }
 +        }
 +        context.botl = 1; /* May have changed the amount of money */
 +        savech(ilet);
 +        for (otmp = invent; otmp; otmp = otmp->nobj)
 +            if (otmp->invlet == ilet) break;
 +        if(!otmp) {
 +            You("don't have that object.");
 +            if (in_doagain) return((struct obj *) 0);
 +            continue;
 +        } else if (cnt < 0 || otmp->quan < cnt) {
 +            You("don't have that many!  You have only %ld.",
 +                otmp->quan);
 +            if (in_doagain) return((struct obj *) 0);
 +            continue;
 +        }
 +        break;
 +    }
 +    if(!allowall && let && !index(let,otmp->oclass)
 +       && !(usegold && otmp->oclass == COIN_CLASS)
 +       ) {
 +        silly_thing(word, otmp);
 +        return((struct obj *)0);
 +    }
 +    if(allowcnt == 2) {       /* cnt given */
 +        if(cnt == 0) return (struct obj *)0;
 +        if(cnt != otmp->quan) {
 +        /* don't split a stack of cursed loadstones */
 +        if (splittable(otmp))
 +            otmp = splitobj(otmp, cnt);
 +        else if (otmp->otyp == LOADSTONE && otmp->cursed)
 +            /* kludge for canletgo()'s can't-drop-this message */
 +            otmp->corpsenm = (int) cnt;
 +        }
 +    }
 +    return(otmp);
  }
  
  void
@@@ -1699,109 -1714,109 +1726,109 @@@ register const char *lets
  boolean want_reply;
  long* out_cnt;
  {
 -      struct obj *otmp;
 -      char ilet, ret;
 -      char *invlet = flags.inv_order;
 -      int n, classcount;
 -      winid win;                              /* windows being used */
 -      static winid local_win = WIN_ERR;       /* window for partial menus */
 -      anything any;
 -      menu_item *selected;
 -
 -      /* overriden by global flag */
 -      if (flags.perm_invent) {
 -          win = (lets && *lets) ? local_win : WIN_INVEN;
 -          /* create the first time used */
 -          if (win == WIN_ERR)
 -              win = local_win = create_nhwindow(NHW_MENU);
 -      } else
 -          win = WIN_INVEN;
 -
 -      /*
 -      Exit early if no inventory -- but keep going if we are doing
 -      a permanent inventory update.  We need to keep going so the
 -      permanent inventory window updates itself to remove the last
 -      item(s) dropped.  One down side:  the addition of the exception
 -      for permanent inventory window updates _can_ pop the window
 -      up when it's not displayed -- even if it's empty -- because we
 -      don't know at this level if its up or not.  This may not be
 -      an issue if empty checks are done before hand and the call
 -      to here is short circuited away.
 -      */
 -      if (!invent && !(flags.perm_invent && !lets && !want_reply)) {
 -          pline("Not carrying anything.");
 -          return 0;
 -      }
 +    struct obj *otmp;
 +    char ilet, ret;
 +    char *invlet = flags.inv_order;
 +    int n, classcount;
 +    winid win;                                /* windows being used */
 +    static winid local_win = WIN_ERR; /* window for partial menus */
 +    anything any;
 +    menu_item *selected;
 +
 +    /* overriden by global flag */
 +    if (flags.perm_invent) {
 +        win = (lets && *lets) ? local_win : WIN_INVEN;
 +        /* create the first time used */
 +        if (win == WIN_ERR)
 +        win = local_win = create_nhwindow(NHW_MENU);
 +    } else
 +        win = WIN_INVEN;
  
 -      /* oxymoron? temporarily assign permanent inventory letters */
 -      if (!flags.invlet_constant) reassign();
 -
 -      if (lets && strlen(lets) == 1 && !iflags.override_ID) {
 -          /* when only one item of interest, use pline instead of menus;
 -             we actually use a fake message-line menu in order to allow
 -             the user to perform selection at the --More-- prompt for tty */
 -          ret = '\0';
 -          for (otmp = invent; otmp; otmp = otmp->nobj) {
 -              if (otmp->invlet == lets[0]) {
 -                  ret = message_menu(lets[0],
 -                        want_reply ? PICK_ONE : PICK_NONE,
 -                        xprname(otmp, (char *)0, lets[0], TRUE, 0L, 0L));
 -                  if (out_cnt) *out_cnt = -1L;        /* select all */
 -                  break;
 -              }
 -          }
 -          return ret;
 -      }
 +    /*
 +    Exit early if no inventory -- but keep going if we are doing
 +    a permanent inventory update.  We need to keep going so the
 +    permanent inventory window updates itself to remove the last
 +    item(s) dropped.  One down side:  the addition of the exception
 +    for permanent inventory window updates _can_ pop the window
 +    up when it's not displayed -- even if it's empty -- because we
 +    don't know at this level if its up or not.  This may not be
 +    an issue if empty checks are done before hand and the call
 +    to here is short circuited away.
 +    */
 +    if (!invent && !(flags.perm_invent && !lets && !want_reply)) {
 +        pline("Not carrying anything.");
 +        return 0;
 +    }
  
 -      start_menu(win);
 -      if (wizard && iflags.override_ID) {
 -              char prompt[BUFSZ];
 -              any.a_char = -1;
 -              /* wiz_identify stuffed the wiz_identify cmd character
 -                 into iflags.override_ID */
 -              Sprintf(prompt, "Debug Identify (%s to permanently identify)",
 -                              visctrl(iflags.override_ID));
 -              add_menu(win, NO_GLYPH, &any,' ', iflags.override_ID, ATR_NONE,
 -                              prompt, MENU_UNSELECTED);
 -      }
 +    /* oxymoron? temporarily assign permanent inventory letters */
 +    if (!flags.invlet_constant) reassign();
 +
 +    if (lets && strlen(lets) == 1 && !iflags.override_ID) {
 +        /* when only one item of interest, use pline instead of menus;
 +           we actually use a fake message-line menu in order to allow
 +           the user to perform selection at the --More-- prompt for tty */
 +        ret = '\0';
 +        for (otmp = invent; otmp; otmp = otmp->nobj) {
 +        if (otmp->invlet == lets[0]) {
 +            ret = message_menu(lets[0],
 +              want_reply ? PICK_ONE : PICK_NONE,
 +              xprname(otmp, (char *)0, lets[0], TRUE, 0L, 0L));
 +            if (out_cnt) *out_cnt = -1L;      /* select all */
 +            break;
 +        }
 +        }
 +        return ret;
 +    }
 +
 +    start_menu(win);
 +    if (wizard && iflags.override_ID) {
 +        char prompt[BUFSZ];
 +        any.a_char = -1;
 +        /* wiz_identify stuffed the wiz_identify cmd character
 +           into iflags.override_ID */
 +        Sprintf(prompt, "Debug Identify (%s to permanently identify)",
 +                visctrl(iflags.override_ID));
 +        add_menu(win, NO_GLYPH, &any,' ', iflags.override_ID, ATR_NONE,
 +                prompt, MENU_UNSELECTED);
 +    }
  nextclass:
 -      classcount = 0;
 -      any = zeroany;          /* set all bits to zero */
 -      for(otmp = invent; otmp; otmp = otmp->nobj) {
 -              ilet = otmp->invlet;
 -              if(!lets || !*lets || index(lets, ilet)) {
 -                      any = zeroany;          /* zero */
 -                      if (!flags.sortpack || otmp->oclass == *invlet) {
 -                          if (flags.sortpack && !classcount) {
 -                              add_menu(win, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
 +    classcount = 0;
 +    any = zeroany;            /* set all bits to zero */
 +    for(otmp = invent; otmp; otmp = otmp->nobj) {
 +        ilet = otmp->invlet;
 +        if(!lets || !*lets || index(lets, ilet)) {
 +            any = zeroany;            /* zero */
 +            if (!flags.sortpack || otmp->oclass == *invlet) {
 +                if (flags.sortpack && !classcount) {
 +                add_menu(win, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
-                     let_to_name(*invlet, FALSE), MENU_UNSELECTED);
+                                        let_to_name(*invlet, FALSE, (want_reply && iflags.menu_head_objsym)), MENU_UNSELECTED);
 -                              classcount++;
 -                          }
 -                          any.a_char = ilet;
 -                          add_menu(win, obj_to_glyph(otmp),
 -                                      &any, ilet, 0, ATR_NONE, doname(otmp),
 -                                      MENU_UNSELECTED);
 -                      }
 -              }
 -      }
 -      if (flags.sortpack) {
 -              if (*++invlet) goto nextclass;
 -              if (--invlet != venom_inv) {
 -                      invlet = venom_inv;
 -                      goto nextclass;
 -              }
 -      }
 -      end_menu(win, (char *) 0);
 +                classcount++;
 +                }
 +                any.a_char = ilet;
 +                add_menu(win, obj_to_glyph(otmp),
 +                    &any, ilet, 0, ATR_NONE, doname(otmp),
 +                    MENU_UNSELECTED);
 +            }
 +        }
 +    }
 +    if (flags.sortpack) {
 +        if (*++invlet) goto nextclass;
 +        if (--invlet != venom_inv) {
 +            invlet = venom_inv;
 +            goto nextclass;
 +        }
 +    }
 +    end_menu(win, (char *) 0);
  
 -      n = select_menu(win, want_reply ? PICK_ONE : PICK_NONE, &selected);
 -      if (n > 0) {
 -          ret = selected[0].item.a_char;
 -          if (out_cnt) *out_cnt = selected[0].count;
 -          free((genericptr_t)selected);
 -      } else
 -          ret = !n ? '\0' : '\033';   /* cancelled */
 +    n = select_menu(win, want_reply ? PICK_ONE : PICK_NONE, &selected);
 +    if (n > 0) {
 +        ret = selected[0].item.a_char;
 +        if (out_cnt) *out_cnt = selected[0].count;
 +        free((genericptr_t)selected);
 +    } else
 +        ret = !n ? '\0' : '\033';     /* cancelled */
  
 -      return ret;
 +    return ret;
  }
  
  /*
@@@ -1827,49 -1842,49 +1854,49 @@@ STATIC_OVL cha
  display_used_invlets(avoidlet)
  char avoidlet;
  {
 -      struct obj *otmp;
 -      char ilet, ret = 0;
 -      char *invlet = flags.inv_order;
 -      int n, classcount, invdone = 0;
 -      winid win;
 -      anything any;
 -      menu_item *selected;
 -
 -      if (invent) {
 -          win = create_nhwindow(NHW_MENU);
 -          start_menu(win);
 -          while (!invdone) {
 -              any = zeroany;          /* set all bits to zero */
 -              classcount = 0;
 -              for(otmp = invent; otmp; otmp = otmp->nobj) {
 -                  ilet = otmp->invlet;
 -                  if (ilet == avoidlet) continue;
 -                  if (!flags.sortpack || otmp->oclass == *invlet) {
 -                      if (flags.sortpack && !classcount) {
 -                              any = zeroany;          /* zero */
 -                              add_menu(win, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
 +    struct obj *otmp;
 +    char ilet, ret = 0;
 +    char *invlet = flags.inv_order;
 +    int n, classcount, invdone = 0;
 +    winid win;
 +    anything any;
 +    menu_item *selected;
 +
 +    if (invent) {
 +        win = create_nhwindow(NHW_MENU);
 +        start_menu(win);
 +        while (!invdone) {
 +        any = zeroany;                /* set all bits to zero */
 +        classcount = 0;
 +        for(otmp = invent; otmp; otmp = otmp->nobj) {
 +            ilet = otmp->invlet;
 +            if (ilet == avoidlet) continue;
 +            if (!flags.sortpack || otmp->oclass == *invlet) {
 +            if (flags.sortpack && !classcount) {
 +                any = zeroany;                /* zero */
 +                add_menu(win, NO_GLYPH, &any, 0, 0, iflags.menu_headings,
-                     let_to_name(*invlet, FALSE), MENU_UNSELECTED);
+                                        let_to_name(*invlet, FALSE, FALSE), MENU_UNSELECTED);
 -                              classcount++;
 -                      }
 -                      any.a_char = ilet;
 -                      add_menu(win, obj_to_glyph(otmp),
 -                              &any, ilet, 0, ATR_NONE, doname(otmp),
 -                              MENU_UNSELECTED);
 -                  }
 -              }
 -              if (flags.sortpack && *++invlet) continue;
 -              invdone = 1;
 -          }
 -          end_menu(win, "Inventory letters used:");
 -
 -          n = select_menu(win, PICK_NONE, &selected);
 -          if (n > 0) {
 -              ret = selected[0].item.a_char;
 -              free((genericptr_t)selected);
 -          } else
 -              ret = !n ? '\0' : '\033';       /* cancelled */
 -      }
 -      return ret;
 +                classcount++;
 +            }
 +            any.a_char = ilet;
 +            add_menu(win, obj_to_glyph(otmp),
 +                &any, ilet, 0, ATR_NONE, doname(otmp),
 +                MENU_UNSELECTED);
 +            }
 +            }
 +        if (flags.sortpack && *++invlet) continue;
 +        invdone = 1;
 +        }
 +        end_menu(win, "Inventory letters used:");
 +
 +        n = select_menu(win, PICK_NONE, &selected);
 +        if (n > 0) {
 +        ret = selected[0].item.a_char;
 +        free((genericptr_t)selected);
 +        } else
 +        ret = !n ? '\0' : '\033';     /* cancelled */
 +    }
 +    return ret;
  }
  
  /*
@@@ -1970,63 -1985,63 +1997,63 @@@ dounpaid(
      if (!flags.invlet_constant) reassign();
  
      do {
 -      classcount = 0;
 -      for (otmp = invent; otmp; otmp = otmp->nobj) {
 -          ilet = otmp->invlet;
 -          if (otmp->unpaid) {
 -              if (!flags.sortpack || otmp->oclass == *invlet) {
 -                  if (flags.sortpack && !classcount) {
 +    classcount = 0;
 +    for (otmp = invent; otmp; otmp = otmp->nobj) {
 +        ilet = otmp->invlet;
 +        if (otmp->unpaid) {
 +        if (!flags.sortpack || otmp->oclass == *invlet) {
 +            if (flags.sortpack && !classcount) {
-             putstr(win, 0, let_to_name(*invlet, TRUE));
+                       putstr(win, 0, let_to_name(*invlet, TRUE, FALSE));
 -                      classcount++;
 -                  }
 -
 -                  totcost += cost = unpaid_cost(otmp, FALSE);
 -                  iflags.suppress_price++; /* suppress "(unpaid)" suffix */
 -                  putstr(win, 0, xprname(otmp, distant_name(otmp, doname),
 -                                         ilet, TRUE, cost, 0L));
 -                  iflags.suppress_price--;
 -                  num_so_far++;
 -              }
 -          }
 -      }
 +            classcount++;
 +            }
 +
 +            totcost += cost = unpaid_cost(otmp, FALSE);
 +            iflags.suppress_price++; /* suppress "(unpaid)" suffix */
 +            putstr(win, 0, xprname(otmp, distant_name(otmp, doname),
 +                       ilet, TRUE, cost, 0L));
 +            iflags.suppress_price--;
 +            num_so_far++;
 +        }
 +        }
 +    }
      } while (flags.sortpack && (*++invlet));
  
      if (count > num_so_far) {
 -      /* something unpaid is contained */
 -      if (flags.sortpack)
 +    /* something unpaid is contained */
 +    if (flags.sortpack)
-         putstr(win, 0, let_to_name(CONTAINED_SYM, TRUE));
+           putstr(win, 0, let_to_name(CONTAINED_SYM, TRUE, FALSE));
 -      /*
 -       * Search through the container objects in the inventory for
 -       * unpaid items.  The top level inventory items have already
 -       * been listed.
 -       */
 -      for (otmp = invent; otmp; otmp = otmp->nobj) {
 -          if (Has_contents(otmp)) {
 -              long contcost = 0L;
 -
 -              marker = (struct obj *) 0;      /* haven't found any */
 -              while (find_unpaid(otmp->cobj, &marker)) {
 -                  totcost += cost = unpaid_cost(marker, FALSE);
 -                  contcost += cost;
 -                  if (otmp->cknown) {
 -                      iflags.suppress_price++; /* suppress "(unpaid)" sfx */
 -                      putstr(win, 0,
 -                             xprname(marker, distant_name(marker, doname),
 -                                     CONTAINED_SYM, TRUE, cost, 0L));
 -                      iflags.suppress_price--;
 -                  }
 -              }
 -              if (!otmp->cknown) {
 -                  char contbuf[BUFSZ];
 -
 -                  /* Shopkeeper knows what to charge for contents */
 -                  Sprintf(contbuf, "%s contents", s_suffix(xname(otmp)));
 -                  putstr(win, 0,
 -                         xprname((struct obj *)0, contbuf,
 -                                 CONTAINED_SYM, TRUE, contcost, 0L));
 -              }
 -          }
 -      }
 +    /*
 +     * Search through the container objects in the inventory for
 +     * unpaid items.  The top level inventory items have already
 +     * been listed.
 +     */
 +    for (otmp = invent; otmp; otmp = otmp->nobj) {
 +        if (Has_contents(otmp)) {
 +        long contcost = 0L;
 +
 +        marker = (struct obj *) 0;    /* haven't found any */
 +        while (find_unpaid(otmp->cobj, &marker)) {
 +            totcost += cost = unpaid_cost(marker, FALSE);
 +            contcost += cost;
 +            if (otmp->cknown) {
 +            iflags.suppress_price++; /* suppress "(unpaid)" sfx */
 +            putstr(win, 0,
 +                   xprname(marker, distant_name(marker, doname),
 +                       CONTAINED_SYM, TRUE, cost, 0L));
 +            iflags.suppress_price--;
 +            }
 +        }
 +        if (!otmp->cknown) {
 +            char contbuf[BUFSZ];
 +
 +            /* Shopkeeper knows what to charge for contents */
 +            Sprintf(contbuf, "%s contents", s_suffix(xname(otmp)));
 +            putstr(win, 0,
 +               xprname((struct obj *)0, contbuf,
 +                   CONTAINED_SYM, TRUE, contcost, 0L));
 +        }
 +        }
 +    }
      }
  
      putstr(win, 0, "");
@@@ -2686,33 -2701,45 +2713,45 @@@ static NEARDATA char *invbuf = (char *)
  static NEARDATA unsigned invbufsiz = 0;
  
  char *
- let_to_name(let,unpaid)
+ let_to_name(let,unpaid,showsym)
  char let;
- boolean unpaid;
+ boolean unpaid,showsym;
  {
 -      const char *class_name;
 -      const char *pos;
 -      int oclass = (let >= 1 && let < MAXOCLASSES) ? let : 0;
 -      unsigned len;
 -
 -      if (oclass)
 -          class_name = names[oclass];
 -      else if ((pos = index(oth_symbols, let)) != 0)
 -          class_name = oth_names[pos - oth_symbols];
 -      else
 -          class_name = names[0];
+       const char *ocsymfmt = "  ('%c')";
+       const int invbuf_sympadding = 8; /* arbitrary */
 +    const char *class_name;
 +    const char *pos;
 +    int oclass = (let >= 1 && let < MAXOCLASSES) ? let : 0;
 +    unsigned len;
 +
 +    if (oclass)
 +        class_name = names[oclass];
 +    else if ((pos = index(oth_symbols, let)) != 0)
 +        class_name = oth_names[pos - oth_symbols];
 +    else
 +        class_name = names[0];
  
-     len = strlen(class_name) + (unpaid ? sizeof "unpaid_" : sizeof "");
+       len = strlen(class_name) + (unpaid ? sizeof "unpaid_" : sizeof "") +
+           (oclass ? (strlen(ocsymfmt)+invbuf_sympadding) : 0);
 -      if (len > invbufsiz) {
 -          if (invbuf) free((genericptr_t)invbuf);
 -          invbufsiz = len + 10; /* add slop to reduce incremental realloc */
 -          invbuf = (char *) alloc(invbufsiz);
 -      }
 -      if (unpaid)
 -          Strcat(strcpy(invbuf, "Unpaid "), class_name);
 -      else
 -          Strcpy(invbuf, class_name);
 +    if (len > invbufsiz) {
 +        if (invbuf) free((genericptr_t)invbuf);
 +        invbufsiz = len + 10; /* add slop to reduce incremental realloc */
 +        invbuf = (char *) alloc(invbufsiz);
 +    }
 +    if (unpaid)
 +        Strcat(strcpy(invbuf, "Unpaid "), class_name);
 +    else
 +        Strcpy(invbuf, class_name);
+       if ((oclass != 0) && showsym) {
+           char *bp = eos(invbuf);
+           int mlen = invbuf_sympadding - strlen(class_name);
+           while (--mlen > 0) {
+               *bp = ' '; bp++;
+           }
+           *bp = '\0';
+           Sprintf(eos(invbuf), ocsymfmt, def_oc_syms[oclass].sym);
+       }
 -      return invbuf;
 +    return invbuf;
  }
  
  /* release the static buffer used by let_to_name() */
diff --cc src/mkobj.c
index d9312a0f3432e71b19d3606c72ccdc8c16c39f9d,4092401cd55a846d5021646654135a8b5b01bf25..25e598746eb3331886c4141a63c014921340acd5
@@@ -1526,13 -1518,12 +1526,12 @@@ struct obj *otmp
      long age, retval = otmp->age;
      
      if (otmp->otyp == CORPSE && otmp->on_ice) {
 -      /* Adjust the age; must be same as obj_timer_checks() for off ice*/
 -      age = monstermoves - otmp->age;
 -      retval += age * (ROT_ICE_ADJUSTMENT-1) / ROT_ICE_ADJUSTMENT;
 +    /* Adjust the age; must be same as obj_timer_checks() for off ice*/
 +    age = monstermoves - otmp->age;
 +    retval += age * (ROT_ICE_ADJUSTMENT-1) / ROT_ICE_ADJUSTMENT;
-     debugpline("The %s age has ice modifications:otmp->age = %ld, returning %ld.",
-         s_suffix(doname(otmp)),otmp->age, retval);
-     debugpline("Effective age of corpse: %ld.",
-         monstermoves - retval);
+       debugpline3("The %s age has ice modifications: otmp->age = %ld, returning %ld.",
+               s_suffix(doname(otmp)), otmp->age, retval);
+       debugpline1("Effective age of corpse: %ld.", monstermoves - retval);
      }
      return retval;
  }
@@@ -1551,50 -1542,52 +1550,52 @@@ int force;   /* 0 = no force so do checks
  
      /* Check for corpses just placed on or in ice */
      if (otmp->otyp == CORPSE && (on_floor || buried) && is_ice(x,y)) {
 -      tleft = stop_timer(action, obj_to_any(otmp));
 -      if (tleft == 0L) {
 -              action = REVIVE_MON;
 -              tleft = stop_timer(action, obj_to_any(otmp));
 -      
 -      if (tleft != 0L) {
 -          long age;
 -          
 -          /* mark the corpse as being on ice */
 -          otmp->on_ice = 1;
 +    tleft = stop_timer(action, obj_to_any(otmp));
 +    if (tleft == 0L) {
 +        action = REVIVE_MON;
 +        tleft = stop_timer(action, obj_to_any(otmp));
 +    } 
 +    if (tleft != 0L) {
 +        long age;
 +        
 +        /* mark the corpse as being on ice */
 +        otmp->on_ice = 1;
-         debugpline("%s is now on ice at %d,%d.", The(xname(otmp)),x,y);
+           debugpline3("%s is now on ice at <%d,%d>.",
+                       The(xname(otmp)), x, y);
 -          /* Adjust the time remaining */
 -          tleft *= ROT_ICE_ADJUSTMENT;
 -          restart_timer = TRUE;
 -          /* Adjust the age; time spent off ice needs to be multiplied
 -             by the ice adjustment and subtracted from the age so that
 -             later calculations behave as if it had been on ice during
 -             that time (longwinded way of saying this is the inverse
 -             of removing it from the ice and of peeking at its age). */
 -          age = monstermoves - otmp->age;
 -          otmp->age = monstermoves - (age * ROT_ICE_ADJUSTMENT);
 -      }
 +        /* Adjust the time remaining */
 +        tleft *= ROT_ICE_ADJUSTMENT;
 +        restart_timer = TRUE;
 +        /* Adjust the age; time spent off ice needs to be multiplied
 +           by the ice adjustment and subtracted from the age so that
 +           later calculations behave as if it had been on ice during
 +           that time (longwinded way of saying this is the inverse
 +           of removing it from the ice and of peeking at its age). */
 +        age = monstermoves - otmp->age;
 +        otmp->age = monstermoves - (age * ROT_ICE_ADJUSTMENT);
 +    }
      }
      /* Check for corpses coming off ice */
      else if ((force < 0) ||
 -           (otmp->otyp == CORPSE && otmp->on_ice &&
 -           !((on_floor || buried) && is_ice(x,y)))) {
 -      tleft = stop_timer(action, obj_to_any(otmp));
 -      if (tleft == 0L) {
 -              action = REVIVE_MON;
 -              tleft = stop_timer(action, obj_to_any(otmp));
 -      }
 -      if (tleft != 0L) {
 -              long age;
 -
 -              otmp->on_ice = 0;
 +         (otmp->otyp == CORPSE && otmp->on_ice &&
 +         !((on_floor || buried) && is_ice(x,y)))) {
 +    tleft = stop_timer(action, obj_to_any(otmp));
 +    if (tleft == 0L) {
 +        action = REVIVE_MON;
 +        tleft = stop_timer(action, obj_to_any(otmp));
 +    }
 +    if (tleft != 0L) {
 +        long age;
 +
 +        otmp->on_ice = 0;
-             debugpline("%s is no longer on ice at %d,%d.", The(xname(otmp)),x,y);
+               debugpline3("%s is no longer on ice at <%d,%d>.",
+                           The(xname(otmp)), x, y);
 -              /* Adjust the remaining time */
 -              tleft /= ROT_ICE_ADJUSTMENT;
 -              restart_timer = TRUE;
 -              /* Adjust the age */
 -              age = monstermoves - otmp->age;
 -              otmp->age += age * (ROT_ICE_ADJUSTMENT-1) / ROT_ICE_ADJUSTMENT;
 -      }
 +        /* Adjust the remaining time */
 +        tleft /= ROT_ICE_ADJUSTMENT;
 +        restart_timer = TRUE;
 +        /* Adjust the age */
 +        age = monstermoves - otmp->age;
 +        otmp->age += age * (ROT_ICE_ADJUSTMENT-1) / ROT_ICE_ADJUSTMENT;
 +    }
      }
      /* now re-start the timer with the appropriate modifications */ 
      if (restart_timer)
diff --cc src/mon.c
index ce04cc3824a10836f8cebf5b91e8104f7bb88792,d9bc63d6d0005a435b45100ea541d2b4bcbaabd7..2da55e37fd6c0c1859e9795efaae2e6a2ad70bc9
+++ b/src/mon.c
@@@ -1278,9 -1259,9 +1278,9 @@@ register struct monst *mtmp, *mtmp2
  
      /* transfer the monster's inventory */
      for (otmp = mtmp2->minvent; otmp; otmp = otmp->nobj) {
 -      if (otmp->where != OBJ_MINVENT || otmp->ocarry != mtmp)
 +    if (otmp->where != OBJ_MINVENT || otmp->ocarry != mtmp)
-         debugpline("replmon: minvent inconsistency");
+           debugpline0("replmon: minvent inconsistency");
 -      otmp->ocarry = mtmp2;
 +    otmp->ocarry = mtmp2;
      }
      mtmp->minvent = 0;
  
  mondead(mtmp)
  register struct monst *mtmp;
  {
 -      struct permonst *mptr;
 -      int tmp;
 -
 -      lifesaved_monster(mtmp);
 -      if (mtmp->mhp > 0) return;
 -
 -      if (is_vampshifter(mtmp)) {
 -              int mndx = mtmp->cham;
 -              int x = mtmp->mx, y = mtmp->my;
 -              /* this only happens if shapeshifted */
 -              if (mndx >= LOW_PM && mndx != monsndx(mtmp->data)) {
 -                      char buf[BUFSZ];
 -                      boolean in_door = amorphous(mtmp->data) &&
 -                                       closed_door(mtmp->mx,mtmp->my);
 -                      Sprintf(buf,
 -                              "The %s%s suddenly %s and rises as %%s!",
 -                              (nonliving(mtmp->data) ||
 -                               noncorporeal(mtmp->data) ||
 -                               amorphous(mtmp->data)) ? "" : "seemingly dead ",
 -                              x_monnam(mtmp, ARTICLE_NONE, (char *)0, 
 -                                  SUPPRESS_SADDLE | SUPPRESS_HALLUCINATION |
 -                                  SUPPRESS_INVISIBLE | SUPPRESS_IT, FALSE),
 -                              (nonliving(mtmp->data) ||
 -                               noncorporeal(mtmp->data) ||
 -                               amorphous(mtmp->data)) ?
 -                              "reconstitutes" : "transforms");
 -                      mtmp->mcanmove = 1;
 -                      mtmp->mfrozen = 0;
 -                      if (mtmp->mhpmax <= 0) mtmp->mhpmax = 10;
 -                      mtmp->mhp = mtmp->mhpmax;
 -                      /* this can happen if previously a fog cloud */
 -                      if (u.uswallow && (mtmp == u.ustuck))
 -                              expels(mtmp, mtmp->data, FALSE);
 -                      if (in_door) {
 -                              coord new_xy;
 -                              if (enexto(&new_xy,
 -                                          mtmp->mx, mtmp->my, &mons[mndx])) {
 -                                      rloc_to(mtmp, new_xy.x, new_xy.y);
 -                              }
 -                      }
 -                      newcham(mtmp, &mons[mndx], FALSE, FALSE);
 -                      if (mtmp->data == &mons[mndx])
 -                              mtmp->cham = NON_PM;
 -                      else
 -                              mtmp->cham = mndx;
 -                      if ((!Blind && canseemon(mtmp)) || sensemon(mtmp))
 -                              pline(buf, a_monnam(mtmp));
 -                      newsym(x,y);
 -                      return;
 -              }
 -      }
 -
 -      /* dead vault guard is actually kept at coordinate <0,0> until
 -         his temporary corridor to/from the vault has been removed;
 -         need to do this after life-saving and before m_detach() */
 -      if (mtmp->isgd && !grddead(mtmp)) return;
 -
 -      /* Player is thrown from his steed when it dies */
 -      if (mtmp == u.usteed)
 -              dismount_steed(DISMOUNT_GENERIC);
 -
 -      mptr = mtmp->data;              /* save this for m_detach() */
 -      /* restore chameleon, lycanthropes to true form at death */
 -      if (mtmp->cham >= LOW_PM) {
 -          set_mon_data(mtmp, &mons[mtmp->cham], -1);
 -          mtmp->cham = NON_PM;
 -      }
 -      else if (mtmp->data == &mons[PM_WEREJACKAL])
 -          set_mon_data(mtmp, &mons[PM_HUMAN_WEREJACKAL], -1);
 -      else if (mtmp->data == &mons[PM_WEREWOLF])
 -          set_mon_data(mtmp, &mons[PM_HUMAN_WEREWOLF], -1);
 -      else if (mtmp->data == &mons[PM_WERERAT])
 -          set_mon_data(mtmp, &mons[PM_HUMAN_WERERAT], -1);
 -
 -      /* if MAXMONNO monsters of a given type have died, and it
 -       * can be done, extinguish that monster.
 -       *
 -       * mvitals[].died does double duty as total number of dead monsters
 -       * and as experience factor for the player killing more monsters.
 -       * this means that a dragon dying by other means reduces the
 -       * experience the player gets for killing a dragon directly; this
 -       * is probably not too bad, since the player likely finagled the
 -       * first dead dragon via ring of conflict or pets, and extinguishing
 -       * based on only player kills probably opens more avenues of abuse
 -       * for rings of conflict and such.
 -       */
 -      tmp = monsndx(mtmp->data);
 -      if (mvitals[tmp].died < 255) mvitals[tmp].died++;
 -
 -      /* if it's a (possibly polymorphed) quest leader, mark him as dead */
 -      if (mtmp->m_id == quest_status.leader_m_id)
 -          quest_status.leader_is_dead = TRUE;
 +    struct permonst *mptr;
 +    int tmp;
 +
 +    lifesaved_monster(mtmp);
 +    if (mtmp->mhp > 0) return;
 +
 +    if (is_vampshifter(mtmp)) {
 +        int mndx = mtmp->cham;
 +        int x = mtmp->mx, y = mtmp->my;
 +        /* this only happens if shapeshifted */
 +        if (mndx >= LOW_PM && mndx != monsndx(mtmp->data)) {
 +            char buf[BUFSZ];
 +            boolean in_door = amorphous(mtmp->data) &&
 +                     closed_door(mtmp->mx,mtmp->my);
 +            Sprintf(buf,
 +                "The %s%s suddenly %s and rises as %%s!",
 +                (nonliving(mtmp->data) ||
 +                 noncorporeal(mtmp->data) ||
 +                 amorphous(mtmp->data)) ? "" : "seemingly dead ",
 +                    x_monnam(mtmp, ARTICLE_NONE, (char *)0, 
 +                        SUPPRESS_SADDLE | SUPPRESS_HALLUCINATION |
 +                    SUPPRESS_INVISIBLE | SUPPRESS_IT, FALSE),
 +                (nonliving(mtmp->data) ||
 +                 noncorporeal(mtmp->data) ||
 +                 amorphous(mtmp->data)) ?
 +                "reconstitutes" : "transforms");
 +            mtmp->mcanmove = 1;
 +            mtmp->mfrozen = 0;
 +            if (mtmp->mhpmax <= 0) mtmp->mhpmax = 10;
 +            mtmp->mhp = mtmp->mhpmax;
 +            /* this can happen if previously a fog cloud */
 +            if (u.uswallow && (mtmp == u.ustuck))
 +                expels(mtmp, mtmp->data, FALSE);
 +            if (in_door) {
 +                coord new_xy;
 +                if (enexto(&new_xy,
 +                        mtmp->mx, mtmp->my, &mons[mndx])) {
 +                    rloc_to(mtmp, new_xy.x, new_xy.y);
 +                }
 +            }
 +            newcham(mtmp, &mons[mndx], FALSE, FALSE);
 +            if (mtmp->data == &mons[mndx])
 +                mtmp->cham = NON_PM;
 +            else
 +                mtmp->cham = mndx;
 +            if ((!Blind && canseemon(mtmp)) || sensemon(mtmp))
 +                pline(buf, a_monnam(mtmp));
 +            newsym(x,y);
 +            return;
 +        }
 +    }
 +
 +    /* dead vault guard is actually kept at coordinate <0,0> until
 +       his temporary corridor to/from the vault has been removed;
 +       need to do this after life-saving and before m_detach() */
 +    if (mtmp->isgd && !grddead(mtmp)) return;
 +
 +    /* Player is thrown from his steed when it dies */
 +    if (mtmp == u.usteed)
 +        dismount_steed(DISMOUNT_GENERIC);
 +
 +    mptr = mtmp->data;                /* save this for m_detach() */
 +    /* restore chameleon, lycanthropes to true form at death */
 +    if (mtmp->cham >= LOW_PM) {
 +        set_mon_data(mtmp, &mons[mtmp->cham], -1);
 +        mtmp->cham = NON_PM;
 +    }
 +    else if (mtmp->data == &mons[PM_WEREJACKAL])
 +        set_mon_data(mtmp, &mons[PM_HUMAN_WEREJACKAL], -1);
 +    else if (mtmp->data == &mons[PM_WEREWOLF])
 +        set_mon_data(mtmp, &mons[PM_HUMAN_WEREWOLF], -1);
 +    else if (mtmp->data == &mons[PM_WERERAT])
 +        set_mon_data(mtmp, &mons[PM_HUMAN_WERERAT], -1);
 +
 +    /* if MAXMONNO monsters of a given type have died, and it
 +     * can be done, extinguish that monster.
 +     *
 +     * mvitals[].died does double duty as total number of dead monsters
 +     * and as experience factor for the player killing more monsters.
 +     * this means that a dragon dying by other means reduces the
 +     * experience the player gets for killing a dragon directly; this
 +     * is probably not too bad, since the player likely finagled the
 +     * first dead dragon via ring of conflict or pets, and extinguishing
 +     * based on only player kills probably opens more avenues of abuse
 +     * for rings of conflict and such.
 +     */
 +    tmp = monsndx(mtmp->data);
 +    if (mvitals[tmp].died < 255) mvitals[tmp].died++;
 +
 +    /* if it's a (possibly polymorphed) quest leader, mark him as dead */
 +    if (mtmp->m_id == quest_status.leader_m_id)
 +        quest_status.leader_is_dead = TRUE;
  #ifdef MAIL
 -      /* if the mail daemon dies, no more mail delivery.  -3. */
 -      if (tmp == PM_MAIL_DAEMON) mvitals[tmp].mvflags |= G_GENOD;
 +    /* if the mail daemon dies, no more mail delivery.  -3. */
 +    if (tmp == PM_MAIL_DAEMON) mvitals[tmp].mvflags |= G_GENOD;
  #endif
  
 -      if (mtmp->data->mlet == S_KOP) {
 -          /* Dead Kops may come back. */
 -          switch(rnd(5)) {
 -              case 1:      /* returns near the stairs */
 -                      (void) makemon(mtmp->data,xdnstair,ydnstair,NO_MM_FLAGS);
 -                      break;
 -              case 2:      /* randomly */
 -                      (void) makemon(mtmp->data,0,0,NO_MM_FLAGS);
 -                      break;
 -              default:
 -                      break;
 -          }
 -      }
 -      if(mtmp->iswiz) wizdead();
 -      if(mtmp->data->msound == MS_NEMESIS) nemdead();
 +    if (mtmp->data->mlet == S_KOP) {
 +        /* Dead Kops may come back. */
 +        switch(rnd(5)) {
 +        case 1:            /* returns near the stairs */
 +            (void) makemon(mtmp->data,xdnstair,ydnstair,NO_MM_FLAGS);
 +            break;
 +        case 2:            /* randomly */
 +            (void) makemon(mtmp->data,0,0,NO_MM_FLAGS);
 +            break;
 +        default:
 +            break;
 +        }
 +    }
 +    if(mtmp->iswiz) wizdead();
 +    if(mtmp->data->msound == MS_NEMESIS) nemdead();
+         if(mtmp->data == &mons[PM_MEDUSA])
+             u.uachieve.killed_medusa = 1;
 -      if(glyph_is_invisible(levl[mtmp->mx][mtmp->my].glyph))
 -          unmap_object(mtmp->mx, mtmp->my);
 -      m_detach(mtmp, mptr);
 +    if(glyph_is_invisible(levl[mtmp->mx][mtmp->my].glyph))
 +        unmap_object(mtmp->mx, mtmp->my);
 +    m_detach(mtmp, mptr);
  }
  
  /* TRUE if corpse might be dropped, magr may die if mon was swallowed */
diff --cc src/objnam.c
index 8b418d769caa919471d0594a50e4b70d1acc979b,898dfb60a395457422882e9021eca03dadad626a..13a14504361931fac27bc83695e6fbf166511170
@@@ -1691,22 -1680,22 +1691,22 @@@ static struct sing_plur one_off[] = 
  };
  
  static const char *const as_is[] = {
 -      /* makesingular() leaves these plural due to how they're used */
 -      "boots", "shoes",
 +    /* makesingular() leaves these plural due to how they're used */
 +    "boots", "shoes",
-     "gloves", "lenses", "scales",
+       "gloves", "lenses", "scales", "eyes",
 -      "gauntlets",
 -      "iron bars",
 -      /* both singular and plural are spelled the same */
 -      "deer", "fish", "tuna", "yaki", "-hai",
 -      "krill", "manes", "ninja", "sheep", "ronin", "roshi", "shito", "tengu",
 -      "ki-rin", "Nazgul",
 -      "gunyoki", "piranha", "samurai",
 -      "shuriken",
 -      0,
 -      /* Note:  "fish" and "piranha" are collective plurals, suitable
 -         for "wiped out all <foo>".  For "3 <foo>", they should be
 -         "fishes" and "piranhas" instead.  We settle for collective
 -         variant instead of attempting to support both. */
 +    "gauntlets",
 +    "iron bars",
 +    /* both singular and plural are spelled the same */
 +    "deer", "fish", "tuna", "yaki", "-hai",
 +    "krill", "manes", "ninja", "sheep", "ronin", "roshi", "shito", "tengu",
 +    "ki-rin", "Nazgul",
 +    "gunyoki", "piranha", "samurai",
 +    "shuriken",
 +    0,
 +    /* Note:  "fish" and "piranha" are collective plurals, suitable
 +       for "wiped out all <foo>".  For "3 <foo>", they should be
 +       "fishes" and "piranhas" instead.  We settle for collective
 +       variant instead of attempting to support both. */
  };
  
  /* singularize/pluralize decisiions common to both makesingular & makeplural */