+STATIC_OVL struct monst *
+find_targ(mtmp, dx, dy, maxdist)
+register struct monst *mtmp;
+int dx, dy;
+int maxdist;
+{
+ struct monst *targ = 0;
+ int curx = mtmp->mx, cury = mtmp->my;
+ int dist = 0;
+
+ /* Walk outwards */
+ for ( ; dist < maxdist; ++dist) {
+ curx += dx;
+ cury += dy;
+ if (!isok(curx, cury))
+ break;
+
+ /* FIXME: Check if we hit a wall/door/boulder to
+ * short-circuit unnecessary subsequent checks
+ */
+
+ /* If we can't see up to here, forget it - will this
+ * mean pets in corridors don't breathe at monsters
+ * in rooms? If so, is that necessarily bad?
+ */
+ if (!m_cansee(mtmp, curx, cury))
+ break;
+
+ if (curx == mtmp->mux && cury == mtmp->muy)
+ return &youmonst;
+
+ if ((targ = m_at(curx, cury)) != 0) {
+ /* Is the monster visible to the pet? */
+ if ((!targ->minvis || perceives(mtmp->data))
+ && !targ->mundetected)
+ break;
+ /* If the pet can't see it, it assumes it aint there */
+ targ = 0;
+ }
+ }
+ return targ;
+}
+
+STATIC_OVL int
+find_friends(mtmp, mtarg, maxdist)
+struct monst *mtmp, *mtarg;
+int maxdist;
+{
+ struct monst *pal;
+ int dx = sgn(mtarg->mx - mtmp->mx),
+ dy = sgn(mtarg->my - mtmp->my);
+ int curx = mtarg->mx, cury = mtarg->my;
+ int dist = distmin(mtarg->mx, mtarg->my, mtmp->mx, mtmp->my);
+
+ for ( ; dist <= maxdist; ++dist) {
+ curx += dx;
+ cury += dy;
+
+ if (!isok(curx, cury))
+ return 0;
+
+ /* If the pet can't see beyond this point, don't
+ * check any farther
+ */
+ if (!m_cansee(mtmp, curx, cury))
+ return 0;
+
+ /* Does pet think you're here? */
+ if (mtmp->mux == curx && mtmp->muy == cury)
+ return 1;
+
+ pal = m_at(curx, cury);
+
+ if (pal) {
+ if (pal->mtame) {
+ /* Pet won't notice invisible pets */
+ if (!pal->minvis || perceives(mtmp->data))
+ return 1;
+ } else {
+ /* Quest leaders and guardians are always seen */
+ if (pal->data->msound == MS_LEADER
+ || pal->data->msound == MS_GUARDIAN)
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+STATIC_OVL long
+score_targ(mtmp, mtarg)
+struct monst *mtmp, *mtarg;
+{
+ long score = 0L;
+
+ /* If the monster is confused, normal scoring is disrupted -
+ * anything may happen
+ */
+
+ /* Give 1 in 3 chance of safe breathing even if pet is confused or
+ * if you're on the quest start level */
+ if (!mtmp->mconf || !rn2(3) || Is_qstart(&u.uz)) {
+ int mtmp_lev;
+ aligntyp align1 = A_NONE, align2 = A_NONE; /* For priests, minions */
+ boolean faith1 = TRUE, faith2 = TRUE;
+
+ if (mtmp->isminion)
+ align1 = EMIN(mtmp)->min_align;
+ else if (mtmp->ispriest)
+ align1 = EPRI(mtmp)->shralign;
+ else
+ faith1 = FALSE;
+ if (mtarg->isminion)
+ align2 = EMIN(mtarg)->min_align; /* MAR */
+ else if (mtarg->ispriest)
+ align2 = EPRI(mtarg)->shralign; /* MAR */
+ else
+ faith2 = FALSE;
+
+ /* Never target quest friendlies */
+ if (mtarg->data->msound == MS_LEADER
+ || mtarg->data->msound == MS_GUARDIAN)
+ return -5000L;
+ /* D: Fixed angelic beings using gaze attacks on coaligned priests */
+ if (faith1 && faith2 && align1 == align2 && mtarg->mpeaceful) {
+ score -= 5000L;
+ return score;
+ }
+ /* Is monster adjacent? */
+ if (distmin(mtmp->mx, mtmp->my, mtarg->mx, mtarg->my) <= 1) {
+ score -= 3000L;
+ return score;
+ }
+ /* Is the monster peaceful or tame? */
+ if (/*mtarg->mpeaceful ||*/ mtarg->mtame || mtarg == &youmonst) {
+ /* Pets will never be targeted */
+ score -= 3000L;
+ return score;
+ }
+ /* Is master/pet behind monster? Check up to 15 squares beyond pet. */
+ if (find_friends(mtmp, mtarg, 15)) {
+ score -= 3000L;
+ return score;
+ }
+ /* Target hostile monsters in preference to peaceful ones */
+ if (!mtarg->mpeaceful)
+ score += 10;
+ /* Is the monster passive? Don't waste energy on it, if so */
+ if (mtarg->data->mattk[0].aatyp == AT_NONE)
+ score -= 1000;
+ /* Even weak pets with breath attacks shouldn't take on very
+ low-level monsters. Wasting breath on lichens is ridiculous. */
+ if ((mtarg->m_lev < 2 && mtmp->m_lev > 5)
+ || (mtmp->m_lev > 12 && mtarg->m_lev < mtmp->m_lev - 9
+ && u.ulevel > 8 && mtarg->m_lev < u.ulevel - 7))
+ score -= 25;
+ /* for strength purposes, a vampshifter in weak form (vampire bat,
+ fog cloud, maybe wolf) will attack as if in vampire form;
+ otherwise if won't do much and usually wouldn't suffer enough
+ damage (from counterattacks) to switch back to vampire form;
+ make it be more aggressive by behaving as if stronger */
+ mtmp_lev = mtmp->m_lev;
+ if (is_vampshifter(mtmp) && mtmp->data->mlet != S_VAMPIRE) {
+ /* is_vampshifter() implies (mtmp->cham >= LOW_PM) */
+ mtmp_lev = mons[mtmp->cham].mlevel;
+ /* actual vampire level would range from 1.0*mlvl to 1.5*mlvl */
+ mtmp_lev += rn2(mtmp_lev / 2 + 1);
+ /* we don't expect actual level in weak form to exceed
+ base level of strong form, but handle that if it happens */
+ if (mtmp->m_lev > mtmp_lev)
+ mtmp_lev = mtmp->m_lev;
+ }
+ /* And pets will hesitate to attack vastly stronger foes.
+ This penalty will be discarded if master's in trouble. */
+ if (mtarg->m_lev > mtmp_lev + 4L)
+ score -= (mtarg->m_lev - mtmp_lev) * 20L;
+ /* All things being the same, go for the beefiest monster. This
+ bonus should not be large enough to override the pet's aversion
+ to attacking much stronger monsters. */
+ score += mtarg->m_lev * 2 + mtarg->mhp / 3;
+ }
+ /* Fuzz factor to make things less predictable when very
+ similar targets are abundant. */
+ score += rnd(5);
+ /* Pet may decide not to use ranged attack when confused */
+ if (mtmp->mconf && !rn2(3))
+ score -= 1000;
+ return score;
+}
+
+STATIC_OVL struct monst *
+best_target(mtmp)
+struct monst *mtmp; /* Pet */
+{
+ int dx, dy;
+ long bestscore = -40000L, currscore;
+ struct monst *best_targ = 0, *temp_targ = 0;
+
+ /* Help! */
+ if (!mtmp)
+ return 0;
+
+ /* If the pet is blind, it's not going to see any target */
+ if (!mtmp->mcansee)
+ return 0;
+
+ /* Search for any monsters lined up with the pet, within an arbitrary
+ * distance from the pet (7 squares, even along diagonals). Monsters
+ * are assigned scores and the best score is chosen.
+ */
+ for (dy = -1; dy < 2; ++dy) {
+ for (dx = -1; dx < 2; ++dx) {
+ if (!dx && !dy)
+ continue;
+ /* Traverse the line to find the first monster within 7
+ * squares. Invisible monsters are skipped (if the
+ * pet doesn't have see invisible).
+ */
+ temp_targ = find_targ(mtmp, dx, dy, 7);
+
+ /* Nothing in this line? */
+ if (!temp_targ)
+ continue;
+
+ /* Decide how attractive the target is */
+ currscore = score_targ(mtmp, temp_targ);
+
+ if (currscore > bestscore) {
+ bestscore = currscore;
+ best_targ = temp_targ;
+ }
+ }
+ }
+
+ /* Filter out targets the pet doesn't like */
+ if (bestscore < 0L)
+ best_targ = 0;
+
+ return best_targ;
+}
+