diff --git a/src/dogmove.c b/src/dogmove.c index 3833ee626..ce4ab4a73 100644 --- a/src/dogmove.c +++ b/src/dogmove.c @@ -18,6 +18,7 @@ static int dog_goal(struct monst *, struct edog *, int, int, int); static struct monst *find_targ(struct monst *, int, int, int); static int find_friends(struct monst *, struct monst *, int); static struct monst *best_target(struct monst *); +static int pet_ranged_attk(struct monst *); static long score_targ(struct monst *, struct monst *); static boolean can_reach_location(struct monst *, coordxy, coordxy, coordxy, coordxy); @@ -854,6 +855,81 @@ best_target(struct monst *mtmp) /* Pet */ return best_targ; } +/* Pet considers and maybe executes a ranged attack */ +static int +pet_ranged_attk(struct monst *mtmp) +{ + struct monst *mtarg; + int hungry = 0; + + /* How hungry is the pet? */ + if (!mtmp->isminion) { + struct edog *dog = EDOG(mtmp); + + hungry = (gm.moves > (dog->hungrytime + DOG_HUNGRY)); + } + + /* Identify the best target in a straight line from the pet; + * if there is such a target, we'll let the pet attempt an attack. + */ + mtarg = best_target(mtmp); + + /* Hungry pets are unlikely to use breath/spit attacks */ + if (mtarg && (!hungry || !rn2(5))) { + int mstatus = MM_MISS; + + if (mtarg == &gy.youmonst) { + if (mattacku(mtmp)) + return MMOVE_DIED; + /* Treat this as the pet having initiated an attack even if it + * didn't, so it will lose its move. This isn't entirely fair, + * but mattacku doesn't distinguish between "did not attack" + * and "attacked but didn't die" cases, and this is preferable + * to letting the pet attack the player and continuing to move. + */ + mstatus = MM_HIT; + } else { + mstatus = mattackm(mtmp, mtarg); + + /* Shouldn't happen, really */ + if (mstatus & MM_AGR_DIED) + return MMOVE_DIED; + + /* Allow the targeted nasty to strike back - if + * the targeted beast doesn't have a ranged attack, + * nothing will happen. + */ + if ((mstatus & MM_HIT) && !(mstatus & MM_DEF_DIED) + && rn2(4) && mtarg != &gy.youmonst) { + + /* Can monster see? If it can, it can retaliate + * even if the pet is invisible, since it'll see + * the direction from which the ranged attack came; + * if it's blind or unseeing, it can't retaliate + */ + if (mtarg->mcansee && haseyes(mtarg->data)) { + mstatus = mattackm(mtarg, mtmp); + if (mstatus & MM_DEF_DIED) + return MMOVE_DIED; + } + } + } + /* Only return 3 if the pet actually made a ranged attack, and + * thus should lose the rest of its move. + * There's a chain of assumptions here: + * 1. score_targ and best_target will never select a monster + * that can be attacked in melee, so the mattackm call can + * only ever try ranged options + * 2. if the only attacks available to mattackm are ranged + * options, and the monster cannot make a ranged attack, it + * will return MM_MISS. + */ + if (mstatus != MM_MISS) + return MMOVE_DONE; + } + return MMOVE_NOTHING; +} + /* Return values (same as m_move): * 0: did not move, but can still attack and do other stuff. * 1: moved, possibly can attack. @@ -1130,76 +1206,8 @@ dog_move(register struct monst *mtmp, * now's the time for ranged attacks. Note that the pet can move * after it performs its ranged attack. Should this be changed? */ - { - struct monst *mtarg; - int hungry = 0; - - /* How hungry is the pet? */ - if (!mtmp->isminion) { - struct edog *dog = EDOG(mtmp); - - hungry = (gm.moves > (dog->hungrytime + DOG_HUNGRY)); - } - - /* Identify the best target in a straight line from the pet; - * if there is such a target, we'll let the pet attempt an attack. - */ - mtarg = best_target(mtmp); - - /* Hungry pets are unlikely to use breath/spit attacks */ - if (mtarg && (!hungry || !rn2(5))) { - int mstatus = MM_MISS; - - if (mtarg == &gy.youmonst) { - if (mattacku(mtmp)) - return MMOVE_DIED; - /* Treat this as the pet having initiated an attack even if it - * didn't, so it will lose its move. This isn't entirely fair, - * but mattacku doesn't distinguish between "did not attack" - * and "attacked but didn't die" cases, and this is preferable - * to letting the pet attack the player and continuing to move. - */ - mstatus = MM_HIT; - } else { - mstatus = mattackm(mtmp, mtarg); - - /* Shouldn't happen, really */ - if (mstatus & MM_AGR_DIED) - return MMOVE_DIED; - - /* Allow the targeted nasty to strike back - if - * the targeted beast doesn't have a ranged attack, - * nothing will happen. - */ - if ((mstatus & MM_HIT) && !(mstatus & MM_DEF_DIED) - && rn2(4) && mtarg != &gy.youmonst) { - - /* Can monster see? If it can, it can retaliate - * even if the pet is invisible, since it'll see - * the direction from which the ranged attack came; - * if it's blind or unseeing, it can't retaliate - */ - if (mtarg->mcansee && haseyes(mtarg->data)) { - mstatus = mattackm(mtarg, mtmp); - if (mstatus & MM_DEF_DIED) - return MMOVE_DIED; - } - } - } - /* Only return 3 if the pet actually made a ranged attack, and - * thus should lose the rest of its move. - * There's a chain of assumptions here: - * 1. score_targ and best_target will never select a monster - * that can be attacked in melee, so the mattackm call can - * only ever try ranged options - * 2. if the only attacks available to mattackm are ranged - * options, and the monster cannot make a ranged attack, it - * will return MM_MISS. - */ - if (mstatus != MM_MISS) - return MMOVE_DONE; - } - } + if ((i = pet_ranged_attk(mtmp)) != MMOVE_NOTHING) + return i; newdogpos: if (nix != omx || niy != omy) {