From 3fdcd5f4e0b361847f1e5bea320c869f5d26c86e Mon Sep 17 00:00:00 2001 From: PatR Date: Tue, 19 Sep 2023 07:16:00 -0700 Subject: [PATCH] fix #K4006 - 'null obj after quiver merge' panic Using apply to unlight a lit potion of oil makes it unlit, removes it from inventory, and then re-adds it to try to force it to merge with other potions of oil. If it was wielded and the other potions were quivered, the game would panic. When merging, they get forced into the weapon slot in preference to the quiver slot. Unwearing it before freeinv+addinv would solve this but also leave the hero with nothing wielded, even if it didn't merge with another stack. Instead, don't try to merge if the potion being unlit happens to be worn. 3.6.x was subject to this too and the fix is small+isolated but the situation is so uncommon that I haven't bothered backporting it. Applying a lump of royal jelly and then not picking anything to rub it on had a similar problem. It also panicked if the applied lump was wielded and other lumps were quivered. The fix is different because the stack it gets split from during apply is known. This one doesn't impact 3.6.x; applying jelly to eggs wasn't implemented yet. --- doc/fixes3-7-0.txt | 6 ++++++ src/apply.c | 30 +++++++++++++++++++++--------- src/nhlua.c | 2 ++ 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 939947a17..6c72b4cdf 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1237,6 +1237,9 @@ ring of hunger prevents choking on your food paranoid_confirm:pray can be changed to require yes/no response instead of y/n by also setting paranoid_confirm:Confirm wand of probing reveals map locations in the ray path +applying a wielded, lit potion of oil to unlight it while other unlit + potion(s) of oil were quivered would trigger panic + "addinv: null obj after quiver merge otyp=N" where N is POT_OIL Fixes to 3.7.0-x General Problems Exposed Via git Repository @@ -1676,6 +1679,9 @@ using wizard mode #wizkill outside the endgame followed by m^V to enter the dwarf/elf/orc/gnome hero killed by zombie would rise as human zombie for bones paranoid_confirm:Autoall for menustyle:full has the test handling backward and was treating 'yes' as no and 'n' or ESC as 'y' +applying a wielded lump of royal jelly but not picking something to rub it on + while other lumps of jelly were quivered would trigger a panic, + "addinv: null obj after quiver merge" (like applying lit potion of oil) Fixes to 3.7.0-x Platform and/or Interface Problems Exposed Via git Repository diff --git a/src/apply.c b/src/apply.c index 1dead6b0e..579f9833c 100644 --- a/src/apply.c +++ b/src/apply.c @@ -1684,10 +1684,14 @@ light_cocktail(struct obj **optr) /* * Free & add to re-merge potion. This will average the * age of the potions. Not exactly the best solution, - * but its easy. + * but its easy. Don't do that unless obj is not worn (uwep, + * uswapwep, or uquiver) because if wielded and other oil is + * quivered a "null obj after quiver merge" panic will occur. */ - freeinv(obj); - *optr = addinv(obj); + if (!obj->owornmask) { + freeinv(obj); + *optr = addinv(obj); + } return; } else if (Underwater) { There("is not enough oxygen to sustain a fire."); @@ -1702,7 +1706,8 @@ light_cocktail(struct obj **optr) Blind ? "" : " It gives off a dim light."); if (obj->unpaid && costly_spot(u.ux, u.uy)) { - struct monst *shkp VOICEONLY = shop_keeper(*in_rooms(u.ux, u.uy, SHOPBASE)); + struct monst *shkp VOICEONLY = shop_keeper(*in_rooms(u.ux, u.uy, + SHOPBASE)); /* Normally, we shouldn't both partially and fully charge * for an item, but (Yendorian Fuel) Taxes are inevitable... @@ -3477,10 +3482,10 @@ use_royal_jelly(struct obj **optr) { int oldcorpsenm; unsigned was_timed; - struct obj *obj = *optr; - struct obj *eobj; + struct obj *eobj, *obj = *optr; + boolean splitit = (obj->quan > 1L); - if (obj->quan > 1L) + if (splitit) obj = splitobj(obj, 1L); /* remove from inventory so that it won't be offered as a choice to rub on itself */ @@ -3489,8 +3494,15 @@ use_royal_jelly(struct obj **optr) /* right now you can rub one royal jelly on an entire stack of eggs */ eobj = getobj("rub the royal jelly on", jelly_ok, GETOBJ_PROMPT); if (!eobj) { - addinv(obj); /* put the unused lump back; if it came from - * a split, it should merge back */ + if (splitit) { + (void) unsplitobj(obj); + update_inventory(); /* freeinv() updated perminv w/ obj omitted */ + } else { + /* this lump was already separate; pervent merge */ + obj->nomerge = 1; + addinv(obj); /* put the unused lump back; updates perminv */ + obj->nomerge = 0; + } return ECMD_CANCEL; } diff --git a/src/nhlua.c b/src/nhlua.c index 9a203f155..df77c0ba0 100644 --- a/src/nhlua.c +++ b/src/nhlua.c @@ -1583,7 +1583,9 @@ nhl_gamestate(lua_State *L) wornmask = otmp->owornmask; otmp->owornmask = 0L; extract_nobj(otmp, &gmst_invent); + otmp->nomerge = 1; addinv(otmp); + otmp->nomerge = 0; if (wornmask) setworn(otmp, wornmask); }