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.
This commit is contained in:
Michael Meyer
2023-11-11 01:17:54 -08:00
committed by PatR
parent ee0247ac5f
commit d42564bacd
3 changed files with 58 additions and 60 deletions
+2 -1
View File
@@ -667,6 +667,7 @@ extern boolean harmless_missile(struct obj *);
extern boolean throwing_weapon(struct obj *); extern boolean throwing_weapon(struct obj *);
extern void throwit(struct obj *, long, boolean, struct obj *); extern void throwit(struct obj *, long, boolean, struct obj *);
extern int omon_adj(struct monst *, struct obj *, boolean); 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 thitmonst(struct monst *, struct obj *);
extern int hero_breaks(struct obj *, coordxy, coordxy, unsigned); extern int hero_breaks(struct obj *, coordxy, coordxy, unsigned);
extern int breaks(struct obj *, coordxy, coordxy); extern int breaks(struct obj *, coordxy, coordxy);
@@ -1824,7 +1825,7 @@ extern void Delay(int);
extern const char *rnd_hallublast(void); extern const char *rnd_hallublast(void);
extern boolean m_has_launcher_and_ammo(struct monst *); extern boolean m_has_launcher_and_ammo(struct monst *);
extern int thitu(int, int, struct obj **, const char *); 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 void thrwmu(struct monst *);
extern int spitmu(struct monst *, struct attack *); extern int spitmu(struct monst *, struct attack *);
extern int breamu(struct monst *, struct attack *); extern int breamu(struct monst *, struct attack *);
+34 -28
View File
@@ -1926,6 +1926,35 @@ tmiss(struct obj *obj, struct monst *mon, boolean maybe_wakeup)
|| (obj->otyp == FAKE_AMULET_OF_YENDOR && !obj->known)) \ || (obj->otyp == FAKE_AMULET_OF_YENDOR && !obj->known)) \
&& mon->m_id == gq.quest_status.leader_m_id) && 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. * Object thrown by player arrives at monster's location.
* Return 1 if obj has disappeared or otherwise been taken care of, * Return 1 if obj has disappeared or otherwise been taken care of,
@@ -2141,34 +2170,11 @@ thitmonst(
/* projectiles other than magic stones sometimes disappear /* projectiles other than magic stones sometimes disappear
when thrown; projectiles aren't among the types of weapon when thrown; projectiles aren't among the types of weapon
that hmon() might have destroyed so obj is intact */ that hmon() might have destroyed so obj is intact */
if (objects[otyp].oc_skill < P_NONE if (should_mulch_missile(obj)) {
&& objects[otyp].oc_skill > -P_BOOMERANG if (*u.ushops || obj->unpaid)
&& !objects[otyp].oc_magic) { check_shop_obj(obj, gb.bhitpos.x, gb.bhitpos.y, TRUE);
/* we were breaking 2/3 of everything unconditionally. obfree(obj, (struct obj *) 0);
* we still don't want anything to survive unconditionally, return 1;
* 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;
}
} }
passive_obj(mon, obj, (struct attack *) 0); passive_obj(mon, obj, (struct attack *) 0);
} else { } else {
+22 -31
View File
@@ -9,7 +9,7 @@ static int monmulti(struct monst *, struct obj *, struct obj *);
static void monshoot(struct monst *, struct obj *, struct obj *); static void monshoot(struct monst *, struct obj *, struct obj *);
static boolean ucatchgem(struct obj *, struct monst *); static boolean ucatchgem(struct obj *, struct monst *);
static const char* breathwep_name(int); 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 *); static int m_lined_up(struct monst *, struct monst *);
#define URETREATING(x, y) \ #define URETREATING(x, y) \
@@ -152,51 +152,43 @@ thitu(
/* Be sure this corresponds with what happens to player-thrown objects in /* Be sure this corresponds with what happens to player-thrown objects in
* dothrow.c (for consistency). --KAA * 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( drop_throw(
register struct obj *obj, register struct obj *obj,
boolean ohit, boolean ohit,
coordxy x, coordxy x,
coordxy y) coordxy y)
{ {
int retvalu = 1; boolean broken;
int create;
struct monst *mtmp;
struct trap *t;
if (obj->otyp == CREAM_PIE || obj->oclass == VENOM_CLASS if (obj->otyp == CREAM_PIE || obj->oclass == VENOM_CLASS
|| (ohit && obj->otyp == EGG)) || (ohit && obj->otyp == EGG)) {
create = 0; broken = TRUE;
else if (ohit && (is_multigen(obj) || obj->otyp == ROCK)) } else {
create = !rn2(3); broken = (ohit && should_mulch_missile(obj));
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;
if (broken) {
delobj(obj);
} else {
if (down_gate(x, y) != -1) if (down_gate(x, y) != -1)
objgone = ship_object(obj, x, y, FALSE); broken = ship_object(obj, x, y, FALSE);
if (!objgone) { if (!broken) {
if (!flooreffects(obj, x, y, "fall")) { struct monst *mtmp = m_at(x, y);
if (!(broken = flooreffects(obj, x, y, "fall"))) {
place_object(obj, x, y); place_object(obj, x, y);
if (!mtmp && u_at(x, y)) if (!mtmp && u_at(x, y))
mtmp = &gy.youmonst; mtmp = &gy.youmonst;
if (mtmp && ohit) if (mtmp && ohit)
passive_obj(mtmp, obj, (struct attack *) 0); passive_obj(mtmp, obj, (struct attack *) 0);
stackobj(obj); stackobj(obj);
retvalu = 0;
} }
} }
} else {
delobj(obj);
} }
gt.thrownobj = 0; gt.thrownobj = 0;
return retvalu; return broken;
} }
/* calculate multishot volley count for mtmp throwing otmp (if not ammo) or /* 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) 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 can anger the monster, if this happened due to hero (eg. exploding
bag of holding throwing the items) */ bag of holding throwing the items) */
int boolean
ohitmon( ohitmon(
struct monst *mtmp, /* accidental target, located at <gb.bhitpos.x,.y> */ struct monst *mtmp, /* accidental target, located at <gb.bhitpos.x,.y> */
struct obj *otmp, /* missile; might be destroyed by drop_throw */ 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 */ boolean verbose)/* give message(s) even when you can't see what happened */
{ {
int damage, tmp; int damage, tmp;
boolean vis, ismimic; boolean vis, ismimic, objgone;
int objgone = 1;
struct obj *mon_launcher = gm.marcher ? MON_WEP(gm.marcher) : NULL; struct obj *mon_launcher = gm.marcher ? MON_WEP(gm.marcher) : NULL;
gn.notonhead = (gb.bhitpos.x != mtmp->mx || gb.bhitpos.y != mtmp->my); 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); objgone = drop_throw(otmp, 1, gb.bhitpos.x, gb.bhitpos.y);
if (!objgone && range == -1) { /* special case */ if (!objgone && range == -1) { /* special case */
obj_extract_self(otmp); /* free it for motion again */ 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 */ /* hero catches gem thrown by mon iff poly'd into unicorn; might drop it */