]> granicus.if.org Git - nethack/commitdiff
lost objects thrown by monsters
authorPatR <rankin@nethack.org>
Fri, 25 Feb 2022 02:10:52 +0000 (18:10 -0800)
committerPatR <rankin@nethack.org>
Fri, 25 Feb 2022 02:10:52 +0000 (18:10 -0800)
Reported by entrez:  if a monster or explosion kills the hero with an
object that has timers or is a light source, it could trigger a panic
when end of game cleanup can't find it because it has been removed
from the map or monster's inventory and not placed back on the map
yet.  This isn't much different from something thrown by hero which
had a similar situation dealt with a long time ago.  Fix by setting
'thrownobj' for monster-launched and explosion-launched missiles.
That way done_object_cleanup() called from really_done() will place the
missile on the map where saving bones or general cleanup can find it.

It doesn't bother dealing with exploding a lit potion of oil that
kills the hero by missile damage before the potion explodes.  If that
ends up in bones, it should still be lit and might blow up before the
new character reaches it.  (Not verified.)

The code for a hero polymorphed into a unicorn and catching a thrown
gem has been moved into its own routine.  No change in behavior, just
less clutter in the thrown-object-hits-hero section of the monster
throwing routine.

doc/fixes3-7-0.txt
src/explode.c
src/mthrowu.c

index df96a252482a8d4266b29d1fded0ba183b795f2a..efb0f3bb40e0cbfaea4bff1b1b07eedd625dcced 100644 (file)
@@ -819,6 +819,10 @@ discovering an object on first turn with persistent inventory enabled might
        or spellbook and read it as first action; it becomes discovered but
        will still be shown as if undiscovered until next inventory update)
 most traps now require touching the floor to trigger
+if a lit potion of oil on the floor was launched by an explosion and it hit
+       and killed the hero via missile damage rather than its own explosion,
+       it could trigger an "obj_is_local" panic when end of game cleanup
+       tried to extinguish it as a light source
 
 
 Fixes to 3.7.0-x Problems that Were Exposed Via git Repository
index 4068672674e401504568ab951bee88db73300f60..661eb1c1641811321c7244101ed25a1d089e08f9 100644 (file)
@@ -746,6 +746,7 @@ scatter(int sx, int sy,  /* location of objects to scatter */
     while (farthest-- > 0) {
         for (stmp = schain; stmp; stmp = stmp->next) {
             if ((stmp->range-- > 0) && (!stmp->stopped)) {
+                g.thrownobj = stmp->obj; /* mainly in case it kills hero */
                 g.bhitpos.x = stmp->ox + stmp->dx;
                 g.bhitpos.y = stmp->oy + stmp->dy;
                 typ = levl[g.bhitpos.x][g.bhitpos.y].typ;
@@ -794,6 +795,7 @@ scatter(int sx, int sy,  /* location of objects to scatter */
                 stmp->oy = g.bhitpos.y;
                 if (IS_SINK(levl[stmp->ox][stmp->oy].typ))
                     stmp->stopped = TRUE;
+                g.thrownobj = (struct obj *) 0;
             }
         }
     }
index a0cb795fff2fcc1edff5a7a8e08f098e4de76e6e..2fca18fb62bf76e436d079a5c88b9e924fa8f714 100644 (file)
@@ -7,6 +7,7 @@
 
 static int monmulti(struct monst *, struct obj *, struct obj *);
 static void monshoot(struct monst *, struct obj *, struct obj *);
+static boolean ucatchgem(struct obj *, struct monst *);
 static const char* breathwep_name(int);
 static int drop_throw(struct obj *, boolean, int, int);
 static int m_lined_up(struct monst *, struct monst *);
@@ -189,8 +190,10 @@ drop_throw(
                 retvalu = 0;
             }
         }
-    } else
+    } else {
         delobj(obj);
+    }
+    g.thrownobj = 0;
     return retvalu;
 }
 
@@ -491,6 +494,34 @@ ohitmon(
     return 0;
 }
 
+/* hero catches gem thrown by mon iff poly'd into unicorn; might drop it */
+static boolean
+ucatchgem(
+    struct obj *gem,   /* caller has verified gem->oclass */
+    struct monst *mon)
+{
+    /* won't catch rock or gray stone; catch (then drop) worthless glass */
+    if (gem->otyp <= LAST_GEM + NUM_GLASS_GEMS
+        && is_unicorn(g.youmonst.data)) {
+        char *gem_xname = xname(gem),
+             *mon_s_name = s_suffix(mon_nam(mon));
+
+        if (gem->otyp > LAST_GEM) {
+            You("catch the %s.", gem_xname);
+            You("are not interested in %s junk.", mon_s_name);
+            makeknown(gem->otyp);
+            dropy(gem);
+        } else {
+            You("accept %s gift in the spirit in which it was intended.",
+                mon_s_name);
+            (void) hold_another_object(gem, "You catch, but drop, %s.",
+                                       gem_xname, "You catch:");
+        }
+        return TRUE;
+    }
+    return FALSE;
+}
+
 #define MT_FLIGHTCHECK(pre,forcehit) \
     (/* missile hits edge of screen */                                  \
      !isok(g.bhitpos.x + dx, g.bhitpos.y + dy)                          \
@@ -550,6 +581,8 @@ m_throw(
         singleobj = splitobj(obj, 1L);
         obj_extract_self(singleobj);
     }
+    /* global pointer for missile object in OBJ_FREE state */
+    g.thrownobj = singleobj;
 
     singleobj->owornmask = 0; /* threw one of multiple weapons in hand? */
     if (!canseemon(mon))
@@ -585,8 +618,8 @@ m_throw(
     if (sym)
         tmp_at(DISP_FLASH, obj_to_glyph(singleobj, rn2_on_display_rng));
     while (range-- > 0) { /* Actually the loop is always exited by break */
-        g.bhitpos.x += dx;
-        g.bhitpos.y += dy;
+        singleobj->ox = g.bhitpos.x += dx;
+        singleobj->oy = g.bhitpos.y += dy;
         if (cansee(g.bhitpos.x, g.bhitpos.y))
             singleobj->dknown = 1;
 
@@ -602,29 +635,13 @@ m_throw(
             if (g.multi)
                 nomul(0);
 
-            if (singleobj->oclass == GEM_CLASS
-                && singleobj->otyp <= LAST_GEM + NUM_GLASS_GEMS
-                && is_unicorn(g.youmonst.data)) {
-                if (singleobj->otyp > LAST_GEM) {
-                    You("catch the %s.", xname(singleobj));
-                    You("are not interested in %s junk.",
-                        s_suffix(mon_nam(mon)));
-                    makeknown(singleobj->otyp);
-                    dropy(singleobj);
-                } else {
-                    You(
-                     "accept %s gift in the spirit in which it was intended.",
-                        s_suffix(mon_nam(mon)));
-                    (void) hold_another_object(singleobj,
-                                               "You catch, but drop, %s.",
-                                               xname(singleobj),
-                                               "You catch:");
-                }
-                break;
-            }
             if (singleobj->oclass == POTION_CLASS) {
                 potionhit(&g.youmonst, singleobj, POTHIT_MONST_THROW);
                 break;
+            } else if (singleobj->oclass == GEM_CLASS) {
+                /* hero might be poly'd into a unicorn */
+                if (ucatchgem(singleobj, mon))
+                    break;
             }
             oldumort = u.umortality;
 
@@ -748,6 +765,9 @@ m_throw(
         if (!Blind)
             Your1(vision_clears);
     }
+    /* note: all early returns follow drop_throw() which clears thrownobj */
+    g.thrownobj = 0;
+    return;
 }
 
 #undef MT_FLIGHTCHECK