diff --git a/src/monmove.c b/src/monmove.c index 290274558..f3b760a0d 100644 --- a/src/monmove.c +++ b/src/monmove.c @@ -1100,19 +1100,22 @@ maybe_spin_web(struct monst *mtmp) } } +/* max distmin() distance for monster to look for items */ #define SQSRCHRADIUS 5 +/* monster looks for items it wants nearby */ static boolean m_search_items(struct monst *mtmp, coordxy *ggx, coordxy *ggy, schar *mmoved, int *appr) { register int minr = SQSRCHRADIUS; /* not too far away */ register struct obj *otmp; register coordxy xx, yy; - coordxy oomx, oomy, lmx, lmy; + coordxy hmx, hmy, lmx, lmy; struct trap *ttmp; coordxy omx = mtmp->mx, omy = mtmp->my; struct permonst *ptr = mtmp->data; struct monst *mtoo; + boolean costly; /* cut down the search radius if it thinks character is closer. */ if (distmin(mtmp->mux, mtmp->muy, omx, omy) < SQSRCHRADIUS @@ -1122,76 +1125,93 @@ m_search_items(struct monst *mtmp, coordxy *ggx, coordxy *ggy, schar *mmoved, in if (!mtmp->mpeaceful && is_mercenary(ptr)) minr = 1; - if ((!*in_rooms(omx, omy, SHOPBASE) || (!rn2(25) && !mtmp->isshk))) { - oomx = min(COLNO - 1, omx + minr); - oomy = min(ROWNO - 1, omy + minr); - lmx = max(1, omx - minr); - lmy = max(0, omy - minr); - for (otmp = fobj; otmp; otmp = otmp->nobj) { - /* monsters may pick rocks up, but won't go out of their way - to grab them; this might hamper sling wielders, but it cuts - down on move overhead by filtering out most common item */ - if (otmp->otyp == ROCK) - continue; - /* avoid special items; once hero picks them up, they'll - cease being special */ - if (is_mines_prize(otmp) || is_soko_prize(otmp)) - continue; + /* in shop, usually skip */ + if (*in_rooms(omx, omy, SHOPBASE) && (rn2(25) || mtmp->isshk)) + goto finish_search; - xx = otmp->ox; - yy = otmp->oy; - /* Nymphs take everything. Most other creatures should not - * pick up corpses except as a special case like in - * searches_for_item(). We need to do this check in - * mpickstuff() as well. - */ - if (xx >= lmx && xx <= oomx && yy >= lmy && yy <= oomy) { - /* don't get stuck circling around object that's - underneath an immobile or hidden monster; - paralysis victims excluded */ - if ((mtoo = m_at(xx, yy)) != 0 - && (helpless(mtoo) || mtoo->mundetected - || (mtoo->mappearance && !mtoo->iswiz) - || !mtoo->data->mmove)) - continue; - /* the mfndpos() test for whether to allow a move to a - water location accepts flyers, but they can't reach - underwater objects, so being able to move to a spot - is insufficient for deciding whether to do so */ - if (!could_reach_item(mtmp, xx, yy)) - continue; + /* distmin() gives a rectangular area */ + hmx = min(COLNO - 1, omx + minr); + hmy = min(ROWNO - 1, omy + minr); + lmx = max(1, omx - minr); + lmy = max(0, omy - minr); - /* ignore obj if there's a trap and monster knows it */ - if ((ttmp = t_at(xx, yy)) != 0 - && mon_knows_traps(mtmp, ttmp->ttyp)) { - if (*ggx == xx && *ggy == yy) { - *ggx = mtmp->mux; - *ggy = mtmp->muy; - } - continue; + for (xx = lmx; xx <= hmx; xx++) { + for (yy = lmy; yy <= hmy; yy++) { + /* no object here */ + if (!OBJ_AT(xx, yy)) + continue; + /* found an object closer already */ + if (minr < distmin(omx, omy, xx, yy)) + continue; + /* the mfndpos() test for whether to allow a move to a + water location accepts flyers, but they can't reach + underwater objects, so being able to move to a spot + is insufficient for deciding whether to do so */ + if (!could_reach_item(mtmp, xx, yy)) + continue; + /* hiders avoid hero's line of sight */ + if (hides_under(ptr) && cansee(xx, yy)) + continue; + /* don't get stuck circling around object that's + underneath an immobile or hidden monster; + paralysis victims excluded */ + if ((mtoo = m_at(xx, yy)) != 0 + && (helpless(mtoo) || mtoo->mundetected + || (mtoo->mappearance && !mtoo->iswiz) + || !mtoo->data->mmove)) + continue; + /* Don't get stuck circling an Elbereth */ + if (onscary(xx, yy, mtmp)) + continue; + /* ignore obj if there's a trap and monster knows it */ + if ((ttmp = t_at(xx, yy)) != 0 + && mon_knows_traps(mtmp, ttmp->ttyp)) { + if (*ggx == xx && *ggy == yy) { + *ggx = mtmp->mux; + *ggy = mtmp->muy; } + continue; + } + /* avoid getting stuck on eg. items in niches */ + if (!m_cansee(mtmp, xx, yy)) + continue; - if (((mon_would_take_item(mtmp, otmp) && (can_carry(mtmp, otmp) > 0)) - || (hides_under(ptr) && !cansee(otmp->ox, otmp->oy))) - && can_touch_safely(mtmp, otmp) - /* Don't get stuck circling an Elbereth */ - && !onscary(xx, yy, mtmp)) { + costly = costly_spot(xx, yy); + + /* look through the items on this location */ + for (otmp = gl.level.objects[xx][yy]; + otmp; otmp = otmp->nexthere) { + /* monsters may pick rocks up, but won't go out of their way + to grab them; this might hamper sling wielders, but it cuts + down on move overhead by filtering out most common item */ + if (otmp->otyp == ROCK) + continue; + /* avoid special items; once hero picks them up, they'll + cease being special */ + if (is_mines_prize(otmp) || is_soko_prize(otmp)) + continue; + /* skip shop merchandise */ + if (costly && !otmp->no_charge) + continue; + + if (mon_would_take_item(mtmp, otmp) + && (can_carry(mtmp, otmp) > 0) + && can_touch_safely(mtmp, otmp)) { minr = distmin(omx, omy, xx, yy); - oomx = min(COLNO - 1, omx + minr); - oomy = min(ROWNO - 1, omy + minr); - lmx = max(1, omx - minr); - lmy = max(0, omy - minr); *ggx = otmp->ox; *ggy = otmp->oy; if (*ggx == omx && *ggy == omy) { *mmoved = MMOVE_DONE; /* actually unnecessary */ return TRUE; } + /* found an item of interest; skip the rest of the pile */ + break; } } } } +finish_search: if (minr < SQSRCHRADIUS && *appr == -1) { if (distmin(omx, omy, mtmp->mux, mtmp->muy) <= 3) { *ggx = mtmp->mux;