]> granicus.if.org Git - nethack/commitdiff
fix add_to_minv panic when aklys kills enfulger
authorPatR <rankin@nethack.org>
Mon, 23 Sep 2019 22:16:39 +0000 (15:16 -0700)
committerPatR <rankin@nethack.org>
Mon, 23 Sep 2019 22:16:39 +0000 (15:16 -0700)
Reported directly to devteam rather than via the web contact form:
throwing wielded aklys while swallowed would hit the engulfer and
return to the hero's hand but leave a stale 'thrownobj' pointer if
the monster survived.  Under usual circumstances, throwing anything
else or throwing the aklys again when not engulfed would clear that
pointer, putting things back to normal.  However, killing any engulfer
with the same weapon would try to add it to engulfer's inventory to
be dropped as it died.  If the killing blow was via melee rather than
another throw, the object in question would still be in hero's
inventory instead of free, hence panic.

The initial returning-aklys implementation shared Mjollnir's code
which doesn't have this issue.  This reverts from having attached
aklys always returning successfully when thrown while swallowed to
Mjollnir's 99% chance of return and 99% to be caught when it does
come back.  (That was already the case if the engulfer was killed by
the throw, where hero wasn't swallowed anymore after the damage was
inflicted.)

doc/fixes36.3
include/flag.h
src/dothrow.c
src/mon.c

index 569d3d74279aaefa3f46916e8a4261e0cb8db62e..86b1df33e5919caaa155644a65f8474ac2cd97c9 100644 (file)
@@ -1,4 +1,4 @@
-$NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.115 $ $NHDT-Date: 1569189769 2019/09/22 22:02:49 $
+$NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.116 $ $NHDT-Date: 1569276988 2019/09/23 22:16:28 $
 
 This fixes36.3 file is here to capture information about updates in the 3.6.x
 lineage following the release of 3.6.2 in May 2019. Please note, however,
@@ -149,6 +149,9 @@ zapping self with wand of opening or spell of knock to escape from a trap
        reported "you're released from <trap>" rather than "from the <trap>"
 zapping self with wand of opening or spell of knock to escape from a pit trap
        didn't reset vision limits--only spots adjacent to pit were visible
+wielded aklys that returned to hero when thrown while inside an engulfer left
+       a stale 'thrownobj' pointer that triggered "add_to_minv: obj not free"
+       panic if same weapon killed any engulfer via melee from inside
 
 
 Fixes to Post-3.6.2 Problems that Were Exposed Via git Repository
index 3ea3ada261941589ba7cf3f9a3bd72c5ffa4e557..daef72bc44a7251c567b7c27fbb6106c4cc6bc59 100644 (file)
@@ -1,4 +1,4 @@
-/* NetHack 3.6 flag.h  $NHDT-Date: 1562532730 2019/07/07 20:52:10 $  $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.152 $ */
+/* NetHack 3.6 flag.h  $NHDT-Date: 1569276988 2019/09/23 22:16:28 $  $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.155 $ */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /*-Copyright (c) Michael Allison, 2006. */
 /* NetHack may be freely redistributed.  See license for details. */
@@ -446,6 +446,7 @@ struct instance_flags {
     boolean windowtype_deferred; /* pick a windowport and store it in
                                     chosen_windowport[], but do not switch to
                                     it in the midst of options processing */
+    genericptr_t returning_missile; /* 'struct obj *'; Mjollnir or aklys */
     boolean obsolete;  /* obsolete options can point at this, it isn't used */
 };
 
index a88e504894adba83dbc329eaff25fcdcb1899b81..a2d12c93a8043b06f65654a0cd12eacd893f81bd 100644 (file)
@@ -1,4 +1,4 @@
-/* NetHack 3.6 dothrow.c       $NHDT-Date: 1556201496 2019/04/25 14:11:36 $  $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.160 $ */
+/* NetHack 3.6 dothrow.c       $NHDT-Date: 1569276989 2019/09/23 22:16:29 $  $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.161 $ */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /*-Copyright (c) Robert Patrick Rankin, 2013. */
 /* NetHack may be freely redistributed.  See license for details. */
@@ -1098,10 +1098,11 @@ long wep_mask; /* used to re-equip returning boomerang */
 boolean twoweap; /* used to restore twoweapon mode if wielded weapon returns */
 {
     register struct monst *mon;
-    register int range, urange;
-    boolean crossbowing, impaired = (Confusion || Stunned || Blind
-                                     || Hallucination || Fumbling);
-    boolean tethered_weapon = (obj->otyp == AKLYS && (wep_mask & W_WEP) != 0);
+    int range, urange;
+    boolean crossbowing, clear_thrownobj = FALSE,
+            impaired = (Confusion || Stunned || Blind
+                        || Hallucination || Fumbling),
+            tethered_weapon = (obj->otyp == AKLYS && (wep_mask & W_WEP) != 0);
 
     notonhead = FALSE; /* reset potentially stale value */
     if ((obj->cursed || obj->greased) && (u.dx || u.dy) && !rn2(7)) {
@@ -1142,6 +1143,12 @@ boolean twoweap; /* used to restore twoweapon mode if wielded weapon returns */
 
     thrownobj = obj;
     thrownobj->was_thrown = 1;
+    iflags.returning_missile = ((obj->oartifact == ART_MJOLLNIR
+                                 && Role_if(PM_VALKYRIE))
+                                || tethered_weapon) ? (genericptr_t) obj
+                                                    : (genericptr_t) 0;
+    /* NOTE:  No early returns after this point or returning_missile
+       will be left with a stale pointer. */
 
     if (u.uswallow) {
         mon = u.ustuck;
@@ -1153,8 +1160,7 @@ boolean twoweap; /* used to restore twoweapon mode if wielded weapon returns */
         if (u.dz < 0
             /* Mjollnir must we wielded to be thrown--caller verifies this;
                aklys must we wielded as primary to return when thrown */
-            && ((Role_if(PM_VALKYRIE) && obj->oartifact == ART_MJOLLNIR)
-                || tethered_weapon)
+            && iflags.returning_missile
             && !impaired) {
             pline("%s the %s and returns to your hand!", Tobjnam(obj, "hit"),
                   ceiling(u.ux, u.uy));
@@ -1174,8 +1180,8 @@ boolean twoweap; /* used to restore twoweapon mode if wielded weapon returns */
         } else {
             hitfloor(obj, TRUE);
         }
-        thrownobj = (struct obj *) 0;
-        return;
+        clear_thrownobj = TRUE;
+        goto throwit_return;
 
     } else if (obj->otyp == BOOMERANG && !Underwater) {
         if (Is_airlevel(&u.uz) || Levitation)
@@ -1189,8 +1195,8 @@ boolean twoweap; /* used to restore twoweapon mode if wielded weapon returns */
                 setworn(obj, wep_mask);
                 u.twoweap = twoweap;
             }
-            thrownobj = (struct obj *) 0;
-            return;
+            clear_thrownobj = TRUE;
+            goto throwit_return;
         }
     } else {
         /* crossbow range is independent of strength */
@@ -1265,7 +1271,7 @@ boolean twoweap; /* used to restore twoweapon mode if wielded weapon returns */
                we're about to return */
             if (tethered_weapon)
                 tmp_at(DISP_END, 0);
-            return;
+            goto throwit_return;
         }
     }
 
@@ -1273,8 +1279,8 @@ boolean twoweap; /* used to restore twoweapon mode if wielded weapon returns */
         boolean obj_gone;
 
         if (mon->isshk && obj->where == OBJ_MINVENT && obj->ocarry == mon) {
-            thrownobj = (struct obj *) 0;
-            return; /* alert shk caught it */
+            clear_thrownobj = TRUE;
+            goto throwit_return; /* alert shk caught it */
         }
         (void) snuff_candle(obj);
         notonhead = (bhitpos.x != mon->mx || bhitpos.y != mon->my);
@@ -1289,33 +1295,22 @@ boolean twoweap; /* used to restore twoweapon mode if wielded weapon returns */
             hot_pursuit(mon);
 
         if (obj_gone)
-            thrownobj = 0;
+            thrownobj = (struct obj *) 0;
     }
 
     if (!thrownobj) {
         /* missile has already been handled */
-        if (tethered_weapon) tmp_at(DISP_END, 0);
-    } else if (u.uswallow) {
-        if (tethered_weapon) {
+        if (tethered_weapon)
             tmp_at(DISP_END, 0);
-            pline("%s returns to your hand!", The(xname(thrownobj)));
-            thrownobj = addinv(thrownobj);
-            (void) encumber_msg();
-            /* in case addinv() autoquivered */
-            if (thrownobj->owornmask & W_QUIVER)
-                setuqwep((struct obj *) 0);
-            setuwep(thrownobj);
-        } else {
-            /* ball is not picked up by monster */
-            if (obj != uball)
-                (void) mpickobj(u.ustuck, obj);
-            thrownobj = (struct obj *) 0;
-        }
+    } else if (u.uswallow && !iflags.returning_missile) {
+ swallowit:
+        if (obj != uball)
+            (void) mpickobj(u.ustuck, obj); /* clears 'thrownobj' */
+        goto throwit_return;
     } else {
         /* Mjollnir must we wielded to be thrown--caller verifies this;
            aklys must we wielded as primary to return when thrown */
-        if ((obj->oartifact == ART_MJOLLNIR && Role_if(PM_VALKYRIE))
-            || tethered_weapon) {
+        if (iflags.returning_missile) { /* Mjollnir or aklys */
             if (rn2(100)) {
                 if (tethered_weapon)
                     tmp_at(DISP_END, BACKTRACK);
@@ -1355,14 +1350,14 @@ boolean twoweap; /* used to restore twoweapon mode if wielded weapon returns */
                         losehp(Maybe_Half_Phys(dmg), killer_xname(obj),
                                KILLED_BY);
                     }
-                    if (ship_object(obj, u.ux, u.uy, FALSE)) {
-                        thrownobj = (struct obj *) 0;
-                        return;
-                    }
-                    dropy(obj);
+
+                    if (u.uswallow)
+                        goto swallowit;
+                    if (!ship_object(obj, u.ux, u.uy, FALSE))
+                        dropy(obj);
                 }
-                thrownobj = (struct obj *) 0;
-                return;
+                clear_thrownobj = TRUE;
+                goto throwit_return;
             } else {
                 if (tethered_weapon)
                     tmp_at(DISP_END, 0);
@@ -1374,6 +1369,9 @@ boolean twoweap; /* used to restore twoweapon mode if wielded weapon returns */
                    explicitly rewield the weapon to get throw-and-return
                    capability back anyway, quivered or not shouldn't matter */
                 pline("%s to return!", Tobjnam(obj, "fail"));
+
+                if (u.uswallow)
+                    goto swallowit;
                 /* continue below with placing 'obj' at target location */
             }
         }
@@ -1388,12 +1386,12 @@ boolean twoweap; /* used to restore twoweapon mode if wielded weapon returns */
             tmp_at(DISP_END, 0);
             breakmsg(obj, cansee(bhitpos.x, bhitpos.y));
             breakobj(obj, bhitpos.x, bhitpos.y, TRUE, TRUE);
-            thrownobj = (struct obj *) 0;
-            return;
+            clear_thrownobj = TRUE;
+            goto throwit_return;
         }
         if (flooreffects(obj, bhitpos.x, bhitpos.y, "fall")) {
-            thrownobj = (struct obj *) 0;
-            return;
+            clear_thrownobj = TRUE;
+            goto throwit_return;
         }
         obj_no_longer_held(obj);
         if (mon && mon->isshk && is_pick(obj)) {
@@ -1402,13 +1400,13 @@ boolean twoweap; /* used to restore twoweapon mode if wielded weapon returns */
             if (*u.ushops || obj->unpaid)
                 check_shop_obj(obj, bhitpos.x, bhitpos.y, FALSE);
             (void) mpickobj(mon, obj); /* may merge and free obj */
-            thrownobj = (struct obj *) 0;
-            return;
+            clear_thrownobj = TRUE;
+            goto throwit_return;
         }
         (void) snuff_candle(obj);
         if (!mon && ship_object(obj, bhitpos.x, bhitpos.y, FALSE)) {
-            thrownobj = (struct obj *) 0;
-            return;
+            clear_thrownobj = TRUE;
+            goto throwit_return;
         }
         thrownobj = (struct obj *) 0;
         place_object(obj, bhitpos.x, bhitpos.y);
@@ -1431,6 +1429,12 @@ boolean twoweap; /* used to restore twoweapon mode if wielded weapon returns */
         if (obj_sheds_light(obj))
             vision_full_recalc = 1;
     }
+
+ throwit_return:
+    iflags.returning_missile = (genericptr_t) 0;
+    if (clear_thrownobj)
+        thrownobj = (struct obj *) 0;
+    return;
 }
 
 /* an object may hit a monster; various factors adjust chance of hitting */
index ca2e1927654cd97ce64ff7dd0b84a6ece3148661..8f9de1f4a86fb52ec5ddae23628e40714f39b7c0 100644 (file)
--- a/src/mon.c
+++ b/src/mon.c
@@ -1,4 +1,4 @@
-/* NetHack 3.6 mon.c   $NHDT-Date: 1565833749 2019/08/15 01:49:09 $  $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.296 $ */
+/* NetHack 3.6 mon.c   $NHDT-Date: 1569276991 2019/09/23 22:16:31 $  $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.297 $ */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /*-Copyright (c) Derek S. Ray, 2015. */
 /* NetHack may be freely redistributed.  See license for details. */
@@ -2370,7 +2370,9 @@ int xkill_flags; /* 1: suppress message, 2: suppress corpse, 4: pacifist */
     if (mtmp->mtame && !mtmp->isminion)
         EDOG(mtmp)->killed_by_u = 1;
 
-    if (wasinside && thrownobj && thrownobj != uball) {
+    if (wasinside && thrownobj && thrownobj != uball
+        /* don't give to mon if missile is going to return to hero */
+        && thrownobj != (struct obj *) iflags.returning_missile) {
         /* thrown object has killed hero's engulfer; add it to mon's
            inventory now so that it will be placed with mon's other
            stuff prior to lookhere/autopickup when hero is expelled