From d42564bacd2438937c937393b0ea5e77e320aa21 Mon Sep 17 00:00:00 2001 From: Michael Meyer Date: Thu, 9 Nov 2023 23:31:35 -0500 Subject: [PATCH] Better align drop_throw with hero ammo breakage There is a comment above the function indicating that it should be aligned with hero ammo breakage, but this wasn't the case. One big difference is that any monster-thrown or -shot object would be deleted unconditionally if it hit another monster trapped in a pit. I don't know why that was in there, but it's not present in hero ammo breakage chances, and it meant that a monster could sling the Mines luckstone at the hero, hit a monster in the pit, and permanently lock the hero out of getting the luckstone (as just happened to a player during the current tournament). This pulls the hero breakage rules out into their own function and uses that for monster breakage as well, to make sure they are aligned. I also refactored drop_throw a bit to reduce the number of separate variables tracking whether the object was deleted (was create, objgone, and retvalu), and changed its (and ohitmon's) type to boolean. --- include/extern.h | 3 ++- src/dothrow.c | 62 ++++++++++++++++++++++++++---------------------- src/mthrowu.c | 53 +++++++++++++++++------------------------ 3 files changed, 58 insertions(+), 60 deletions(-) diff --git a/include/extern.h b/include/extern.h index 22c374bbd..8cfe3ce65 100644 --- a/include/extern.h +++ b/include/extern.h @@ -667,6 +667,7 @@ extern boolean harmless_missile(struct obj *); extern boolean throwing_weapon(struct obj *); extern void throwit(struct obj *, long, boolean, struct obj *); extern int omon_adj(struct monst *, struct obj *, boolean); +extern boolean should_mulch_missile(struct obj *); extern int thitmonst(struct monst *, struct obj *); extern int hero_breaks(struct obj *, coordxy, coordxy, unsigned); extern int breaks(struct obj *, coordxy, coordxy); @@ -1824,7 +1825,7 @@ extern void Delay(int); extern const char *rnd_hallublast(void); extern boolean m_has_launcher_and_ammo(struct monst *); extern int thitu(int, int, struct obj **, const char *); -extern int ohitmon(struct monst *, struct obj *, int, boolean); +extern boolean ohitmon(struct monst *, struct obj *, int, boolean); extern void thrwmu(struct monst *); extern int spitmu(struct monst *, struct attack *); extern int breamu(struct monst *, struct attack *); diff --git a/src/dothrow.c b/src/dothrow.c index f2417169d..e0a2cc6d0 100644 --- a/src/dothrow.c +++ b/src/dothrow.c @@ -1926,6 +1926,35 @@ tmiss(struct obj *obj, struct monst *mon, boolean maybe_wakeup) || (obj->otyp == FAKE_AMULET_OF_YENDOR && !obj->known)) \ && mon->m_id == gq.quest_status.leader_m_id) +/* whether or not object should be destroyed when it hits its target */ +boolean +should_mulch_missile(struct obj *obj) +{ + boolean broken; + int chance; + + /* only ammo (excluding magic stones) or missiles will break */ + if (!obj || !(is_ammo(obj) || is_missile(obj)) + || objects[obj->otyp].oc_magic) + return FALSE; + + /* we had been breaking 2/3 of everything unconditionally. we still don't + want anything to survive unconditionally, but we need ammo to stay + around longer on average. */ + chance = 3 + greatest_erosion(obj) - obj->spe; + broken = chance > 1 ? rn2(chance) : !rn2(4); + if (obj->blessed && (gc.context.mon_moving ? !rn2(3) : !rnl(4))) + broken = FALSE; + + /* Flint and hard gems don't break easily */ + if (((obj->oclass == GEM_CLASS && objects[obj->otyp].oc_tough) + || obj->otyp == FLINT) + && !rn2(2)) + broken = FALSE; + + return broken; +} + /* * Object thrown by player arrives at monster's location. * Return 1 if obj has disappeared or otherwise been taken care of, @@ -2141,34 +2170,11 @@ thitmonst( /* projectiles other than magic stones sometimes disappear when thrown; projectiles aren't among the types of weapon that hmon() might have destroyed so obj is intact */ - if (objects[otyp].oc_skill < P_NONE - && objects[otyp].oc_skill > -P_BOOMERANG - && !objects[otyp].oc_magic) { - /* we were breaking 2/3 of everything unconditionally. - * we still don't want anything to survive unconditionally, - * but we need ammo to stay around longer on average. - */ - int broken, chance; - - chance = 3 + greatest_erosion(obj) - obj->spe; - if (chance > 1) - broken = rn2(chance); - else - broken = !rn2(4); - if (obj->blessed && !rnl(4)) - broken = 0; - - /* Flint and hard gems don't break easily */ - if (((obj->oclass == GEM_CLASS && objects[otyp].oc_tough) - || obj->otyp == FLINT) && !rn2(2)) - broken = 0; - - if (broken) { - if (*u.ushops || obj->unpaid) - check_shop_obj(obj, gb.bhitpos.x, gb.bhitpos.y, TRUE); - obfree(obj, (struct obj *) 0); - return 1; - } + if (should_mulch_missile(obj)) { + if (*u.ushops || obj->unpaid) + check_shop_obj(obj, gb.bhitpos.x, gb.bhitpos.y, TRUE); + obfree(obj, (struct obj *) 0); + return 1; } passive_obj(mon, obj, (struct attack *) 0); } else { diff --git a/src/mthrowu.c b/src/mthrowu.c index efe8411db..e1aec8426 100644 --- a/src/mthrowu.c +++ b/src/mthrowu.c @@ -9,7 +9,7 @@ static int monmulti(struct monst *, struct obj *, struct obj *); static void monshoot(struct monst *, struct obj *, struct obj *); static boolean ucatchgem(struct obj *, struct monst *); static const char* breathwep_name(int); -static int drop_throw(struct obj *, boolean, coordxy, coordxy); +static boolean drop_throw(struct obj *, boolean, coordxy, coordxy); static int m_lined_up(struct monst *, struct monst *); #define URETREATING(x, y) \ @@ -152,51 +152,43 @@ thitu( /* Be sure this corresponds with what happens to player-thrown objects in * dothrow.c (for consistency). --KAA - * Returns 0 if object still exists (not destroyed). + * Returns FALSE if object still exists (not destroyed). */ -static int +static boolean drop_throw( register struct obj *obj, boolean ohit, coordxy x, coordxy y) { - int retvalu = 1; - int create; - struct monst *mtmp; - struct trap *t; + boolean broken; if (obj->otyp == CREAM_PIE || obj->oclass == VENOM_CLASS - || (ohit && obj->otyp == EGG)) - create = 0; - else if (ohit && (is_multigen(obj) || obj->otyp == ROCK)) - create = !rn2(3); - else - create = 1; - - if (create && !((mtmp = m_at(x, y)) != 0 && mtmp->mtrapped - && (t = t_at(x, y)) != 0 - && is_pit(t->ttyp))) { - int objgone = 0; + || (ohit && obj->otyp == EGG)) { + broken = TRUE; + } else { + broken = (ohit && should_mulch_missile(obj)); + } + if (broken) { + delobj(obj); + } else { if (down_gate(x, y) != -1) - objgone = ship_object(obj, x, y, FALSE); - if (!objgone) { - if (!flooreffects(obj, x, y, "fall")) { + broken = ship_object(obj, x, y, FALSE); + if (!broken) { + struct monst *mtmp = m_at(x, y); + if (!(broken = flooreffects(obj, x, y, "fall"))) { place_object(obj, x, y); if (!mtmp && u_at(x, y)) mtmp = &gy.youmonst; if (mtmp && ohit) passive_obj(mtmp, obj, (struct attack *) 0); stackobj(obj); - retvalu = 0; } } - } else { - delobj(obj); } gt.thrownobj = 0; - return retvalu; + return broken; } /* calculate multishot volley count for mtmp throwing otmp (if not ammo) or @@ -318,7 +310,7 @@ monshoot(struct monst* mtmp, struct obj* otmp, struct obj* mwep) return 1 if the object has stopped moving (hit or its range used up) can anger the monster, if this happened due to hero (eg. exploding bag of holding throwing the items) */ -int +boolean ohitmon( struct monst *mtmp, /* accidental target, located at */ struct obj *otmp, /* missile; might be destroyed by drop_throw */ @@ -328,8 +320,7 @@ ohitmon( boolean verbose)/* give message(s) even when you can't see what happened */ { int damage, tmp; - boolean vis, ismimic; - int objgone = 1; + boolean vis, ismimic, objgone; struct obj *mon_launcher = gm.marcher ? MON_WEP(gm.marcher) : NULL; gn.notonhead = (gb.bhitpos.x != mtmp->mx || gb.bhitpos.y != mtmp->my); @@ -495,11 +486,11 @@ ohitmon( objgone = drop_throw(otmp, 1, gb.bhitpos.x, gb.bhitpos.y); if (!objgone && range == -1) { /* special case */ obj_extract_self(otmp); /* free it for motion again */ - return 0; + return FALSE; } - return 1; + return TRUE; } - return 0; + return FALSE; } /* hero catches gem thrown by mon iff poly'd into unicorn; might drop it */