OSDN Git Service

upgrade to 3.6.1
[jnethack/source.git] / src / detect.c
index 73e4ef6..97365de 100644 (file)
@@ -1,5 +1,6 @@
-/* NetHack 3.6 detect.c        $NHDT-Date: 1446369464 2015/11/01 09:17:44 $  $NHDT-Branch: master $:$NHDT-Revision: 1.61 $ */
+/* NetHack 3.6 detect.c        $NHDT-Date: 1522891623 2018/04/05 01:27:03 $  $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.81 $ */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/*-Copyright (c) Robert Patrick Rankin, 2018. */
 /* NetHack may be freely redistributed.  See license for details. */
 
 /*
 
 extern boolean known; /* from read.c */
 
+STATIC_DCL boolean NDECL(unconstrain_map);
+STATIC_DCL void NDECL(reconstrain_map);
+STATIC_DCL void FDECL(browse_map, (int, const char *));
+STATIC_DCL void FDECL(map_monst, (struct monst *, BOOLEAN_P));
 STATIC_DCL void FDECL(do_dknown_of, (struct obj *));
 STATIC_DCL boolean FDECL(check_map_spot, (int, int, CHAR_P, unsigned));
 STATIC_DCL boolean FDECL(clear_stale_map, (CHAR_P, unsigned));
@@ -25,9 +30,136 @@ STATIC_DCL int FDECL(detect_obj_traps, (struct obj *, BOOLEAN_P, int));
 STATIC_DCL void FDECL(show_map_spot, (int, int));
 STATIC_PTR void FDECL(findone, (int, int, genericptr_t));
 STATIC_PTR void FDECL(openone, (int, int, genericptr_t));
+STATIC_DCL int FDECL(mfind0, (struct monst *, BOOLEAN_P));
+STATIC_DCL int FDECL(reveal_terrain_getglyph, (int, int, int,
+                                               unsigned, int, int));
 
-/* Recursively search obj for an object in class oclass and return 1st found
- */
+/* bring hero out from underwater or underground or being engulfed;
+   return True iff any change occurred */
+STATIC_OVL boolean
+unconstrain_map()
+{
+    boolean res = u.uinwater || u.uburied || u.uswallow;
+
+    /* bring Underwater, buried, or swallowed hero to normal map */
+    iflags.save_uinwater = u.uinwater, u.uinwater = 0;
+    iflags.save_uburied  = u.uburied,  u.uburied  = 0;
+    iflags.save_uswallow = u.uswallow, u.uswallow = 0;
+
+    return res;
+}
+
+/* put hero back underwater or underground or engulfed */
+STATIC_OVL void
+reconstrain_map()
+{
+    u.uinwater = iflags.save_uinwater, iflags.save_uinwater = 0;
+    u.uburied  = iflags.save_uburied,  iflags.save_uburied  = 0;
+    u.uswallow = iflags.save_uswallow, iflags.save_uswallow = 0;
+}
+
+/* use getpos()'s 'autodescribe' to view whatever is currently shown on map */
+STATIC_DCL void
+browse_map(ter_typ, ter_explain)
+int ter_typ;
+const char *ter_explain;
+{
+    coord dummy_pos; /* don't care whether player actually picks a spot */
+    boolean save_autodescribe;
+
+    dummy_pos.x = u.ux, dummy_pos.y = u.uy; /* starting spot for getpos() */
+    save_autodescribe = iflags.autodescribe;
+    iflags.autodescribe = TRUE;
+    iflags.terrainmode = ter_typ;
+    getpos(&dummy_pos, FALSE, ter_explain);
+    iflags.terrainmode = 0;
+    iflags.autodescribe = save_autodescribe;
+}
+
+/* extracted from monster_detection() so can be shared by do_vicinity_map() */
+STATIC_DCL void
+map_monst(mtmp, showtail)
+struct monst *mtmp;
+boolean showtail;
+{
+    if (def_monsyms[(int) mtmp->data->mlet].sym == ' ')
+        show_glyph(mtmp->mx, mtmp->my, detected_mon_to_glyph(mtmp));
+    else
+        show_glyph(mtmp->mx, mtmp->my,
+                   mtmp->mtame ? pet_to_glyph(mtmp) : mon_to_glyph(mtmp));
+
+    if (showtail && mtmp->data == &mons[PM_LONG_WORM])
+        detect_wsegs(mtmp, 0);
+}
+
+/* this is checking whether a trap symbol represents a trapped chest,
+   not whether a trapped chest is actually present */
+boolean
+trapped_chest_at(ttyp, x, y)
+int ttyp;
+int x, y;
+{
+    struct monst *mtmp;
+    struct obj *otmp;
+
+    if (!glyph_is_trap(glyph_at(x, y)))
+        return FALSE;
+    if (ttyp != BEAR_TRAP || (Hallucination && rn2(20)))
+        return FALSE;
+
+    /*
+     * TODO?  We should check containers recursively like the trap
+     * detecting routine does.  Chests and large boxes do not nest in
+     * themselves or each other, but could be contained inside statues.
+     *
+     * For farlook, we should also check for buried containers, but
+     * for '^' command to examine adjacent trap glyph, we shouldn't.
+     */
+
+    /* on map, presence of any trappable container will do */
+    if (sobj_at(CHEST, x, y) || sobj_at(LARGE_BOX, x, y))
+        return TRUE;
+    /* in inventory, we need to find one which is actually trapped */
+    if (x == u.ux && y == u.uy) {
+        for (otmp = invent; otmp; otmp = otmp->nobj)
+            if (Is_box(otmp) && otmp->otrapped)
+                return TRUE;
+        if (u.usteed) { /* steed isn't on map so won't be found by m_at() */
+            for (otmp = u.usteed->minvent; otmp; otmp = otmp->nobj)
+                if (Is_box(otmp) && otmp->otrapped)
+                    return TRUE;
+        }
+    }
+    if ((mtmp = m_at(x, y)) != 0)
+        for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
+            if (Is_box(otmp) && otmp->otrapped)
+                return TRUE;
+    return FALSE;
+}
+
+/* this is checking whether a trap symbol represents a trapped door,
+   not whether the door here is actually trapped */
+boolean
+trapped_door_at(ttyp, x, y)
+int ttyp;
+int x, y;
+{
+    struct rm *lev;
+
+    if (!glyph_is_trap(glyph_at(x, y)))
+        return FALSE;
+    if (ttyp != BEAR_TRAP || (Hallucination && rn2(20)))
+        return FALSE;
+    lev = &levl[x][y];
+    if (!IS_DOOR(lev->typ))
+        return FALSE;
+    if ((lev->doormask & (D_NODOOR | D_BROKEN | D_ISOPEN)) != 0
+         && trapped_chest_at(ttyp, x, y))
+        return FALSE;
+    return TRUE;
+}
+
+/* recursively search obj for an object in class oclass, return 1st found */
 struct obj *
 o_in(obj, oclass)
 struct obj *obj;
@@ -43,7 +175,7 @@ char oclass;
         for (otmp = obj->cobj; otmp; otmp = otmp->nobj)
             if (otmp->oclass == oclass)
                 return otmp;
-            else if (Has_contents(otmp) && (temp = o_in(otmp, oclass)))
+            else if (Has_contents(otmp) && (temp = o_in(otmp, oclass)) != 0)
                 return temp;
     }
     return (struct obj *) 0;
@@ -68,7 +200,7 @@ unsigned material;
             if (objects[otmp->otyp].oc_material == material)
                 return otmp;
             else if (Has_contents(otmp)
-                     && (temp = o_material(otmp, material)))
+                     && (temp = o_material(otmp, material)) != 0)
                 return temp;
     }
     return (struct obj *) 0;
@@ -170,28 +302,36 @@ register struct obj *sobj;
 {
     register struct obj *obj;
     register struct monst *mtmp;
-    struct obj *temp;
-    boolean stale;
+    struct obj gold, *temp = 0;
+    boolean stale, ugold = FALSE, steedgold = FALSE;
+    int ter_typ = TER_DETECT | TER_OBJ;
 
-    known = stale =
-        clear_stale_map(COIN_CLASS, (unsigned) (sobj->blessed ? GOLD : 0));
+    known = stale = clear_stale_map(COIN_CLASS,
+                                    (unsigned) (sobj->blessed ? GOLD : 0));
 
     /* look for gold carried by monsters (might be in a container) */
     for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
         if (DEADMONSTER(mtmp))
             continue; /* probably not needed in this case but... */
         if (findgold(mtmp->minvent) || monsndx(mtmp->data) == PM_GOLD_GOLEM) {
-            known = TRUE;
-            goto outgoldmap; /* skip further searching */
-        } else
+            if (mtmp == u.usteed) {
+                steedgold = TRUE;
+            } else {
+                known = TRUE;
+                goto outgoldmap; /* skip further searching */
+            }
+        } else {
             for (obj = mtmp->minvent; obj; obj = obj->nobj)
-                if (sobj->blessed && o_material(obj, GOLD)) {
-                    known = TRUE;
-                    goto outgoldmap;
-                } else if (o_in(obj, COIN_CLASS)) {
-                    known = TRUE;
-                    goto outgoldmap; /* skip further searching */
+                if ((sobj->blessed && o_material(obj, GOLD))
+                    || o_in(obj, COIN_CLASS)) {
+                    if (mtmp == u.usteed) {
+                        steedgold = TRUE;
+                    } else {
+                        known = TRUE;
+                        goto outgoldmap; /* skip further searching */
+                    }
                 }
+        }
     }
 
     /* look for gold objects */
@@ -212,22 +352,31 @@ register struct obj *sobj;
            adjust message if you have gold in your inventory */
         if (sobj) {
             char buf[BUFSZ];
-            if (youmonst.data == &mons[PM_GOLD_GOLEM]) {
+
+            if (youmonst.data == &mons[PM_GOLD_GOLEM])
 /*JP
                 Sprintf(buf, "You feel like a million %s!", currency(2L));
 */
                 Strcpy(buf, "\82 \82È\82½\82Í\8bà\8e\9d\82¿\82É\82È\82Á\82½\82æ\82¤\82É\8a´\82\82½\81I");
-            } else if (hidden_gold() || money_cnt(invent))
+            else if (money_cnt(invent) || hidden_gold())
                 Strcpy(buf,
 /*JP
                    "You feel worried about your future financial situation.");
 */
                    "\82 \82È\82½\82Í\8f«\97\88\82Ì\8co\8dÏ\8fó\8bµ\82ª\90S\94z\82É\82È\82Á\82½\81D");
+            else if (steedgold)
+                Sprintf(buf, "You feel interested in %s financial situation.",
+                        s_suffix(x_monnam(u.usteed,
+                                          u.usteed->mtame ? ARTICLE_YOUR
+                                                          : ARTICLE_THE,
+                                          (char *) 0,
+                                          SUPPRESS_SADDLE, FALSE)));
             else
 /*JP
                 Strcpy(buf, "You feel materially poor.");
 */
                 Strcpy(buf, "\82 \82È\82½\82Í\82Ð\82à\82\82³\82ð\8a´\82\82½\81D");
+
             strange_feeling(sobj, buf);
         }
         return 1;
@@ -244,56 +393,67 @@ register struct obj *sobj;
 outgoldmap:
     cls();
 
-    iflags.save_uinwater = u.uinwater, iflags.save_uburied = u.uburied;
-    u.uinwater = u.uburied = 0;
+    (void) unconstrain_map();
     /* Discover gold locations. */
     for (obj = fobj; obj; obj = obj->nobj) {
-        if (sobj->blessed && (temp = o_material(obj, GOLD))) {
+        if (sobj->blessed && (temp = o_material(obj, GOLD)) != 0) {
             if (temp != obj) {
                 temp->ox = obj->ox;
                 temp->oy = obj->oy;
             }
             map_object(temp, 1);
-        } else if ((temp = o_in(obj, COIN_CLASS))) {
+        } else if ((temp = o_in(obj, COIN_CLASS)) != 0) {
             if (temp != obj) {
                 temp->ox = obj->ox;
                 temp->oy = obj->oy;
             }
             map_object(temp, 1);
         }
+        if (temp && temp->ox == u.ux && temp->oy == u.uy)
+            ugold = TRUE;
     }
     for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
         if (DEADMONSTER(mtmp))
             continue; /* probably overkill here */
+        temp = 0;
         if (findgold(mtmp->minvent) || monsndx(mtmp->data) == PM_GOLD_GOLEM) {
-            struct obj gold;
             gold = zeroobj; /* ensure oextra is cleared too */
             gold.otyp = GOLD_PIECE;
+            gold.quan = (long) rnd(10); /* usually more than 1 */
             gold.ox = mtmp->mx;
             gold.oy = mtmp->my;
             map_object(&gold, 1);
-        } else
+            temp = &gold;
+        } else {
             for (obj = mtmp->minvent; obj; obj = obj->nobj)
-                if (sobj->blessed && (temp = o_material(obj, GOLD))) {
+                if (sobj->blessed && (temp = o_material(obj, GOLD)) != 0) {
                     temp->ox = mtmp->mx;
                     temp->oy = mtmp->my;
                     map_object(temp, 1);
                     break;
-                } else if ((temp = o_in(obj, COIN_CLASS))) {
+                } else if ((temp = o_in(obj, COIN_CLASS)) != 0) {
                     temp->ox = mtmp->mx;
                     temp->oy = mtmp->my;
                     map_object(temp, 1);
                     break;
                 }
+        }
+        if (temp && temp->ox == u.ux && temp->oy == u.uy)
+            ugold = TRUE;
+    }
+    if (!ugold) {
+        newsym(u.ux, u.uy);
+        ter_typ |= TER_MON; /* so autodescribe will recognize hero */
     }
-    newsym(u.ux, u.uy);
-    u.uinwater = iflags.save_uinwater, u.uburied = iflags.save_uburied;
 /*JP
     You_feel("very greedy, and sense gold!");
 */
     You("\82Ç\82ñ\97~\82É\82È\82Á\82½\82æ\82¤\82È\8bC\82ª\82µ\82½\81C\82»\82µ\82Ä\8bà\89Ý\82Ì\88Ê\92u\82ð\8a´\92m\82µ\82½\81I");
     exercise(A_WIS, TRUE);
-    display_nhwindow(WIN_MAP, TRUE);
+
+    browse_map(ter_typ, "gold");
+
+    reconstrain_map();
     docrt();
     if (Underwater)
         under_water(2);
@@ -319,6 +479,8 @@ register struct obj *sobj;
     const char *what = confused ? "\83n\83\89\83w\83\8a" : "\90H\82×\95¨";
 
     stale = clear_stale_map(oclass, 0);
+    if (u.usteed) /* some situations leave steed with stale coordinates */
+        u.usteed->mx = u.ux, u.usteed->my = u.uy;
 
     for (obj = fobj; obj; obj = obj->nobj)
         if (o_in(obj, oclass)) {
@@ -327,12 +489,14 @@ register struct obj *sobj;
             else
                 ct++;
         }
-    for (mtmp = fmon; mtmp && !ct; mtmp = mtmp->nmon) {
-        /* no DEADMONSTER(mtmp) check needed since dmons never have inventory
-         */
+    for (mtmp = fmon; mtmp && (!ct || !ctu); mtmp = mtmp->nmon) {
+        /* no DEADMONSTER(mtmp) check needed -- dmons never have inventory */
         for (obj = mtmp->minvent; obj; obj = obj->nobj)
             if (o_in(obj, oclass)) {
-                ct++;
+                if (mtmp->mx == u.ux && mtmp->my == u.uy)
+                    ctu++; /* steed or an engulfer with inventory */
+                else
+                    ct++;
                 break;
             }
     }
@@ -355,7 +519,8 @@ register struct obj *sobj;
             }
         } else if (sobj) {
             char buf[BUFSZ];
-#if 0 /*JP*/
+
+#if 0 /*JP:T*/
             Sprintf(buf, "Your %s twitches%s.", body_part(NOSE),
                     (sobj->blessed && !u.uedibility)
                         ? " then starts to tingle"
@@ -393,10 +558,11 @@ register struct obj *sobj;
         }
     } else {
         struct obj *temp;
+        int ter_typ = TER_DETECT | TER_OBJ;
+
         known = TRUE;
         cls();
-        iflags.save_uinwater = u.uinwater, iflags.save_uburied = u.uburied;
-        u.uinwater = u.uburied = 0;
+        (void) unconstrain_map();
         for (obj = fobj; obj; obj = obj->nobj)
             if ((temp = o_in(obj, oclass)) != 0) {
                 if (temp != obj) {
@@ -406,8 +572,7 @@ register struct obj *sobj;
                 map_object(temp, 1);
             }
         for (mtmp = fmon; mtmp; mtmp = mtmp->nmon)
-            /* no DEADMONSTER(mtmp) check needed since dmons never have
-             * inventory */
+            /* no DEADMONSTER() check needed -- dmons never have inventory */
             for (obj = mtmp->minvent; obj; obj = obj->nobj)
                 if ((temp = o_in(obj, oclass)) != 0) {
                     temp->ox = mtmp->mx;
@@ -415,8 +580,10 @@ register struct obj *sobj;
                     map_object(temp, 1);
                     break; /* skip rest of this monster's inventory */
                 }
-        newsym(u.ux, u.uy);
-        u.uinwater = iflags.save_uinwater, u.uburied = iflags.save_uburied;
+        if (!ctu) {
+            newsym(u.ux, u.uy);
+            ter_typ |= TER_MON; /* for autodescribe of self */
+        }
         if (sobj) {
             if (sobj->blessed) {
 #if 0 /*JP*/
@@ -437,8 +604,11 @@ register struct obj *sobj;
             You("sense %s.", what);
 */
             You("%s\82ð\8a´\92m\82µ\82½\81D", what);
-        display_nhwindow(WIN_MAP, TRUE);
         exercise(A_WIS, TRUE);
+
+        browse_map(ter_typ, "food");
+
+        reconstrain_map();
         docrt();
         if (Underwater)
             under_water(2);
@@ -468,7 +638,7 @@ int class;            /* an object class, 0 for all */
     int ct = 0, ctu = 0;
     register struct obj *obj, *otmp = (struct obj *) 0;
     register struct monst *mtmp;
-    int sym, boulder = 0;
+    int sym, boulder = 0, ter_typ = TER_DETECT | TER_OBJ;
 
     if (class < 0 || class >= MAXOCLASSES) {
         impossible("object_detect:  illegal class %d", class);
@@ -524,6 +694,9 @@ int class;            /* an object class, 0 for all */
             do_dknown_of(obj);
     }
 
+    if (u.usteed)
+        u.usteed->mx = u.ux, u.usteed->my = u.uy;
+
     for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
         if (DEADMONSTER(mtmp))
             continue;
@@ -561,13 +734,12 @@ int class;            /* an object class, 0 for all */
 
     cls();
 
-    iflags.save_uinwater = u.uinwater, iflags.save_uburied = u.uburied;
-    u.uinwater = u.uburied = 0;
+    (void) unconstrain_map();
     /*
      *  Map all buried objects first.
      */
     for (obj = level.buriedobjlist; obj; obj = obj->nobj)
-        if (!class || (otmp = o_in(obj, class))) {
+        if (!class || (otmp = o_in(obj, class)) != 0) {
             if (class) {
                 if (otmp != obj) {
                     otmp->ox = obj->ox;
@@ -588,8 +760,8 @@ int class;            /* an object class, 0 for all */
     for (x = 1; x < COLNO; x++)
         for (y = 0; y < ROWNO; y++)
             for (obj = level.objects[x][y]; obj; obj = obj->nexthere)
-                if ((!class && !boulder) || (otmp = o_in(obj, class))
-                    || (otmp = o_in(obj, boulder))) {
+                if ((!class && !boulder) || (otmp = o_in(obj, class)) != 0
+                    || (otmp = o_in(obj, boulder)) != 0) {
                     if (class || boulder) {
                         if (otmp != obj) {
                             otmp->ox = obj->ox;
@@ -606,8 +778,8 @@ int class;            /* an object class, 0 for all */
         if (DEADMONSTER(mtmp))
             continue;
         for (obj = mtmp->minvent; obj; obj = obj->nobj)
-            if ((!class && !boulder) || (otmp = o_in(obj, class))
-                || (otmp = o_in(obj, boulder))) {
+            if ((!class && !boulder) || (otmp = o_in(obj, class)) != 0
+                || (otmp = o_in(obj, boulder)) != 0) {
                 if (!class && !boulder)
                     otmp = obj;
                 otmp->ox = mtmp->mx; /* at monster location */
@@ -620,8 +792,9 @@ int class;            /* an object class, 0 for all */
             && (!class || class == objects[mtmp->mappearance].oc_class)) {
             struct obj temp;
 
-            temp.oextra = (struct oextra *) 0;
+            temp = zeroobj;
             temp.otyp = mtmp->mappearance; /* needed for obj_to_glyph() */
+            temp.quan = 1L;
             temp.ox = mtmp->mx;
             temp.oy = mtmp->my;
             temp.corpsenm = PM_TENGU; /* if mimicing a corpse */
@@ -629,27 +802,31 @@ int class;            /* an object class, 0 for all */
         } else if (findgold(mtmp->minvent)
                    && (!class || class == COIN_CLASS)) {
             struct obj gold;
+
             gold = zeroobj; /* ensure oextra is cleared too */
             gold.otyp = GOLD_PIECE;
+            gold.quan = (long) rnd(10); /* usually more than 1 */
             gold.ox = mtmp->mx;
             gold.oy = mtmp->my;
             map_object(&gold, 1);
         }
     }
-
-    newsym(u.ux, u.uy);
-    u.uinwater = iflags.save_uinwater, u.uburied = iflags.save_uburied;
+    if (!glyph_is_object(glyph_at(u.ux, u.uy))) {
+        newsym(u.ux, u.uy);
+        ter_typ |= TER_MON;
+    }
 /*JP
     You("detect the %s of %s.", ct ? "presence" : "absence", stuff);
 */
     You("%s%s\81D", stuff, ct ? "\82ð\94­\8c©\82µ\82½" : "\82Í\89½\82à\82È\82¢\82±\82Æ\82ª\82í\82©\82Á\82½" );
-    display_nhwindow(WIN_MAP, TRUE);
-    /*
-     * What are we going to do when the hero does an object detect while blind
-     * and the detected object covers a known pool?
-     */
-    docrt(); /* this will correctly reset vision */
 
+    if (!ct)
+        display_nhwindow(WIN_MAP, TRUE);
+    else
+        browse_map(ter_typ, "object");
+
+    reconstrain_map();
+    docrt(); /* this will correctly reset vision */
     if (Underwater)
         under_water(2);
     if (u.uburied)
@@ -695,26 +872,19 @@ int mclass;                /* monster class, 0 for all */
                                       : "\82 \82È\82½\82Í\8b°\95|\82Å\82¼\82­\82Á\82Æ\82µ\82½\81D");
         return 1;
     } else {
-        boolean woken = FALSE;
+        boolean unconstrained, woken = FALSE;
+        unsigned swallowed = u.uswallow; /* before unconstrain_map() */
 
         cls();
+        unconstrained = unconstrain_map();
         for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
             if (DEADMONSTER(mtmp))
                 continue;
             if (!mclass || mtmp->data->mlet == mclass
                 || (mtmp->data == &mons[PM_LONG_WORM]
                     && mclass == S_WORM_TAIL))
-                if (mtmp->mx > 0) {
-                    if (mclass && def_monsyms[mclass].sym == ' ')
-                        show_glyph(mtmp->mx, mtmp->my,
-                                   detected_mon_to_glyph(mtmp));
-                    else
-                        show_glyph(mtmp->mx, mtmp->my,
-                                   mtmp->mtame ? pet_to_glyph(mtmp) : mon_to_glyph(mtmp));
-                    /* don't be stingy - display entire worm */
-                    if (mtmp->data == &mons[PM_LONG_WORM])
-                        detect_wsegs(mtmp, 0);
-                }
+                map_monst(mtmp, TRUE);
+
             if (otmp && otmp->cursed
                 && (mtmp->msleeping || !mtmp->mcanmove)) {
                 mtmp->msleeping = mtmp->mfrozen = 0;
@@ -722,7 +892,8 @@ int mclass;                /* monster class, 0 for all */
                 woken = TRUE;
             }
         }
-        display_self();
+        if (!swallowed)
+            display_self();
 /*JP
         You("sense the presence of monsters.");
 */
@@ -732,8 +903,20 @@ int mclass;                /* monster class, 0 for all */
             pline("Monsters sense the presence of you.");
 */
             pline("\89ö\95¨\82Í\82 \82È\82½\82Ì\91\8dÝ\82ð\9ak\82¬\82Â\82¯\82½\81D");
-        display_nhwindow(WIN_MAP, TRUE);
-        docrt();
+
+        if ((otmp && otmp->blessed) && !unconstrained) {
+            /* persistent detection--just show updated map */
+            display_nhwindow(WIN_MAP, TRUE);
+        } else {
+            /* one-shot detection--allow player to move cursor around and
+               get autodescribe feedback */
+            EDetect_monsters |= I_SPECIAL;
+            browse_map(TER_DETECT | TER_MON, "monster of interest");
+            EDetect_monsters &= ~I_SPECIAL;
+        }
+
+        reconstrain_map();
+        docrt(); /* redraw the screen to remove unseen monsters from map */
         if (Underwater)
             under_water(2);
         if (u.uburied)
@@ -751,7 +934,7 @@ int src_cursed;
     if (Hallucination || src_cursed) {
         struct obj obj; /* fake object */
 
-        obj.oextra = (struct oextra *) 0;
+        obj = zeroobj;
         if (trap) {
             obj.ox = trap->tx;
             obj.oy = trap->ty;
@@ -759,14 +942,18 @@ int src_cursed;
             obj.ox = x;
             obj.oy = y;
         }
-        obj.otyp = (src_cursed) ? GOLD_PIECE : random_object();
+        obj.otyp = !Hallucination ? GOLD_PIECE : random_object();
+        obj.quan = (long) ((obj.otyp == GOLD_PIECE) ? rnd(10)
+                           : objects[obj.otyp].oc_merge ? rnd(2) : 1);
         obj.corpsenm = random_monster(); /* if otyp == CORPSE */
         map_object(&obj, 1);
     } else if (trap) {
         map_trap(trap, 1);
         trap->tseen = 1;
-    } else {
+    } else { /* trapped door or trapped chest */
         struct trap temp_trap; /* fake trap */
+
+        (void) memset((genericptr_t) &temp_trap, 0, sizeof temp_trap);
         temp_trap.tx = x;
         temp_trap.ty = y;
         temp_trap.ttyp = BEAR_TRAP; /* some kind of trap */
@@ -791,6 +978,11 @@ int how; /* 1 for misleading map feedback */
     xchar x, y;
     int result = OTRAP_NONE;
 
+    /*
+     * TODO?  Display locations of unarmed land mine and beartrap objects.
+     * If so, should they be displayed as objects or as traps?
+     */
+
     for (otmp = objlist; otmp; otmp = otmp->nobj) {
         if (Is_box(otmp) && otmp->otrapped
             && get_obj_location(otmp, &x, &y, BURIED_TOO | CONTAINED_TOO)) {
@@ -811,16 +1003,18 @@ int how; /* 1 for misleading map feedback */
  */
 int
 trap_detect(sobj)
-register struct obj *sobj;
-/* sobj is null if crystal ball, *scroll if gold detection scroll */
+struct obj *sobj; /* null if crystal ball, *scroll if gold detection scroll */
 {
     register struct trap *ttmp;
     struct monst *mon;
-    int door, glyph, tr;
+    int door, glyph, tr, ter_typ = TER_DETECT | TER_TRP;
     int cursed_src = sobj && sobj->cursed;
     boolean found = FALSE;
     coord cc;
 
+    if (u.usteed)
+        u.usteed->mx = u.ux, u.usteed->my = u.uy;
+
     /* floor/ceiling traps */
     for (ttmp = ftrap; ttmp; ttmp = ttmp->ntrap) {
         if (ttmp->tx != u.ux || ttmp->ty != u.uy)
@@ -835,8 +1029,7 @@ register struct obj *sobj;
         else
             found = TRUE;
     }
-    if ((tr = detect_obj_traps(level.buriedobjlist, FALSE, 0))
-        != OTRAP_NONE) {
+    if ((tr = detect_obj_traps(level.buriedobjlist, FALSE, 0)) != OTRAP_NONE) {
         if (tr & OTRAP_THERE)
             goto outtrapmap;
         else
@@ -880,12 +1073,11 @@ register struct obj *sobj;
 */
     Your("%s\82Í\82Þ\82¸\82Þ\82¸\82µ\82½\81D", makeplural(body_part(TOE)));
     return 0;
+
 outtrapmap:
     cls();
 
-    iflags.save_uinwater = u.uinwater, iflags.save_uburied = u.uburied;
-    u.uinwater = u.uburied = 0;
-
+    (void) unconstrain_map();
     /* show chest traps first, so that subsequent floor trap display
        will override if both types are present at the same location */
     (void) detect_obj_traps(fobj, TRUE, cursed_src);
@@ -908,17 +1100,19 @@ outtrapmap:
 
     /* redisplay hero unless sense_trap() revealed something at <ux,uy> */
     glyph = glyph_at(u.ux, u.uy);
-    if (!(glyph_is_trap(glyph) || glyph_is_object(glyph)))
+    if (!(glyph_is_trap(glyph) || glyph_is_object(glyph))) {
         newsym(u.ux, u.uy);
-    u.uinwater = iflags.save_uinwater, u.uburied = iflags.save_uburied;
-
+        ter_typ |= TER_MON; /* for autodescribe at <u.ux,u.uy> */
+    }
 /*JP
     You_feel("%s.", cursed_src ? "very greedy" : "entrapped");
 */
     You("%s\8bC\8e\9d\82É\82È\82Á\82½\81D", cursed_src ? "\82Æ\82Ä\82à\82Ç\82ñ\97~\82È" : "\82¾\82Ü\82³\82ê\82Ä\82¢\82é\82æ\82¤\82È");
-    /* wait for user to respond, then reset map display to normal */
-    display_nhwindow(WIN_MAP, TRUE);
-    docrt();
+
+    browse_map(ter_typ, "trap of interest");
+
+    reconstrain_map();
+    docrt(); /* redraw the screen to remove unseen traps from the map */
     if (Underwater)
         under_water(2);
     if (u.uburied)
@@ -1194,13 +1388,13 @@ struct obj **optr;
 */
     multi_reason = "\90\85\8f»\8b\85\82ð\94`\82«\8d\9e\82ñ\82Å\82¢\82é\8e\9e\82É";
     nomovemsg = "";
-    if (obj->spe <= 0)
+    if (obj->spe <= 0) {
 /*JP
         pline_The("vision is unclear.");
 */
         pline("\89f\91\9c\82Í\95s\91N\96¾\82¾\82Á\82½\81D");
-    else {
-        int class;
+    else {
+        int class, i;
         int ret = 0;
 
         makeknown(CRYSTAL_BALL);
@@ -1223,16 +1417,15 @@ struct obj **optr;
             case '^':
                 ret = trap_detect((struct obj *) 0);
                 break;
-            default: {
-                int i = rn2(SIZE(level_detects));
-#if 0 /*JP*/
+            default:
+                i = rn2(SIZE(level_detects));
+#if 0 /*JP:T*/
                 You_see("%s, %s.", level_detects[i].what,
                         level_distance(level_detects[i].where));
 #else
                 You_see("%s\82ð%s\8c©\82½\81D", level_detects[i].what,
                         level_distance(level_detects[i].where));
 #endif
-            }
                 ret = 0;
                 break;
             }
@@ -1302,39 +1495,71 @@ void
 do_mapping()
 {
     register int zx, zy;
+    boolean unconstrained;
 
-    iflags.save_uinwater = u.uinwater, iflags.save_uburied = u.uburied;
-    u.uinwater = u.uburied = 0;
+    unconstrained = unconstrain_map();
     for (zx = 1; zx < COLNO; zx++)
         for (zy = 0; zy < ROWNO; zy++)
             show_map_spot(zx, zy);
-    u.uinwater = iflags.save_uinwater, u.uburied = iflags.save_uburied;
-    if (!level.flags.hero_memory || Underwater) {
+
+    if (!level.flags.hero_memory || unconstrained) {
         flush_screen(1);                 /* flush temp screen */
-        display_nhwindow(WIN_MAP, TRUE); /* wait */
+        /* browse_map() instead of display_nhwindow(WIN_MAP, TRUE) */
+        browse_map(TER_DETECT | TER_MAP | TER_TRP | TER_OBJ,
+                   "anything of interest");
         docrt();
     }
+    reconstrain_map();
     exercise(A_WIS, TRUE);
 }
 
+/* clairvoyance */
 void
-do_vicinity_map()
+do_vicinity_map(sobj)
+struct obj *sobj; /* scroll--actually fake spellbook--object */
 {
     register int zx, zy;
-    int lo_y = (u.uy - 5 < 0 ? 0 : u.uy - 5),
-        hi_y = (u.uy + 6 > ROWNO ? ROWNO : u.uy + 6),
-        lo_x = (u.ux - 9 < 1 ? 1 : u.ux - 9), /* avoid column 0 */
-        hi_x = (u.ux + 10 > COLNO ? COLNO : u.ux + 10);
-
-    for (zx = lo_x; zx < hi_x; zx++)
-        for (zy = lo_y; zy < hi_y; zy++)
+    struct monst *mtmp;
+    boolean unconstrained, refresh = FALSE, mdetected = FALSE,
+            extended = (sobj && sobj->blessed);
+    int lo_y = ((u.uy - 5 < 0) ? 0 : u.uy - 5),
+        hi_y = ((u.uy + 6 >= ROWNO) ? ROWNO - 1 : u.uy + 6),
+        lo_x = ((u.ux - 9 < 1) ? 1 : u.ux - 9), /* avoid column 0 */
+        hi_x = ((u.ux + 10 >= COLNO) ? COLNO - 1 : u.ux + 10),
+        ter_typ = TER_DETECT | TER_MAP | TER_TRP | TER_OBJ;
+
+    unconstrained = unconstrain_map();
+    for (zx = lo_x; zx <= hi_x; zx++)
+        for (zy = lo_y; zy <= hi_y; zy++) {
             show_map_spot(zx, zy);
 
-    if (!level.flags.hero_memory || Underwater) {
+            if (extended && (mtmp = m_at(zx, zy)) != 0
+                && mtmp->mx == zx && mtmp->my == zy) { /* skip worm tails */
+                int oldglyph = glyph_at(zx, zy);
+
+                map_monst(mtmp, FALSE);
+                if (glyph_at(zx, zy) != oldglyph)
+                    mdetected = TRUE;
+            }
+        }
+
+    if (!level.flags.hero_memory || unconstrained || mdetected) {
         flush_screen(1);                 /* flush temp screen */
-        display_nhwindow(WIN_MAP, TRUE); /* wait */
-        docrt();
+        /* the getpos() prompt from browse_map() is only shown when
+           flags.verbose is set, but make this unconditional so that
+           not-verbose users become aware of the prompting situation */
+        You("sense your surroundings.");
+        if (extended || glyph_is_monster(glyph_at(u.ux, u.uy)))
+            ter_typ |= TER_MON;
+        if (extended)
+            EDetect_monsters |= I_SPECIAL;
+        browse_map(ter_typ, "anything of interest");
+        EDetect_monsters &= ~I_SPECIAL;
+        refresh = TRUE;
     }
+    reconstrain_map();
+    if (refresh)
+        docrt();
 }
 
 /* convert a secret door into a normal door */
@@ -1394,9 +1619,7 @@ genericptr_t num;
         }
         if (!canspotmon(mtmp) && !glyph_is_invisible(levl[zx][zy].glyph))
             map_invisible(zx, zy);
-    } else if (glyph_is_invisible(levl[zx][zy].glyph)) {
-        unmap_object(zx, zy);
-        newsym(zx, zy);
+    } else if (unmap_invisible(zx, zy)) {
         (*(int *) num)++;
     }
 }
@@ -1549,15 +1772,70 @@ struct trap *trap;
     }
 }
 
+STATIC_OVL int
+mfind0(mtmp, via_warning)
+struct monst *mtmp;
+boolean via_warning;
+{
+    xchar x = mtmp->mx,
+          y = mtmp->my;
+
+    if (via_warning && !warning_of(mtmp))
+        return -1;
+
+    if (mtmp->m_ap_type) {
+        seemimic(mtmp);
+    find:
+        exercise(A_WIS, TRUE);
+        if (!canspotmon(mtmp)) {
+            if (glyph_is_invisible(levl[x][y].glyph)) {
+                /* Found invisible monster in a square which already has
+                 * an 'I' in it.  Logically, this should still take time
+                 * and lead to a return 1, but if we did that the player
+                 * would keep finding the same monster every turn.
+                 */
+                return -1;
+            } else {
+/*JP
+                                    You_feel("an unseen monster!");
+*/
+                                    You("\8c©\82¦\82È\82¢\89ö\95¨\82Ì\8bC\94z\82ð\8a´\82\82½\81I");
+                map_invisible(x, y);
+            }
+        } else if (!sensemon(mtmp))
+#if 0 /*JP:T*/
+                You("find %s.",
+                    mtmp->mtame ? y_monnam(mtmp) : a_monnam(mtmp));
+#else
+                You("%s\82ð\8c©\82Â\82¯\82½\81D",
+                    mtmp->mtame ? y_monnam(mtmp) : a_monnam(mtmp));
+#endif
+        return 1;
+    }
+    if (!canspotmon(mtmp)) {
+        if (mtmp->mundetected
+            && (is_hider(mtmp->data) || mtmp->data->mlet == S_EEL))
+            if (via_warning) {
+                Your("warning senses cause you to take a second %s.",
+                     Blind ? "to check nearby" : "look close by");
+                display_nhwindow(WIN_MESSAGE, FALSE); /* flush messages */
+            }
+        mtmp->mundetected = 0;
+        newsym(x, y);
+        goto find;
+    }
+    return 0;
+}
+
 int
 dosearch0(aflag)
 register int aflag; /* intrinsic autosearch vs explicit searching */
 {
 #ifdef GCC_BUG
-    /* some versions of gcc seriously muck up nested loops. if you get strange
-       crashes while searching in a version compiled with gcc, try putting
-       #define GCC_BUG in *conf.h (or adding -DGCC_BUG to CFLAGS in the
-       makefile).
+    /* Some old versions of gcc seriously muck up nested loops.  If you get
+     * strange crashes while searching in a version compiled with gcc, try
+     * putting #define GCC_BUG in *conf.h (or adding -DGCC_BUG to CFLAGS in
+     * the makefile).
      */
     volatile xchar x, y;
 #else
@@ -1607,7 +1885,7 @@ register int aflag; /* intrinsic autosearch vs explicit searching */
                     unblock_point(x, y); /* vision */
                     exercise(A_WIS, TRUE);
                     nomul(0);
-                    feel_location(x, y); /* make sure it shows up */
+                    feel_newsym(x, y); /* make sure it shows up */
 /*JP
                     You("find a hidden passage.");
 */
@@ -1615,54 +1893,19 @@ register int aflag; /* intrinsic autosearch vs explicit searching */
                 } else {
                     /* Be careful not to find anything in an SCORR or SDOOR */
                     if ((mtmp = m_at(x, y)) != 0 && !aflag) {
-                        if (mtmp->m_ap_type) {
-                            seemimic(mtmp);
-                        find:
-                            exercise(A_WIS, TRUE);
-                            if (!canspotmon(mtmp)) {
-                                if (glyph_is_invisible(levl[x][y].glyph)) {
-                                    /* found invisible monster in a square
-                                     * which already has an 'I' in it.
-                                     * Logically, this should still take
-                                     * time and lead to a return(1), but
-                                     * if we did that the player would keep
-                                     * finding the same monster every turn.
-                                     */
-                                    continue;
-                                } else {
-/*JP
-                                    You_feel("an unseen monster!");
-*/
-                                    You("\8c©\82¦\82È\82¢\89ö\95¨\82Ì\8bC\94z\82ð\8a´\82\82½\81I");
-                                    map_invisible(x, y);
-                                }
-                            } else if (!sensemon(mtmp))
-/*JP
-                                You("find %s.", mtmp->mtame
-*/
-                                You("%s\82ð\8c©\82Â\82¯\82½\81D", mtmp->mtame
-                                                   ? y_monnam(mtmp)
-                                                   : a_monnam(mtmp));
-                            return 1;
-                        }
-                        if (!canspotmon(mtmp)) {
-                            if (mtmp->mundetected
-                                && (is_hider(mtmp->data)
-                                    || mtmp->data->mlet == S_EEL))
-                                mtmp->mundetected = 0;
-                            newsym(x, y);
-                            goto find;
-                        }
+                        int mfres = mfind0(mtmp, 0);
+
+                        if (mfres == -1)
+                            continue;
+                        else if (mfres > 0)
+                            return mfres;
                     }
 
                     /* see if an invisible monster has moved--if Blind,
                      * feel_location() already did it
                      */
-                    if (!aflag && !mtmp && !Blind
-                        && glyph_is_invisible(levl[x][y].glyph)) {
-                        unmap_object(x, y);
-                        newsym(x, y);
-                    }
+                    if (!aflag && !mtmp && !Blind)
+                        (void) unmap_invisible(x, y);
 
                     if ((trap = t_at(x, y)) && !trap->tseen && !rnl(8)) {
                         nomul(0);
@@ -1687,6 +1930,22 @@ dosearch()
     return dosearch0(0);
 }
 
+void
+warnreveal()
+{
+    int x, y;
+    struct monst *mtmp;
+
+    for (x = u.ux - 1; x <= u.ux + 1; x++)
+        for (y = u.uy - 1; y <= u.uy + 1; y++) {
+            if (!isok(x, y) || (x == u.ux && y == u.uy))
+                continue;
+            if ((mtmp = m_at(x, y)) != 0
+                && warning_of(mtmp) && mtmp->mundetected)
+                (void) mfind0(mtmp, 1); /* via_warning */
+        }
+}
+
 /* Pre-map the sokoban levels */
 void
 sokoban_detect()
@@ -1715,6 +1974,141 @@ sokoban_detect()
     }
 }
 
+STATIC_DCL int
+reveal_terrain_getglyph(x, y, full, swallowed, default_glyph, which_subset)
+int x, y, full;
+unsigned swallowed;
+int default_glyph, which_subset;
+{
+    int glyph, levl_glyph;
+    uchar seenv;
+    boolean keep_traps = (which_subset & TER_TRP) !=0,
+            keep_objs = (which_subset & TER_OBJ) != 0,
+            keep_mons = (which_subset & TER_MON) != 0;
+    struct monst *mtmp;
+    struct trap *t;
+
+    /* for 'full', show the actual terrain for the entire level,
+       otherwise what the hero remembers for seen locations with
+       monsters, objects, and/or traps removed as caller dictates */
+    seenv = (full || level.flags.hero_memory)
+              ? levl[x][y].seenv : cansee(x, y) ? SVALL : 0;
+    if (full) {
+        levl[x][y].seenv = SVALL;
+        glyph = back_to_glyph(x, y);
+        levl[x][y].seenv = seenv;
+    } else {
+        levl_glyph = level.flags.hero_memory
+              ? levl[x][y].glyph
+              : seenv ? back_to_glyph(x, y): default_glyph;
+        /* glyph_at() returns the displayed glyph, which might
+           be a monster.  levl[][].glyph contains the remembered
+           glyph, which will never be a monster (unless it is
+           the invisible monster glyph, which is handled like
+           an object, replacing any object or trap at its spot) */
+        glyph = !swallowed ? glyph_at(x, y) : levl_glyph;
+        if (keep_mons && x == u.ux && y == u.uy && swallowed)
+            glyph = mon_to_glyph(u.ustuck);
+        else if (((glyph_is_monster(glyph)
+                   || glyph_is_warning(glyph)) && !keep_mons)
+                 || glyph_is_swallow(glyph))
+            glyph = levl_glyph;
+        if (((glyph_is_object(glyph) && !keep_objs)
+             || glyph_is_invisible(glyph))
+            && keep_traps && !covers_traps(x, y)) {
+            if ((t = t_at(x, y)) != 0 && t->tseen)
+                glyph = trap_to_glyph(t);
+        }
+        if ((glyph_is_object(glyph) && !keep_objs)
+            || (glyph_is_trap(glyph) && !keep_traps)
+            || glyph_is_invisible(glyph)) {
+            if (!seenv) {
+                glyph = default_glyph;
+            } else if (lastseentyp[x][y] == levl[x][y].typ) {
+                glyph = back_to_glyph(x, y);
+            } else {
+                /* look for a mimic here posing as furniture;
+                   if we don't find one, we'll have to fake it */
+                if ((mtmp = m_at(x, y)) != 0
+                    && mtmp->m_ap_type == M_AP_FURNITURE) {
+                    glyph = cmap_to_glyph(mtmp->mappearance);
+                } else {
+                    /* we have a topology type but we want a screen
+                       symbol in order to derive a glyph; some screen
+                       symbols need the flags field of levl[][] in
+                       addition to the type (to disambiguate STAIRS to
+                       S_upstair or S_dnstair, for example; current
+                       flags might not be intended for remembered type,
+                       but we've got no other choice) */
+                    schar save_typ = levl[x][y].typ;
+
+                    levl[x][y].typ = lastseentyp[x][y];
+                    glyph = back_to_glyph(x, y);
+                    levl[x][y].typ = save_typ;
+                }
+            }
+        }
+    }
+    if (glyph == cmap_to_glyph(S_darkroom))
+        glyph = cmap_to_glyph(S_room); /* FIXME: dirty hack */
+    return glyph;
+}
+
+#ifdef DUMPLOG
+void
+dump_map()
+{
+    int x, y, glyph, skippedrows, lastnonblank;
+    int subset = TER_MAP | TER_TRP | TER_OBJ | TER_MON;
+    int default_glyph = cmap_to_glyph(level.flags.arboreal ? S_tree : S_stone);
+    char buf[BUFSZ];
+    boolean blankrow, toprow;
+
+    /*
+     * Squeeze out excess vertial space when dumping the map.
+     * If there are any blank map rows at the top, suppress them
+     * (our caller has already printed a separator).  If there is
+     * more than one blank map row at the bottom, keep just one.
+     * Any blank rows within the middle of the map are kept.
+     * Note: putstr() with winid==0 is for dumplog.
+     */
+    skippedrows = 0;
+    toprow = TRUE;
+    for (y = 0; y < ROWNO; y++) {
+        blankrow = TRUE; /* assume blank until we discover otherwise */
+        lastnonblank = -1; /* buf[] index rather than map's x */
+        for (x = 1; x < COLNO; x++) {
+            int ch, color;
+            unsigned special;
+
+            glyph = reveal_terrain_getglyph(x, y, FALSE, u.uswallow,
+                                            default_glyph, subset);
+            (void) mapglyph(glyph, &ch, &color, &special, x, y);
+            buf[x - 1] = ch;
+            if (ch != ' ') {
+                blankrow = FALSE;
+                lastnonblank = x - 1;
+            }
+        }
+        if (!blankrow) {
+            buf[lastnonblank + 1] = '\0';
+            if (toprow) {
+                skippedrows = 0;
+                toprow = FALSE;
+            }
+            for (x = 0; x < skippedrows; x++)
+                putstr(0, 0, "");
+            putstr(0, 0, buf); /* map row #y */
+            skippedrows = 0;
+        } else {
+            ++skippedrows;
+        }
+    }
+    if (skippedrows)
+        putstr(0, 0, "");
+}
+#endif /* DUMPLOG */
+
 /* idea from crawl; show known portion of map without any monsters,
    objects, or traps occluding the view of the underlying terrain */
 void
@@ -1728,94 +2122,27 @@ int which_subset; /* when not full, whether to suppress objs and/or traps */
 */
         You("\8d¬\97\90\82µ\82Ä\82¢\82é\82Ì\82Å\82»\82ê\82Í\82Å\82«\82È\82¢\81D");
     } else {
-        int x, y, glyph, levl_glyph, default_glyph;
-        uchar seenv;
-        unsigned save_swallowed;
-        struct monst *mtmp;
-        struct trap *t;
+        int x, y, glyph, default_glyph;
         char buf[BUFSZ];
-        boolean keep_traps = (which_subset & 1) !=0,
-                keep_objs = (which_subset & 2) != 0,
-                keep_mons = (which_subset & 4) != 0; /* actually always 0 */
-
-        save_swallowed = u.uswallow;
-        iflags.save_uinwater = u.uinwater, iflags.save_uburied = u.uburied;
-        u.uinwater = u.uburied = 0;
-        u.uswallow = 0;
+        /* there is a TER_MAP bit too; we always show map regardless of it */
+        boolean keep_traps = (which_subset & TER_TRP) !=0,
+                keep_objs = (which_subset & TER_OBJ) != 0,
+                keep_mons = (which_subset & TER_MON) != 0; /* not used */
+        unsigned swallowed = u.uswallow; /* before unconstrain_map() */
+
+        if (unconstrain_map())
+            docrt();
         default_glyph = cmap_to_glyph(level.flags.arboreal ? S_tree : S_stone);
-        /* for 'full', show the actual terrain for the entire level,
-           otherwise what the hero remembers for seen locations with
-           monsters, objects, and/or traps removed as caller dictates */
+
         for (x = 1; x < COLNO; x++)
             for (y = 0; y < ROWNO; y++) {
-                seenv = (full || level.flags.hero_memory)
-                           ? levl[x][y].seenv : cansee(x, y) ? SVALL : 0;
-                if (full) {
-                    levl[x][y].seenv = SVALL;
-                    glyph = back_to_glyph(x, y);
-                    levl[x][y].seenv = seenv;
-                } else {
-                    levl_glyph = level.flags.hero_memory
-                                    ? levl[x][y].glyph
-                                    : seenv
-                                       ? back_to_glyph(x, y)
-                                       : default_glyph;
-                    /* glyph_at() returns the displayed glyph, which might
-                       be a monster.  levl[][].glyph contains the remembered
-                       glyph, which will never be a monster (unless it is
-                       the invisible monster glyph, which is handled like
-                       an object, replacing any object or trap at its spot) */
-                    glyph = !save_swallowed ? glyph_at(x, y) : levl_glyph;
-                    if (keep_mons && x == u.ux && y == u.uy && save_swallowed)
-                        glyph = mon_to_glyph(u.ustuck);
-                    else if (((glyph_is_monster(glyph)
-                               || glyph_is_warning(glyph)) && !keep_mons)
-                             || glyph_is_swallow(glyph))
-                        glyph = levl_glyph;
-                    if (((glyph_is_object(glyph) && !keep_objs)
-                         || glyph_is_invisible(glyph))
-                        && keep_traps && !covers_traps(x, y)) {
-                        if ((t = t_at(x, y)) != 0 && t->tseen)
-                            glyph = trap_to_glyph(t);
-                    }
-                    if ((glyph_is_object(glyph) && !keep_objs)
-                        || (glyph_is_trap(glyph) && !keep_traps)
-                        || glyph_is_invisible(glyph)) {
-                        if (!seenv) {
-                            glyph = default_glyph;
-                        } else if (lastseentyp[x][y] == levl[x][y].typ) {
-                            glyph = back_to_glyph(x, y);
-                        } else {
-                            /* look for a mimic here posing as furniture;
-                               if we don't find one, we'll have to fake it */
-                            if ((mtmp = m_at(x, y)) != 0
-                                && mtmp->m_ap_type == M_AP_FURNITURE) {
-                                glyph = cmap_to_glyph(mtmp->mappearance);
-                            } else {
-                                /* we have a topology type but we want a
-                                   screen symbol in order to derive a glyph;
-                                   some screen symbols need the flags field
-                                   of levl[][] in addition to the type
-                                   (to disambiguate STAIRS to S_upstair or
-                                   S_dnstair, for example; current flags
-                                   might not be intended for remembered
-                                   type, but we've got no other choice) */
-                                schar save_typ = levl[x][y].typ;
-
-                                levl[x][y].typ = lastseentyp[x][y];
-                                glyph = back_to_glyph(x, y);
-                                levl[x][y].typ = save_typ;
-                            }
-                        }
-                    }
-                }
+                glyph = reveal_terrain_getglyph(x,y, full, swallowed,
+                                                default_glyph, which_subset);
                 show_glyph(x, y, glyph);
             }
 
-        /* [TODO: highlight hero's location somehow] */
-        u.uinwater = iflags.save_uinwater, u.uburied = iflags.save_uburied;
-        if (save_swallowed)
-            u.uswallow = 1;
+        /* hero's location is not highlighted, but getpos() starts with
+           cursor there, and after moving it anywhere '@' moves it back */
         flush_screen(1);
         if (full) {
 /*JP
@@ -1854,7 +2181,13 @@ int which_subset; /* when not full, whether to suppress objs and/or traps */
         pline("Showing %s only...", buf);
 */
         pline("%s\82¾\82¯\82ð\8c©\82é\81D\81D\81D", buf);
-        display_nhwindow(WIN_MAP, TRUE); /* give "--More--" prompt */
+
+        /* allow player to move cursor around and get autodescribe feedback
+           based on what is visible now rather than what is on 'real' map */
+        which_subset |= TER_MAP; /* guarantee non-zero */
+        browse_map(which_subset, "anything of interest");
+
+        reconstrain_map();
         docrt(); /* redraw the screen, restoring regular map */
         if (Underwater)
             under_water(2);