]> granicus.if.org Git - nethack/commitdiff
object bypass bit sanity for container contents
authorPatR <rankin@nethack.org>
Mon, 11 Apr 2022 09:16:22 +0000 (02:16 -0700)
committerPatR <rankin@nethack.org>
Mon, 11 Apr 2022 09:16:22 +0000 (02:16 -0700)
Noticed when adding a 'tip container' choice to item-actions for
context sensitive inventory (update pending).  Putting items into a
container with menustyle traditional and then takiing them out with
the #tip command while 'sanity_check' is On would produce warnings
once they were on the floor.

askchain() uses object bypassing to be able to cope with multi-drop
potentially changing invent, and it tried to reset that when done.
But it did so with the original object list (invent in this case)
and that doesn't reset individual objects that have been moved to
any other list.  The between-turn resetting of bypass bits wasn't
doing so for container contents.  The sanity check wasn't--still
isn't--checking those either, so it wasn't noticeable while items
were still inside the container.  But taking them out with #tip
doesn't touch any bypass bits, so between-turn reset isn't triggered
and the items that came out of the container with bypass set
continued to have it set while on floor.  sanity_check complained.

Change clear_bypasses() to handle container contents, and change
askchain() to call it instead of just clearing bypasses for whatever
is left of its input chain.  (The latter probably isn't necessary
now that the between-turn cleanup deals with contents.)

doc/fixes3-7-0.txt
src/invent.c
src/pickup.c
src/worn.c

index 494e4c1b8379dfb826b183accb1564e8a8ddbb18..49d66b5a0deb76147d1d4f96b1a85c0916f97e97 100644 (file)
@@ -877,6 +877,9 @@ restoring while attached ball or chain is on floor in a breach of a shop wall
        might trigger an impossible about being positioned too far from hero
 don't try to catch up for lost time for shop damage repair in restdamage()
        called from getlev(); let normal shopkeeper movement take care of it
+putting objects into a container with menustyle=traditional and then taking
+       them back out with #tip would result in complaints about obj bypass
+       bit being set if sanity_check was On
 
 
 Fixes to 3.7.0-x Problems that Were Exposed Via git Repository
index be8fb8d731d4c965e197a719fce64c491e4977b9..2334cb3c5f46a10dc23b7dcd7b347c7df81419df 100644 (file)
@@ -2051,15 +2051,16 @@ ggetobj(const char *word, int (*fn)(OBJ_P), int mx,
  * Walk through the chain starting at objchn and ask for all objects
  * with olet in olets (if nonNULL) and satisfying ckfn (if nonnull)
  * whether the action in question (i.e., fn) has to be performed.
- * If allflag then no questions are asked.  Mx gives the max number
- * of objects to be treated.  Return the number of objects treated.
  */
 int
-askchain(struct obj **objchn, /* *objchn might change */
-         const char *olets,   /* olets is an Obj Class char array */
-         int allflag,
-         int (*fn)(OBJ_P), int (*ckfn)(OBJ_P),
-         int mx, const char *word)
+askchain(
+    struct obj **objchn, /* *objchn might change */
+    const char *olets,   /* olets is an Obj Class char array */
+    int allflag,         /* bypass prompting about individual items */
+    int (*fn)(OBJ_P),    /* action to perform on selected items */
+    int (*ckfn)(OBJ_P),  /* callback to decided if an item is selectable */
+    int mx,              /* if non-0, maximum number of objects to process */
+    const char *word)    /* name of the action */
 {
     struct obj *otmp, *otmpo;
     char sym, ilet;
@@ -2202,7 +2203,10 @@ askchain(struct obj **objchn, /* *objchn might change */
         pline("No applicable objects.");
  ret:
     unsortloot(&sortedchn);
-    bypass_objlist(*objchn, FALSE);
+    /* can't just clear bypass bit of items in objchn because the action
+       applied to selected ones might move them to a different chain */
+    /*bypass_objlist(*objchn, FALSE);*/
+    clear_bypasses();
     return cnt;
 }
 
index b762e7d3db3ee23bee5a67ce2c97cf7cb90a085a..759f0926d02bc5da6ccccf9bfed890272ccbae6a 100644 (file)
@@ -2697,10 +2697,10 @@ stash_ok(struct obj *obj)
 }
 
 int
-use_container(struct obj **objp,
-              int held,
-              boolean more_containers) /* True iff #loot multiple and this
-                                          isn't last one */
+use_container(
+    struct obj **objp,
+    int held,
+    boolean more_containers) /* True iff #loot multiple and this isn't last */
 {
     struct obj *otmp, *obj = *objp;
     boolean quantum_cat, cursed_mbag, loot_out, loot_in, loot_in_first,
@@ -2817,7 +2817,8 @@ use_container(struct obj **objp,
             } else {
                 c = in_or_out_menu(qbuf, g.current_container,
                                    outmaybe, inokay,
-                                   (boolean) (used != ECMD_OK), more_containers);
+                                   (boolean) (used != ECMD_OK),
+                                   more_containers);
             }
         } else { /* TRADITIONAL or COMBINATION */
             xbuf[0] = '\0'; /* list of extra acceptable responses */
index 1040715e465bb3eddbb16198aed4b5d00b071a92..74578fc904902c19abdb52a836d783d1395e3dd3 100644 (file)
@@ -6,6 +6,7 @@
 #include "hack.h"
 
 static void m_lose_armor(struct monst *, struct obj *);
+static void clear_bypass(struct obj *);
 static void m_dowear_type(struct monst *, long, boolean, boolean);
 static int extra_pref(struct monst *, struct obj *);
 
@@ -786,11 +787,25 @@ m_lose_armor(struct monst *mon, struct obj *obj)
     newsym(mon->mx, mon->my);
 }
 
-/* all objects with their bypass bit set should now be reset to normal */
+/* clear bypass bits for an object chain, plus contents if applicable */
+static void
+clear_bypass(struct obj *objchn)
+{
+    struct obj *o;
+
+    for (o = objchn; o; o = o->nobj) {
+        o->bypass = 0;
+        if (Has_contents(o))
+            clear_bypass(o->cobj);
+    }
+}
+
+/* all objects with their bypass bit set should now be reset to normal;
+   this can be a relatively expensive operation so is only called if
+   g.context.bypasses is set */
 void
 clear_bypasses(void)
 {
-    struct obj *otmp, *nobj;
     struct monst *mtmp;
 
     /*
@@ -800,41 +815,15 @@ clear_bypasses(void)
      * bypassed.
      */
 
-    for (otmp = fobj; otmp; otmp = nobj) {
-        nobj = otmp->nobj;
-        if (otmp->bypass) {
-            otmp->bypass = 0;
-
-            /* bypass will have inhibited any stacking, but since it's
-             * used for polymorph handling, the objects here probably
-             * have been transformed and won't be stacked in the usual
-             * manner afterwards; so don't bother with this.
-             * [Changing the fobj chain mid-traversal would also be risky.]
-             */
-#if 0
-            if (objects[otmp->otyp].oc_merge) {
-                xchar ox, oy;
-
-                (void) get_obj_location(otmp, &ox, &oy, 0);
-                stack_object(otmp);
-                newsym(ox, oy);
-            }
-#endif /*0*/
-        }
-    }
-    for (otmp = g.invent; otmp; otmp = otmp->nobj)
-        otmp->bypass = 0;
-    for (otmp = g.migrating_objs; otmp; otmp = otmp->nobj)
-        otmp->bypass = 0;
-    for (otmp = g.level.buriedobjlist; otmp; otmp = otmp->nobj)
-        otmp->bypass = 0;
-    for (otmp = g.billobjs; otmp; otmp = otmp->nobj)
-        otmp->bypass = 0;
+    clear_bypass(fobj);
+    clear_bypass(g.invent);
+    clear_bypass(g.migrating_objs);
+    clear_bypass(g.level.buriedobjlist);
+    clear_bypass(g.billobjs);
     for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
         if (DEADMONSTER(mtmp))
             continue;
-        for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
-            otmp->bypass = 0;
+        clear_bypass(mtmp->minvent);
         /* long worm created by polymorph has mon->mextra->mcorpsenm set
            to PM_LONG_WORM to flag it as not being subject to further
            polymorph (so polymorph zap won't hit monster to transform it
@@ -844,22 +833,21 @@ clear_bypasses(void)
             MCORPSENM(mtmp) = NON_PM;
     }
     for (mtmp = g.migrating_mons; mtmp; mtmp = mtmp->nmon) {
-        for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
-            otmp->bypass = 0;
+        clear_bypass(mtmp->minvent);
         /* no MCORPSENM(mtmp)==PM_LONG_WORM check here; long worms can't
            be just created by polymorph and migrating at the same time */
     }
     /* this is a no-op since mydogs is only non-Null during level change or
        final ascension and we aren't called at those times, but be thorough */
     for (mtmp = g.mydogs; mtmp; mtmp = mtmp->nmon)
-        for (otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
-            otmp->bypass = 0;
-
-    /* ball can be "floating", not on any chain */
+        clear_bypass(mtmp->minvent);
+    /* ball and chain can be "floating", not on any object chain (when
+       hero is swallowed by an engulfing monster, for instance) */
     if (uball)
         uball->bypass = 0;
+    if (uchain)
+        uchain->bypass = 0;
 
-    /* g.billobjs and g.mydogs chains don't matter here */
     g.context.bypasses = FALSE;
 }