]> granicus.if.org Git - nethack/commitdiff
unsplitting split object stack
authorPatR <rankin@nethack.org>
Mon, 19 Oct 2015 00:37:15 +0000 (17:37 -0700)
committerPatR <rankin@nethack.org>
Mon, 19 Oct 2015 00:37:15 +0000 (17:37 -0700)
Replace the code that Dean objected to with something a little bit more
robust.  It doesn't rely on the two stacks being adjacent or having the
same inventory letter.  It is still vulnerable to having another
splitobj() occur between the offending split and its attempted unsplit,
or to either of the two halves of a split being extracted from their
object chain.  As before, failure to unsplit only results in the two
halves of the split remaining separate stacks, not anything more drastic
like the panic() that prompted all this.

Simplification of hallucinated currency names got mixed in with this
patch.  I haven't bothered separating it back out.

Whoever reset PATCHLEVEL to 0 jumped the gun.  This patch increments it
since change to the 'context' structure breaks save file compatibility,
so it will need to undergo another reset before release.

include/context.h
include/extern.h
include/patchlevel.h
src/allmain.c
src/dothrow.c
src/invent.c
src/mkobj.c
src/mon.c

index b73499f97939a32a841f6b5fced0da837b427659..0e7595ff929c7e1201a9e6d0d47d96a2ac29397c 100644 (file)
@@ -1,4 +1,4 @@
-/* NetHack 3.6 context.h       $NHDT-Date: 1434421363 2015/06/16 02:22:43 $  $NHDT-Branch: master $:$NHDT-Revision: 1.26 $ */
+/* NetHack 3.6 context.h       $NHDT-Date: 1445215010 2015/10/19 00:36:50 $  $NHDT-Branch: master $:$NHDT-Revision: 1.27 $ */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -76,6 +76,11 @@ struct polearm_info {
     unsigned m_id;        /* monster id of hitmon, in save file */
 };
 
+struct obj_split {
+    unsigned parent_oid, /* set: splitobj(),         */
+             child_oid;  /* reset: clear_splitobjs() */
+};
+
 struct tribute_info {
     size_t tributesz;       /* make it possible to skip this in future */
     boolean enabled;        /* Do we have tributes turned on? */
@@ -117,6 +122,7 @@ struct context_info {
     struct takeoff_info takeoff;
     struct warntype_info warntype;
     struct polearm_info polearm;
+    struct obj_split objsplit; /* track most recently split object stack */
     struct tribute_info tribute;
 };
 
index 3bbcd4d5499e29e2b83fc829d1bcb7ea44524315..bd22f7826f44a9be0b70941eef506f1d61172427 100644 (file)
@@ -1,4 +1,4 @@
-/* NetHack 3.6 extern.h        $NHDT-Date: 1445126411 2015/10/18 00:00:11 $  $NHDT-Branch: master $:$NHDT-Revision: 1.508 $ */
+/* NetHack 3.6 extern.h        $NHDT-Date: 1445215014 2015/10/19 00:36:54 $  $NHDT-Branch: master $:$NHDT-Revision: 1.509 $ */
 /* Copyright (c) Steve Creps, 1988.                              */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -1214,6 +1214,8 @@ E struct obj *FDECL(mkobj, (CHAR_P, BOOLEAN_P));
 E int NDECL(rndmonnum);
 E boolean FDECL(bogon_is_pname, (CHAR_P));
 E struct obj *FDECL(splitobj, (struct obj *, long));
+E struct obj *FDECL(unsplitobj, (struct obj *));
+E void NDECL(clear_splitobjs);
 E void FDECL(replace_object, (struct obj *, struct obj *));
 E void FDECL(bill_dummy_object, (struct obj *));
 E void FDECL(costly_alteration, (struct obj *, int));
index 4db87dae2eaf19a3e4d0d55d851e06363ab9cf88..ca276c52cf9ea5ae989d0b0ce3f8de5b6ae26e89 100644 (file)
@@ -1,4 +1,4 @@
-/* NetHack 3.6 patchlevel.h    $NHDT-Date: 1432512782 2015/05/25 00:13:02 $  $NHDT-Branch: master $:$NHDT-Revision: 1.107 $ */
+/* NetHack 3.6 patchlevel.h    $NHDT-Date: 1445215015 2015/10/19 00:36:55 $  $NHDT-Branch: master $:$NHDT-Revision: 1.109 $ */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -8,7 +8,7 @@
 /*
  * PATCHLEVEL is updated for each release.
  */
-#define PATCHLEVEL 0
+#define PATCHLEVEL 1
 /*
  * Incrementing EDITLEVEL can be used to force invalidation of old bones
  * and save files.
index a60f2c4ac62e23cc2dcd4718a7a8867f70627b8c..aa68aa6a46f4b8f72edc27a2dce29b3d23c55875 100644 (file)
@@ -1,4 +1,4 @@
-/* NetHack 3.6 allmain.c       $NHDT-Date: 1438505671 2015/08/02 08:54:31 $  $NHDT-Branch: master $:$NHDT-Revision: 1.62 $ */
+/* NetHack 3.6 allmain.c       $NHDT-Date: 1445215016 2015/10/19 00:36:56 $  $NHDT-Branch: master $:$NHDT-Revision: 1.65 $ */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -376,6 +376,7 @@ boolean resuming;
         /* once-per-player-input things go here */
         /****************************************/
 
+        clear_splitobjs();
         find_ac();
         if (!context.mv || Blind) {
             /* redo monsters if hallu or wearing a helm of telepathy */
index 9f065c5beb84c5948a101add0b41413bf10a4ca6..3ff1a5e0793401cff9f02c4c2d5c3f95b89955d3 100644 (file)
@@ -1,4 +1,4 @@
-/* NetHack 3.6 dothrow.c       $NHDT-Date: 1444772016 2015/10/13 21:33:36 $  $NHDT-Branch: master $:$NHDT-Revision: 1.106 $ */
+/* NetHack 3.6 dothrow.c       $NHDT-Date: 1445215018 2015/10/19 00:36:58 $  $NHDT-Branch: master $:$NHDT-Revision: 1.110 $ */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -50,24 +50,10 @@ int shotlimit;
          * merge obj into another stack--usually quiver--even if it hadn't
          * been split from there (possibly triggering a panic in addinv),
          * and freeinv+addinv potentially has other side-effects.
-         *
-         * If obj came from splitobj(), it has been split from the item
-         * which precedes it in inventory and shares same inv letter.
-         * (This could get a false match if obj wasn't split from the
-         * preceding item and they're both using the overflow letter '#',
-         * but merging to have fewer '#' items should be a good thing,
-         * and we're not using addinv() so can't trigger its panic.)
          */
-        for (otmp = invent; otmp; otmp = otmp->nobj)
-            if (otmp == obj) {
-                /* obj wasn't the result of splitobj, so we're done */
-                break;
-            } else if (otmp->nobj == obj) {
-                if (otmp->invlet == obj->invlet)
-                    (void) merged(&otmp, &obj);
-                /* found obj's preceding item, so no need to look further */
-                break;
-            }
+        if (obj->o_id == context.objsplit.parent_oid
+            || obj->o_id == context.objsplit.child_oid)
+            (void) unsplitobj(obj);
         return 0; /* no time passes */
     }
 
@@ -108,11 +94,12 @@ int shotlimit;
         Sprintf(killer.name, "throwing %s bare-handed", killer_xname(obj));
         instapetrify(killer.name);
     }
-    if (obj->otyp == TOWEL && obj->spe > 0) obj->spe--;
     if (welded(obj)) {
         weldmsg(obj);
         return 1;
     }
+    if (is_wet_towel(obj))
+        dry_a_towel(obj, -1, FALSE);
 
     /* Multishot calculations
      * (potential volley of up to N missiles; default for N is 1)
@@ -310,12 +297,13 @@ autoquiver()
     for (otmp = invent; otmp; otmp = otmp->nobj) {
         if (otmp->owornmask || otmp->oartifact || !otmp->dknown) {
             ; /* Skip it */
-        } else if (otmp->otyp == ROCK ||
+        } else if (otmp->otyp == ROCK
                    /* seen rocks or known flint or known glass */
-                   (objects[otmp->otyp].oc_name_known && otmp->otyp == FLINT)
-                   || (objects[otmp->otyp].oc_name_known
-                       && otmp->oclass == GEM_CLASS
-                       && objects[otmp->otyp].oc_material == GLASS)) {
+                   || (otmp->otyp == FLINT
+                       && objects[otmp->otyp].oc_name_known)
+                   || (otmp->oclass == GEM_CLASS
+                       && objects[otmp->otyp].oc_material == GLASS
+                       && objects[otmp->otyp].oc_name_known)) {
             if (uslinging())
                 oammo = otmp;
             else if (ammo_and_launcher(otmp, uswapwep))
@@ -327,8 +315,7 @@ autoquiver()
                  player has to select them explicitly */
         } else if (is_ammo(otmp)) {
             if (ammo_and_launcher(otmp, uwep))
-                /* Ammo matched with launcher (bow and arrow, crossbow and
-                 * bolt) */
+                /* Ammo matched with launcher (bow+arrow, crossbow+bolt) */
                 oammo = otmp;
             else if (ammo_and_launcher(otmp, uswapwep))
                 altammo = otmp;
index 02e747e296436191f000751e907806892e44773c..b71b7c8c9756232063bfd3ad1c07a26bc9f0ce5b 100644 (file)
@@ -1,4 +1,4 @@
-/* NetHack 3.6 invent.c        $NHDT-Date: 1438652306 2015/08/04 01:38:26 $  $NHDT-Branch: master $:$NHDT-Revision: 1.170 $ */
+/* NetHack 3.6 invent.c        $NHDT-Date: 1445215019 2015/10/19 00:36:59 $  $NHDT-Branch: master $:$NHDT-Revision: 1.174 $ */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -345,16 +345,16 @@ struct obj **potmp, **pobj;
 }
 
 /*
-Adjust hero intrinsics as if this object was being added to the hero's
-inventory.  Called _before_ the object has been added to the hero's
-inventory.
-
-This is called when adding objects to the hero's inventory normally (via
-addinv) or when an object in the hero's inventory has been polymorphed
-in-place.
-
-It may be valid to merge this code with with addinv_core2().
-*/
+ * Adjust hero intrinsics as if this object was being added to the hero's
+ * inventory.  Called _before_ the object has been added to the hero's
+ * inventory.
+ *
+ * This is called when adding objects to the hero's inventory normally (via
+ * addinv) or when an object in the hero's inventory has been polymorphed
+ * in-place.
+ *
+ * It may be valid to merge this code with with addinv_core2().
+ */
 void
 addinv_core1(obj)
 struct obj *obj;
@@ -402,14 +402,14 @@ struct obj *obj;
 }
 
 /*
-Adjust hero intrinsics as if this object was being added to the hero's
-inventory.  Called _after_ the object has been added to the hero's
-inventory.
-
-This is called when adding objects to the hero's inventory normally (via
-addinv) or when an object in the hero's inventory has been polymorphed
-in-place.
-*/
+ * Adjust hero intrinsics as if this object was being added to the hero's
+ * inventory.  Called _after_ the object has been added to the hero's
+ * inventory.
+ *
+ * This is called when adding objects to the hero's inventory normally (via
+ * addinv) or when an object in the hero's inventory has been polymorphed
+ * in-place.
+ */
 void
 addinv_core2(obj)
 struct obj *obj;
@@ -422,9 +422,9 @@ struct obj *obj;
 }
 
 /*
-Add obj to the hero's inventory.  Make sure the object is "free".
-Adjust hero attributes as necessary.
-*/
+ * Add obj to the hero's inventory.  Make sure the object is "free".
+ * Adjust hero attributes as necessary.
+ */
 struct obj *
 addinv(obj)
 struct obj *obj;
@@ -621,12 +621,12 @@ boolean maybe_unpaid; /* false if caller handles shop billing */
 }
 
 /*
-Adjust hero's attributes as if this object was being removed from the
-hero's inventory.  This should only be called from freeinv() and
-where we are polymorphing an object already in the hero's inventory.
-
-Should think of a better name...
-*/
+ * Adjust hero's attributes as if this object was being removed from the
+ * hero's inventory.  This should only be called from freeinv() and
+ * where we are polymorphing an object already in the hero's inventory.
+ *
+ * Should think of a better name...
+ */
 void
 freeinv_core(obj)
 struct obj *obj;
@@ -783,25 +783,26 @@ static const char *const currencies[] = {
     "Hong Kong Luna Dollar", /* The Moon is a Harsh Mistress */
     "kongbuck",              /* Snow Crash */
     "nanite",                /* System Shock 2 */
-    "quatloo",               /* Sim City */
+    "quatloo",               /* Star Trek, Sim City */
     "simoleon",              /* Sim City */
     "solari",                /* Spaceballs */
     "spacebuck",             /* Spaceballs */
     "sporebuck",             /* Spore */
     "Triganic Pu",           /* The Hitchhiker's Guide to the Galaxy */
     "woolong",               /* Cowboy Bebop */
+    "zorkmid",               /* Zork, NetHack */
 };
 
 const char *
 currency(amount)
 long amount;
 {
-    if (amount == 1L)
-        return (Hallucination ? currencies[rn2(SIZE(currencies))]
-                              : "zorkmid");
-    else
-        return (Hallucination ? makeplural(currencies[rn2(SIZE(currencies))])
-                              : "zorkmids");
+    const char *res;
+
+    res = Hallucination ? currencies[rn2(SIZE(currencies))] : "zorkmid";
+    if (amount != 1L)
+        res = makeplural(res);
+    return res;
 }
 
 boolean
@@ -870,10 +871,10 @@ register int x, y;
     return ((struct obj *) 0);
 }
 
+/* compact a string of inventory letters by dashing runs of letters */
 STATIC_OVL void
 compactify(buf)
 register char *buf;
-/* compact a string of inventory letters by dashing runs of letters */
 {
     register int i1 = 1, i2 = 1;
     register char ilet, ilet1, ilet2;
@@ -1619,18 +1620,18 @@ nextclass:
     ilet = 'a' - 1;
     if (*objchn && (*objchn)->oclass == COIN_CLASS)
         ilet--;                     /* extra iteration */
-                                    /*
-                                     * Multiple Drop can change the invent chain while it operates
-                                     * (dropping a burning potion of oil while levitating creates
-                                     * an explosion which can destroy inventory items), so simple
-                                     * list traversal
-                                     * for (otmp = *objchn; otmp; otmp = otmp2) {
-                                     *     otmp2 = otmp->nobj;
-                                     *     ...
-                                     * }
-                                     * is inadequate here.  Use each object's bypass bit to keep
-                                     * track of which list elements have already been processed.
-                                     */
+    /*
+     * Multiple Drop can change the invent chain while it operates
+     * (dropping a burning potion of oil while levitating creates
+     * an explosion which can destroy inventory items), so simple
+     * list traversal
+     * for (otmp = *objchn; otmp; otmp = otmp2) {
+     *     otmp2 = otmp->nobj;
+     *     ...
+     * }
+     * is inadequate here.  Use each object's bypass bit to keep
+     * track of which list elements have already been processed.
+     */
     bypass_objlist(*objchn, FALSE); /* clear chain's bypass bits */
     while ((otmp = nxt_unbypassed_obj(*objchn)) != 0) {
         if (ilet == 'z')
@@ -3257,7 +3258,8 @@ reassign()
  *     user-assigned names, the 'count' portion being moved is
  *     effectively renamed so that it will merge with 'to' stack.
  */
-int doorganize() /* inventory organizer by Del Lamb */
+int
+doorganize() /* inventory organizer by Del Lamb */
 {
     struct obj *obj, *otmp, *splitting, *bumped;
     int ix, cur, trycnt;
@@ -3435,6 +3437,8 @@ int doorganize() /* inventory organizer by Del Lamb */
     prinv(adj_type, obj, 0L);
     if (bumped)
         prinv("Moving:", bumped, 0L);
+    if (splitting)
+        clear_splitobjs(); /* reset splitobj context */
     update_inventory();
     return (0);
 }
index 7c9e0be5d75001c57fe158219a797fd174767846..f211c255ce8bb6d251a02ef372a4b7deb366a928 100644 (file)
@@ -1,4 +1,4 @@
-/* NetHack 3.6 mkobj.c $NHDT-Date: 1444617220 2015/10/12 02:33:40 $  $NHDT-Branch: master $:$NHDT-Revision: 1.110 $ */
+/* NetHack 3.6 mkobj.c $NHDT-Date: 1445215021 2015/10/19 00:37:01 $  $NHDT-Branch: master $:$NHDT-Revision: 1.111 $ */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -413,6 +413,9 @@ long num;
     obj->owt = weight(obj);
     otmp->quan = num;
     otmp->owt = weight(otmp); /* -= obj->owt ? */
+
+    context.objsplit.parent_oid = obj->o_id;
+    context.objsplit.child_oid = otmp->o_id;
     obj->nobj = otmp;
     /* Only set nexthere when on the floor, nexthere is also used */
     /* as a back pointer to the container object when contained. */
@@ -430,6 +433,85 @@ long num;
     return otmp;
 }
 
+/* try to find the stack obj was split from, then merge them back together;
+   returns the combined object if unsplit is successful, null otherwise */
+struct obj *
+unsplitobj(obj)
+struct obj *obj;
+{
+    unsigned target_oid = 0;
+    struct obj *oparent = 0, *ochild = 0, *list = 0;
+
+    /*
+     * We don't operate on floor objects (we're following o->nobj rather
+     * than o->nexthere), on free objects (don't know which list to use when
+     * looking for obj's parent or child), on bill objects (too complicated,
+     * not needed), or on buried or migrating objects (not needed).
+     * [This could be improved, but at present additional generality isn't
+     * necessary.]
+     */
+    switch (obj->where) {
+    case OBJ_FREE:
+    case OBJ_FLOOR:
+    case OBJ_ONBILL:
+    case OBJ_MIGRATING:
+    case OBJ_BURIED:
+    default:
+        return (struct obj *) 0;
+    case OBJ_INVENT:
+        list = invent;
+        break;
+    case OBJ_MINVENT:
+        list = obj->ocarry->minvent;
+        break;
+    case OBJ_CONTAINED:
+        list = obj->ocontainer->cobj;
+        break;
+    }
+
+    /* first try the expected case; obj is split from another stack */
+    if (obj->o_id == context.objsplit.child_oid) {
+        /* parent probably precedes child and will require list traversal */
+        ochild = obj;
+        target_oid = context.objsplit.parent_oid;
+        if (obj->nobj && obj->nobj->o_id == target_oid)
+            oparent = obj->nobj;
+    } else if (obj->o_id == context.objsplit.parent_oid) {
+        /* alternate scenario: another stack was split from obj;
+           child probably follows parent and will be found here */
+        oparent = obj;
+        target_oid = context.objsplit.child_oid;
+        if (obj->nobj && obj->nobj->o_id == target_oid)
+            ochild = obj->nobj;
+    }
+    /* if we have only half the split, scan obj's list to find other half */
+    if (ochild && !oparent) {
+        /* expected case */
+        for (obj = list; obj; obj = obj->nobj)
+            if (obj->o_id == target_oid) {
+                oparent = obj;
+                break;
+            }
+    } else if (oparent && !ochild) {
+        /* alternate scenario */
+        for (obj = list; obj; obj = obj->nobj)
+            if (obj->o_id == target_oid) {
+                ochild = obj;
+                break;
+            }
+    }
+    /* if we have both parent and child, try to merge them;
+       if successful, return the combined stack, otherwise return null */
+    return (oparent && ochild && merged(&oparent, &ochild)) ? oparent : 0;
+}
+
+/* reset splitobj()/unsplitobj() context */
+void
+clear_splitobjs()
+{
+    context.objsplit.parent_oid = context.objsplit.child_oid = 0;
+}
+
 /*
  * Insert otmp right after obj in whatever chain(s) it is on.  Then extract
  * obj from the chain(s).  This function does a literal swap.  It is up to
index a620fb592a134763b0472ca353147410b20761fb..1ed1695a3cd6a32b58c02302b11166d74a4784c5 100644 (file)
--- a/src/mon.c
+++ b/src/mon.c
@@ -1,4 +1,4 @@
-/* NetHack 3.6 mon.c   $NHDT-Date: 1444095155 2015/10/06 01:32:35 $  $NHDT-Branch: master $:$NHDT-Revision: 1.183 $ */
+/* NetHack 3.6 mon.c   $NHDT-Date: 1445215021 2015/10/19 00:37:01 $  $NHDT-Branch: master $:$NHDT-Revision: 1.190 $ */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /* NetHack may be freely redistributed.  See license for details. */
 
@@ -659,6 +659,7 @@ movemon()
         /* reset obj bypasses before next monster moves */
         if (context.bypasses)
             clear_bypasses();
+        clear_splitobjs();
         if (minliquid(mtmp))
             continue;
 
@@ -705,7 +706,10 @@ movemon()
     /* reset obj bypasses after last monster has moved */
     if (context.bypasses)
         clear_bypasses();
-    dmonsfree();                /* remove all dead monsters */
+    clear_splitobjs();
+    /* remove dead monsters; dead vault guard will be left at <0,0>
+       if temporary corridor out of vault hasn't been removed yet */
+    dmonsfree();
 
     /* a monster may have levteleported player -dlc */
     if (u.utotype) {