]> granicus.if.org Git - nethack/commitdiff
detection revamp
authorPatR <rankin@nethack.org>
Thu, 2 Jun 2016 08:08:34 +0000 (01:08 -0700)
committerPatR <rankin@nethack.org>
Thu, 2 Jun 2016 08:08:34 +0000 (01:08 -0700)
Change most instances of detection to offer the player a chance to
move the cursor around on the map so that the getpos() autodescribe
feature can explain things that might go away as soon as the
current detection completes.  The few instances that don't offer
such a chance are the ones where everything which has been revealed
will still be there once the action finishes (such as regular magic
mapping and blessed/persistent monster detection).

There were quite a lot of inconsistencies in things like handling
for detection while swallowed or underwater.  I didn't keep track
of them to distinguish between 3.6.0, current dev code, or my patch
in progess.  They should be much more consistent now but without a
comprehensive fixes36.1 entry.

Blessed clairvoyance (divination spells at skilled or expert) now
shows monsters as well as terrain.  I first had it like that for
any clairvoyance, but having getpos/autodescribe kick in every 15
or 30 turns once you have the amulet--or pay the appropriate amount
to a temple preist--was nearly unplayable.  When it only follows an
explicit spell cast it is not intrusive.

doc/fixes36.1
include/extern.h
include/flag.h
src/allmain.c
src/detect.c
src/pager.c
src/spell.c

index 8cfd5880ae303cf4250a14aea3f752172379c19b..2e58f8f23bcc1a48746ddc7829262150d82145d3 100644 (file)
@@ -280,6 +280,8 @@ thrown potion that killed peaceful monster would cause "the <mon> gets argry"
        after the message about it being killed
 when poly'd into a hider and engulfed, attempt to hide via #monster was blocked
        but feedback said "can't hide while held" rather than "while engulfed"
+various monster/object/food/gold/trap detections were inconsistent in how they
+       behaved if performed while engulfed or underwater
 
 
 Fixes to Post-3.6.0 Problems that Were Exposed Via git Repository
@@ -395,9 +397,12 @@ during end of game disclosure, the vanquished monsters list can be sorted in
        one of several ways by answering 'a' to "disclose vanquished monsters?"
 when #terrain is displaying a censored version of the map (no monsters, &c),
        moving the cursor will display farlook's brief autodescribe feedback
+when monster/object/trap detection temporarily clears the map to highlight the
+       results of such detection, farlook autodescibe can be used to view it
 interrupt a multi turn action if hp or pw is restored to maximum
 pressing d or D when cursor positioning targets doors and doorways
 pressing x or X when cursor positioning targets possibly unexplored location
+       (potentially useful when using '_' [not mouse] to invoke travel)
 
 
 Platform- and/or Interface-Specific New Features
@@ -420,10 +425,10 @@ X11: status display split into three columns to accomodate Stone/Deaf/Lev/&c;
 NetHack Community Patches (or Variation) Included
 -------------------------------------------------
 Malcolm Ryan's improved tin opener
-Ray Chason's keyboard may stop responding after locking or unlocking a door when
-       using altkeyhandler=nhraykey.dll 
-Ray Chason's fix: window interfaces that support transparency may give away unseen
-       parts of the map
+Ray Chason's keyboard may stop responding after locking or unlocking a door
+       when using altkeyhandler=nhraykey.dll
+Ray Chason's fix: window interfaces that support transparency may give away
+       unseen parts of the map
 Ray Chason's xprname should honor iflags.menu_tab_sep
 Ray Chason's punctuation for "That foo is really a mimic."
 Ray Chason's proper background tiles for lava and water
index 4c5cb8e6485cbff3d821a8e4325f4d42f5bc8f11..4b58776f5747caaf302f6126396a03321d0eaf64 100644 (file)
@@ -248,7 +248,7 @@ E int FDECL(trap_detect, (struct obj *));
 E const char *FDECL(level_distance, (d_level *));
 E void FDECL(use_crystal_ball, (struct obj **));
 E void NDECL(do_mapping);
-E void NDECL(do_vicinity_map);
+E void FDECL(do_vicinity_map, (struct obj *));
 E void FDECL(cvt_sdoor_to_door, (struct rm *));
 #ifdef USE_TRAMPOLI
 E void FDECL(findone, (int, int, genericptr_t));
index 79bedc9b0fc5c05ca5055ade251b46503d207517..a8a6d85939ba566a48987679f56243121ef413ac 100644 (file)
@@ -187,10 +187,11 @@ struct instance_flags {
     int override_ID;       /* true to force full identification of objects */
     int suppress_price;    /* controls doname() for unpaid objects */
     int terrainmode; /* for getpos()'s autodescribe when #terrain is active */
-#define TER_MAP 0x01
-#define TER_TRP 0x02
-#define TER_OBJ 0x04
-#define TER_MON 0x08
+#define TER_MAP    0x01
+#define TER_TRP    0x02
+#define TER_OBJ    0x04
+#define TER_MON    0x08
+#define TER_DETECT 0x10    /* detect_foo magic rather than #terrain */
     coord travelcc;        /* coordinates for travel_cache */
     boolean window_inited; /* true if init_nhwindows() completed */
     boolean vision_inited; /* true if vision is ready */
index 33afd9927b88e1023fe7b69bc26ba957cb122eb8..3aecf399adafa5b8503ebe6fc72b99deacf9d7d4 100644 (file)
@@ -370,7 +370,7 @@ boolean resuming;
                 clear_bypasses();
             if ((u.uhave.amulet || Clairvoyant) && !In_endgame(&u.uz)
                 && !BClairvoyant && !(moves % 15) && !rn2(2))
-                do_vicinity_map();
+                do_vicinity_map((struct obj *) 0);
             if (u.utrap && u.utraptype == TT_LAVA)
                 sink_into_lava();
             /* when/if hero escapes from lava, he can't just stay there */
index 07b93227f4346faf6b988aed34a82172d0a9d982..7668d77bb82eae5600e071f09343dcdb057f967b 100644 (file)
 
 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));
@@ -22,6 +26,62 @@ 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));
 
+/* 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 */
+
+    dummy_pos.x = u.ux, dummy_pos.y = u.uy; /* starting spot for getpos() */
+    iflags.autodescribe = TRUE;
+    iflags.terrainmode = ter_typ;
+    getpos(&dummy_pos, FALSE, ter_explain);
+    iflags.terrainmode = 0;
+    /* leave iflags.autodescribe 'on' even if previously 'off' */
+}
+
+/* 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
@@ -29,12 +89,42 @@ 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;
-    /* presence of any trappable container will do */
-    return (sobj_at(CHEST, x, y) || sobj_at(LARGE_BOX, x, y)) ? TRUE : 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,
@@ -202,27 +292,34 @@ 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 */
+            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 */
+                    }
                 }
         }
     }
@@ -246,13 +343,21 @@ register struct obj *sobj;
         if (sobj) {
             char buf[BUFSZ];
 
-            if (youmonst.data == &mons[PM_GOLD_GOLEM]) {
+            if (youmonst.data == &mons[PM_GOLD_GOLEM])
                 Sprintf(buf, "You feel like a million %s!", currency(2L));
-            } else if (hidden_gold() || money_cnt(invent))
+            else if (money_cnt(invent) || hidden_gold())
                 Strcpy(buf,
                    "You feel worried about your future financial situation.");
+            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
                 Strcpy(buf, "You feel materially poor.");
+
             strange_feeling(sobj, buf);
         }
         return 1;
@@ -266,8 +371,7 @@ 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)) != 0) {
@@ -283,19 +387,21 @@ outgoldmap:
             }
             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);
+            temp = &gold;
         } else {
             for (obj = mtmp->minvent; obj; obj = obj->nobj)
                 if (sobj->blessed && (temp = o_material(obj, GOLD)) != 0) {
@@ -310,13 +416,19 @@ outgoldmap:
                     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;
-    iflags.save_uinwater = iflags.save_uburied = 0;
     You_feel("very greedy, and sense gold!");
     exercise(A_WIS, TRUE);
-    display_nhwindow(WIN_MAP, TRUE);
+
+    browse_map(ter_typ, "gold");
+
+    reconstrain_map();
     docrt();
     if (Underwater)
         under_water(2);
@@ -339,6 +451,8 @@ register struct obj *sobj;
     const char *what = confused ? something : "food";
 
     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)) {
@@ -347,12 +461,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;
             }
     }
@@ -369,6 +485,7 @@ register struct obj *sobj;
             }
         } else if (sobj) {
             char buf[BUFSZ];
+
             Sprintf(buf, "Your %s twitches%s.", body_part(NOSE),
                     (sobj->blessed && !u.uedibility)
                         ? " then starts to tingle"
@@ -394,10 +511,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) {
@@ -407,8 +525,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;
@@ -416,9 +533,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;
-        iflags.save_uinwater = iflags.save_uburied = 0;
+        if (!ctu) {
+            newsym(u.ux, u.uy);
+            ter_typ |= TER_MON; /* for autodescribe of self */
+        }
         if (sobj) {
             if (sobj->blessed) {
                 Your("%s %s to tingle and you smell %s.", body_part(NOSE),
@@ -428,8 +546,11 @@ register struct obj *sobj;
                 Your("%s tingles and you smell %s.", body_part(NOSE), what);
         } else
             You("sense %s.", what);
-        display_nhwindow(WIN_MAP, TRUE);
         exercise(A_WIS, TRUE);
+
+        browse_map(ter_typ, "food");
+
+        reconstrain_map();
         docrt();
         if (Underwater)
             under_water(2);
@@ -459,7 +580,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);
@@ -509,6 +630,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;
@@ -540,8 +664,7 @@ 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.
      */
@@ -618,16 +741,18 @@ int class;            /* an object class, 0 for all */
             map_object(&gold, 1);
         }
     }
-
-    newsym(u.ux, u.uy);
-    u.uinwater = iflags.save_uinwater, u.uburied = iflags.save_uburied;
-    iflags.save_uinwater = iflags.save_uburied = 0;
+    if (!glyph_is_object(glyph_at(u.ux, u.uy))) {
+        newsym(u.ux, u.uy);
+        ter_typ |= TER_MON;
+    }
     You("detect the %s of %s.", ct ? "presence" : "absence", stuff);
-    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?
-     */
+
+    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);
@@ -668,26 +793,19 @@ int mclass;                /* monster class, 0 for all */
                                       : "You feel threatened.");
         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;
@@ -695,12 +813,25 @@ int mclass;                /* monster class, 0 for all */
                 woken = TRUE;
             }
         }
-        display_self();
+        if (!swallowed)
+            display_self();
         You("sense the presence of monsters.");
         if (woken)
             pline("Monsters sense the presence of you.");
-        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)
@@ -734,7 +865,7 @@ int src_cursed;
     } 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);
@@ -762,6 +893,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)) {
@@ -786,11 +922,14 @@ 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)
@@ -847,9 +986,7 @@ struct obj *sobj; /* null if crystal ball, *scroll if gold detection scroll */
 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);
@@ -872,15 +1009,16 @@ 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;
-    iflags.save_uinwater = iflags.save_uburied = 0;
-
+        ter_typ |= TER_MON; /* for autodescribe at <u.ux,u.uy> */
+    }
     You_feel("%s.", cursed_src ? "very greedy" : "entrapped");
-    /* 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)
@@ -1132,40 +1270,67 @@ 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;
-    iflags.save_uinwater = iflags.save_uburied = 0;
-    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();
+        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 */
@@ -1379,12 +1544,10 @@ boolean via_warning;
         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.
+                /* 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 {
@@ -1392,18 +1555,16 @@ boolean via_warning;
                 map_invisible(x, y);
             }
         } else if (!sensemon(mtmp))
-                You("find %s.", mtmp->mtame
-                                ? y_monnam(mtmp)
-                                : a_monnam(mtmp));
+                You("find %s.",
+                    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))
+            && (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");
+                     Blind ? "to check nearby" : "look close by");
                 display_nhwindow(WIN_MESSAGE, FALSE); /* flush messages */
             }
             mtmp->mundetected = 0;
@@ -1418,10 +1579,10 @@ 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
@@ -1566,21 +1727,16 @@ int which_subset; /* when not full, whether to suppress objs and/or traps */
     } else {
         int x, y, glyph, levl_glyph, default_glyph;
         uchar seenv;
-        unsigned save_swallowed;
         struct monst *mtmp;
         struct trap *t;
-        coord pos;
         char buf[BUFSZ];
         /* 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() */
 
-        save_swallowed = u.uswallow;
-        iflags.save_uinwater = u.uinwater, iflags.save_uburied = u.uburied;
-        u.uinwater = u.uburied = 0;
-        u.uswallow = 0;
-        if (iflags.save_uinwater || iflags.save_uburied)
+        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,
@@ -1605,8 +1761,8 @@ int which_subset; /* when not full, whether to suppress objs and/or traps */
                        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 = !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)
@@ -1676,17 +1832,10 @@ int which_subset; /* when not full, whether to suppress objs and/or traps */
 
         /* allow player to move cursor around and get autodescribe feedback
            based on what is visible now rather than what is on 'real' map */
-        pos.x = u.ux, pos.y = u.uy;
-        iflags.autodescribe = TRUE;
-        iflags.terrainmode = which_subset | TER_MAP; /* guaranteed non-zero */
-        getpos(&pos, FALSE, "anything of interest");
-        iflags.terrainmode = 0;
-        /* leave iflags.autodescribe 'on' even if it was previously 'off' */
-
-        u.uinwater = iflags.save_uinwater, u.uburied = iflags.save_uburied;
-        iflags.save_uinwater = iflags.save_uburied = 0;
-        if (save_swallowed)
-            u.uswallow = 1;
+        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);
index 8ffe31409f1d51b320750a5a950a5d4821b6621f..dac2bc3b5cb919da5360668714a93aad7680666f 100644 (file)
@@ -74,11 +74,13 @@ char *outbuf;
             mons[u.umonnum].mname, plname);
     if (u.usteed)
         Sprintf(eos(outbuf), ", mounted on %s", y_monnam(u.usteed));
+    if (u.uundetected || (Upolyd && youmonst.m_ap_type))
+        mhidden_description(&youmonst, FALSE, eos(outbuf));
     return outbuf;
 }
 
 /* describe a hidden monster; used for look_at during extended monster
-   detection and for probing */
+   detection and for probing; also when looking at self */
 void
 mhidden_description(mon, altmon, outbuf)
 struct monst *mon;
@@ -86,8 +88,10 @@ boolean altmon; /* for probing: if mimicking a monster, say so */
 char *outbuf;
 {
     struct obj *otmp;
-    boolean fakeobj;
-    int x = mon->mx, y = mon->my, glyph = levl[x][y].glyph;
+    boolean fakeobj, isyou = (mon == &youmonst);
+    int x = isyou ? u.ux : mon->mx, y = isyou ? u.uy : mon->my,
+        glyph = (level.flags.hero_memory && !isyou) ? levl[x][y].glyph
+                                                    : glyph_at(x, y);
 
     *outbuf = '\0';
     if (mon->m_ap_type == M_AP_FURNITURE
@@ -113,7 +117,7 @@ char *outbuf;
         if (altmon)
             Sprintf(outbuf, ", masquerading as %s",
                     an(mons[mon->mappearance].mname));
-    } else if (mon->mundetected) {
+    } else if (isyou ? u.uundetected : mon->mundetected) {
         Strcpy(outbuf, ", hiding");
         if (hides_under(mon->data)) {
             Strcat(outbuf, " under ");
@@ -141,10 +145,16 @@ struct obj **obj_p;
 {
     boolean fakeobj = FALSE;
     struct monst *mtmp;
-    struct obj *otmp = vobj_at(x, y);
+    struct obj *otmp;
     int glyphotyp = glyph_to_obj(glyph);
 
     *obj_p = (struct obj *) 0;
+    /* TODO: check inside containers in case glyph came from detection */
+    if ((otmp = sobj_at(glyphotyp, x, y)) == 0)
+        for (otmp = level.buriedobjlist; otmp; otmp = otmp->nobj)
+            if (otmp->ox == x && otmp->oy == y && otmp->otyp == glyphotyp)
+                break;
+
     /* there might be a mimic here posing as an object */
     mtmp = m_at(x, y);
     if (mtmp && is_obj_mappear(mtmp, (unsigned) glyphotyp))
@@ -194,7 +204,9 @@ int x, y, glyph;
     } else
         Strcpy(buf, something); /* sanity precaution */
 
-    if (levl[x][y].typ == STONE || levl[x][y].typ == SCORR)
+    if (otmp && otmp->where == OBJ_BURIED)
+        Strcat(buf, " (buried)");
+    else if (levl[x][y].typ == STONE || levl[x][y].typ == SCORR)
         Strcat(buf, " embedded in stone");
     else if (IS_WALL(levl[x][y].typ) || levl[x][y].typ == SDOOR)
         Strcat(buf, " embedded in a wall");
@@ -229,9 +241,14 @@ int x, y;
                     ? "peaceful "
                     : "",
             name);
-    if (u.ustuck == mtmp)
-        Strcat(buf, (Upolyd && sticks(youmonst.data))
-                     ? ", being held" : ", holding you");
+    if (u.ustuck == mtmp) {
+        if (u.uswallow || iflags.save_uswallow) /* monster detection */
+            Strcat(buf, is_animal(mtmp->data)
+                          ? ", swallowing you" : ", engulfing you");
+        else
+            Strcat(buf, (Upolyd && sticks(youmonst.data))
+                          ? ", being held" : ", holding you");
+    }
     if (mtmp->mleashed)
         Strcat(buf, ", leashed to you");
 
@@ -328,6 +345,7 @@ char *buf, *monbuf;
     buf[0] = monbuf[0] = '\0';
     glyph = glyph_at(x, y);
     if (u.ux == x && u.uy == y && canspotself()
+        && !(iflags.save_uswallow && glyph == mon_to_glyph(u.ustuck))
         && (!iflags.terrainmode || (iflags.terrainmode & TER_MON) != 0)) {
         /* fill in buf[] */
         (void) self_lookat(buf);
@@ -342,7 +360,8 @@ char *buf, *monbuf;
            (even if you could also see yourself via other means).
            Sensing self while blind or swallowed is treated as if it
            were by normal vision (cf canseeself()). */
-        if ((Invisible || u.uundetected) && !Blind && !u.uswallow) {
+        if ((Invisible || u.uundetected) && !Blind
+            && !(u.uswallow || iflags.save_uswallow)) {
             unsigned how = 0;
 
             if (Infravision)
@@ -702,10 +721,15 @@ const char **firstmatch;
      * submerged will always both be False and skip this code.)
      */
     x_str = 0;
-    if (looked && (u.uswallow || submerged) && distu(cc.x, cc.y) > 2) {
+    if (!looked) {
+        ; /* skip special handling */
+    } else if (((u.uswallow || submerged) && distu(cc.x, cc.y) > 2)
+               /* detection showing some category, so mostly background */
+               || ((iflags.terrainmode & (TER_DETECT | TER_MAP)) == TER_DETECT
+                   && glyph == cmap_to_glyph(S_stone))) {
         x_str = unreconnoitered;
         need_to_look = FALSE;
-    } else if (looked && u.uswallow && is_swallow_sym(sym)) {
+    } else if (is_swallow_sym(sym)) {
         x_str = mon_interior;
         need_to_look = TRUE; /* for specific monster type */
     }
@@ -943,9 +967,7 @@ coord *click_cc;
         if (quick) {
             from_screen = TRUE; /* yes, we want to use the cursor */
             i = 'y';
-        }
-
-        if (i != 'y') {
+        } else {
             menu_item *pick_list = (menu_item *) 0;
             winid win;
             anything any;
index a8adba34c4c06775abec7d0418c1ee65e09f571a..e0abcb54198bd926c5c2d2f11f5daa81a31f9946 100644 (file)
@@ -1136,6 +1136,7 @@ boolean atme;
     case SPE_INVISIBILITY:
         (void) peffects(pseudo);
         break;
+    /* end of potion-like spells */
 
     case SPE_CURE_BLINDNESS:
         healup(0, 0, FALSE, TRUE);
@@ -1151,10 +1152,12 @@ boolean atme;
         (void) make_familiar((struct obj *) 0, u.ux, u.uy, FALSE);
         break;
     case SPE_CLAIRVOYANCE:
-        if (!BClairvoyant)
-            do_vicinity_map();
+        if (!BClairvoyant) {
+            if (role_skill >= P_SKILLED)
+                pseudo->blessed = 1; /* detect monsters as well as map */
+            do_vicinity_map(pseudo);
         /* at present, only one thing blocks clairvoyance */
-        else if (uarmh && uarmh->otyp == CORNUTHAUM)
+        else if (uarmh && uarmh->otyp == CORNUTHAUM)
             You("sense a pointy hat on top of your %s.", body_part(HEAD));
         break;
     case SPE_PROTECTION: