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.
This commit is contained in:
PatR
2022-12-24 00:27:59 -08:00
parent 9657b78359
commit 9a9f2d596d
3 changed files with 71 additions and 25 deletions

View 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)

View 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 */

View 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);
}