]> granicus.if.org Git - nethack/commitdiff
fix boomerang equiped in multiple slots
authorPatR <rankin@nethack.org>
Sat, 24 Dec 2022 08:27:59 +0000 (00:27 -0800)
committerPatR <rankin@nethack.org>
Sat, 24 Dec 2022 08:27:59 +0000 (00:27 -0800)
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
src/invent.c
src/mkobj.c

index 1a1bfc3eafead7f149fdb06f523caa93978857f0..83a83ff95c0a61428cea7d41afbbd78dfaa0cbc8 100644 (file)
@@ -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)
index a36762de0ab6244a36e145de6c9c33a732cc48b4..39ea9a795bf1f5b8feea06b2db875a862a4f453b 100644 (file)
@@ -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 */
index fc86825181168b09705cfb3eda7614cb49decc15..79013ce7fecd2b294bf1e02f5cab9431833c7891 100644 (file)
@@ -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);
 }