fix K4318

Reported by paxed. A potion of oil, that was already in the midst of exploding,
got picked up through spot_effects(), which led to it merging with
another potion of oil and the freeing of the original obj.

The original obj pointer was still held by breakobj(), and breakobj()
proceeded to delete the obj (again).

Function nesting:

 1    spelleffects()
 2     -> weffects()
 3      -> bhit()
 4       -> bhitpile()
 5        -> bhito(obj ...)
 6         -> hero_breaks(obj ...)
 7          -> breakobj(obj ...)
 8           -> explode_oil(obj ...)
 9            -> splatter_burning_oil()
10             -> explode()
11              -> zap_over_floor()
12               -> melt_ice()
13                -> spot_effects()
14                 -> pickup()
15                  -> pickup_object(obj ...)
16                   -> pick_obj(obj ...)
17                    -> addinv(obj ...)
18                     -> addinv_core0(obj ...)
19                      -> merged(obj ...)
20                       -> obfree(obj ...)
21                        -> dealloc_obj(obj ...)

 8           -> delobj(obj ...)
 9            -> delobj_core(obj ...)
10             -> obfree(obj ...)
11              -> dealloc_obj(obj ...)
12               -> impossible("obj already deleted)

This marks the exploding potion with LOST_EXPLODING, so that it won't
get picked up, or merged with another object during the long
sequence of functions, and that should take care of 15-21 above.
This commit is contained in:
nhmall
2025-01-12 13:50:25 -05:00
parent a8be021913
commit bee21e3447
4 changed files with 13 additions and 6 deletions

View File

@@ -976,6 +976,7 @@ explode_oil(struct obj *obj, coordxy x, coordxy y)
if (!obj->lamplit)
impossible("exploding unlit oil");
end_burn(obj, TRUE);
obj->how_lost = LOST_EXPLODING;
splatter_burning_oil(x, y, diluted_oil);
}

View File

@@ -4943,6 +4943,8 @@ mergable(
if (obj->cursed != otmp->cursed || obj->blessed != otmp->blessed)
return FALSE;
if ((obj->how_lost & LOSTOVERRIDEMASK) != 0)
return FALSE;
#if 0 /* don't require 'bypass' to match; that results in items dropped
* via 'D' not stacking with compatible items already on the floor;
* caller who wants that behavior should use 'nomerge' instead */

View File

@@ -950,6 +950,8 @@ autopick_testobj(struct obj *otmp, boolean calc_costly)
return TRUE;
if (flags.nopick_dropped && otmp->how_lost == LOST_DROPPED)
return FALSE;
if (otmp->how_lost == LOST_EXPLODING)
return FALSE;
/* check for pickup_types */
pickit = (!*otypes || strchr(otypes, otmp->oclass));
@@ -1866,7 +1868,7 @@ pickup_object(
couldn't pick up a thrown, stolen, or dropped item that was split
off from a carried stack even while still carrying the rest of the
stack unless we have at least one free slot available */
obj->how_lost = LOST_NONE; /* affects merge_choice() */
obj->how_lost &= ~LOSTOVERRIDEMASK; /* affects merge_choice() */
res = lift_object(obj, (struct obj *) 0, &count, telekinesis);
obj->how_lost = save_how_lost; /* even when res > 0,
* in case we call splitobj() below */
@@ -1879,7 +1881,7 @@ pickup_object(
if (obj->quan != count && obj->otyp != LOADSTONE)
obj = splitobj(obj, count);
obj->how_lost = LOST_NONE;
obj->how_lost &= ~LOSTOVERRIDEMASK;
obj = pick_obj(obj);
if (uwep && uwep == obj)