From 9a9f2d596d2e1b5f7581da1c2b8be25668540eaf Mon Sep 17 00:00:00 2001 From: PatR Date: Sat, 24 Dec 2022 00:27:59 -0800 Subject: [PATCH] fix boomerang equiped in multiple slots Reported directly to devteam: with one quivered boomerang and a compatable stack of one or more boomerangs either wielded or in the alternate weapon slot, throwing the quivered one, failing to hit any target or obstacle, and catching the returning boomerang would empty the quiver, merge the caught boomerang with the wielded weapon or swap-weapon slot, then re-quiver and yield |x - 2 boomerangs (wielded) (at the ready) or |y - 2 boomerangs (alternate weapon; not wielded) (at the ready) If 'sanity_check' was On, complaints would ensue. 'autoquiver' may need to be On in addition to the thrown boomerang being the last item in the quiver. The unsplit portion of the fix feels unclean. There's bound to be a better way. --- src/dothrow.c | 80 +++++++++++++++++++++++++++++++++++++++------------ src/invent.c | 5 +--- src/mkobj.c | 11 +++++-- 3 files changed, 71 insertions(+), 25 deletions(-) diff --git a/src/dothrow.c b/src/dothrow.c index 1a1bfc3ea..83a83ff95 100644 --- a/src/dothrow.c +++ b/src/dothrow.c @@ -13,13 +13,15 @@ static int throw_ok(struct obj *); static void autoquiver(void); static struct obj *find_launcher(struct obj *); static int gem_accept(struct monst *, struct obj *); +static boolean harmless_missile(struct obj *); +static boolean toss_up(struct obj *, boolean); +static void sho_obj_return_to_u(struct obj * obj); +static struct obj *return_throw_to_inv(struct obj *, long, boolean, + struct obj *); static void tmiss(struct obj *, struct monst *, boolean); static int throw_gold(struct obj *); static void check_shop_obj(struct obj *, coordxy, coordxy, boolean); -static boolean harmless_missile(struct obj *); static void breakmsg(struct obj *, boolean); -static boolean toss_up(struct obj *, boolean); -static void sho_obj_return_to_u(struct obj * obj); static boolean mhurtle_step(genericptr_t, coordxy, coordxy); /* uwep might already be removed from inventory so test for W_WEP instead; @@ -1413,7 +1415,7 @@ void throwit(struct obj *obj, long wep_mask, /* used to re-equip returning boomerang */ boolean twoweap, /* used to restore twoweapon mode if - wielded weapon returns */ + * wielded weapon returns */ struct obj *oldslot) /* for thrown-and-return used with !fixinv */ { register struct monst *mon; @@ -1485,12 +1487,7 @@ throwit(struct obj *obj, && !impaired) { pline("%s the %s and returns to your hand!", Tobjnam(obj, "hit"), ceiling(u.ux, u.uy)); - obj = addinv_before(obj, oldslot); - (void) encumber_msg(); - if (obj->owornmask & W_QUIVER) /* in case addinv() autoquivered */ - setuqwep((struct obj *) 0); - setuwep(obj); - set_twoweap(twoweap); /* u.twoweap = twoweap */ + obj = return_throw_to_inv(obj, wep_mask, twoweap, oldslot); } else if (u.dz < 0) { (void) toss_up(obj, rn2(5) && !Underwater); } else if (u.dz > 0 && u.usteed && obj->oclass == POTION_CLASS @@ -1507,17 +1504,11 @@ throwit(struct obj *obj, } else if (obj->otyp == BOOMERANG && !Underwater) { if (Is_airlevel(&u.uz) || Levitation) hurtle(-u.dx, -u.dy, 1, TRUE); - iflags.returning_missile = 0; /* doesn't return if it hits monster */ mon = boomhit(obj, u.dx, u.dy); + iflags.returning_missile = 0; /* has returned or isn't going to */ if (mon == &gy.youmonst) { /* the thing was caught */ exercise(A_DEX, TRUE); - obj = addinv_before(obj, oldslot); - (void) encumber_msg(); - if (wep_mask && !(obj->owornmask & wep_mask)) { - setworn(obj, wep_mask); - /* moot; can no longer two-weapon with missile(s) */ - set_twoweap(twoweap); /* u.twoweap = twoweap */ - } + obj = return_throw_to_inv(obj, wep_mask, twoweap, oldslot); clear_thrownobj = TRUE; goto throwit_return; } @@ -1768,6 +1759,59 @@ throwit(struct obj *obj, return; } +static struct obj * +return_throw_to_inv( + struct obj *obj, + long wep_mask, + boolean twoweap, + struct obj *oldslot) +{ + struct obj *otmp = NULL; + + /* if 'obj' is from a stack split, we can put it back by undoing split + so there's no chance of merging with some other compatable stack */ + if (obj->o_id == gc.context.objsplit.parent_oid + || obj->o_id == gc.context.objsplit.child_oid) { + obj->nobj = gi.invent; + gi.invent = obj; + obj->where = OBJ_INVENT; + otmp = unsplitobj(obj); + if (!otmp) { + gi.invent = obj->nobj; + obj->nobj = 0; + obj->where = OBJ_FREE; + } else { + obj = otmp; + } + } + + /* if 'obj' wasn't from a stack split or if it wouldn't merge back + (maybe new erosion damage?) then it needs to be added to invent; + don't merge with any other stack even if there is a compatable one + (others with similar erosion?) */ + if (!otmp) { + obj->nomerge = 1; + obj = addinv_before(obj, oldslot); + obj->nomerge = 0; + + /* in case addinv() autoquivered */ + if ((obj->owornmask & (W_WEP | W_SWAPWEP)) != 0 + && (obj->owornmask & W_QUIVER) != 0) + setuqwep((struct obj *) 0); + + if ((wep_mask & W_WEP) && !uwep) + setuwep(obj); + else if ((wep_mask & W_SWAPWEP) && !uswapwep) + setuswapwep(obj); + else if ((wep_mask & W_QUIVER) && !uquiver) + setuqwep(obj); + } + + (void) encumber_msg(); + set_twoweap(twoweap); /* u.twoweap = twoweap */ + return obj; +} + /* an object may hit a monster; various factors adjust chance of hitting */ int omon_adj(struct monst *mon, struct obj *obj, boolean mon_notices) diff --git a/src/invent.c b/src/invent.c index a36762de0..39ea9a795 100644 --- a/src/invent.c +++ b/src/invent.c @@ -4524,10 +4524,7 @@ mergable( if (obj->oartifact != otmp->oartifact) return FALSE; - if (obj->known == otmp->known || !objects[otmp->otyp].oc_uses_known) { - return (boolean) objects[obj->otyp].oc_merge; - } else - return FALSE; + return (obj->known == otmp->known) ? TRUE : FALSE; } /* the #showgold command */ diff --git a/src/mkobj.c b/src/mkobj.c index fc8682518..79013ce7f 100644 --- a/src/mkobj.c +++ b/src/mkobj.c @@ -2737,15 +2737,20 @@ obj_sanity_check(void) /* objects temporarily freed from invent/floor lists; they should have arrived somewhere by the time we get called */ if (gt.thrownobj) - insane_object(gt.thrownobj, ofmt3, "gt.thrownobj sanity", + insane_object(gt.thrownobj, ofmt3, "thrownobj sanity", (struct monst *) 0); if (gk.kickedobj) - insane_object(gk.kickedobj, ofmt3, "gk.kickedobj sanity", + insane_object(gk.kickedobj, ofmt3, "kickedobj sanity", + (struct monst *) 0); + /* returning_missile temporarily remembers thrownobj and should be + Null in between moves */ + if (iflags.returning_missile) + insane_object(gk.kickedobj, ofmt3, "returning_missile sanity", (struct monst *) 0); /* gc.current_wand isn't removed from invent while in use, but should be Null between moves when we're called */ if (gc.current_wand) - insane_object(gc.current_wand, ofmt3, "gc.current_wand sanity", + insane_object(gc.current_wand, ofmt3, "current_wand sanity", (struct monst *) 0); }