Improve m_search_item

Previously when monster was interested to pick up an item,
the code went through the whole object chain, so going through
all the items on the level. This caused problems with some games,
for example where the player created thousands of meatballs
in separate stacks.

Changed the code so it now looks at the map locations inside
the search radius, and the stacks in those map locations,
skipping locations as early as possible.
This commit is contained in:
Pasi Kallinen
2023-04-03 21:03:20 +03:00
parent f1ac29d42f
commit 201ee8383e
+75 -55
View File
@@ -1100,19 +1100,22 @@ maybe_spin_web(struct monst *mtmp)
} }
} }
/* max distmin() distance for monster to look for items */
#define SQSRCHRADIUS 5 #define SQSRCHRADIUS 5
/* monster looks for items it wants nearby */
static boolean static boolean
m_search_items(struct monst *mtmp, coordxy *ggx, coordxy *ggy, schar *mmoved, int *appr) m_search_items(struct monst *mtmp, coordxy *ggx, coordxy *ggy, schar *mmoved, int *appr)
{ {
register int minr = SQSRCHRADIUS; /* not too far away */ register int minr = SQSRCHRADIUS; /* not too far away */
register struct obj *otmp; register struct obj *otmp;
register coordxy xx, yy; register coordxy xx, yy;
coordxy oomx, oomy, lmx, lmy; coordxy hmx, hmy, lmx, lmy;
struct trap *ttmp; struct trap *ttmp;
coordxy omx = mtmp->mx, omy = mtmp->my; coordxy omx = mtmp->mx, omy = mtmp->my;
struct permonst *ptr = mtmp->data; struct permonst *ptr = mtmp->data;
struct monst *mtoo; struct monst *mtoo;
boolean costly;
/* cut down the search radius if it thinks character is closer. */ /* cut down the search radius if it thinks character is closer. */
if (distmin(mtmp->mux, mtmp->muy, omx, omy) < SQSRCHRADIUS 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)) if (!mtmp->mpeaceful && is_mercenary(ptr))
minr = 1; minr = 1;
if ((!*in_rooms(omx, omy, SHOPBASE) || (!rn2(25) && !mtmp->isshk))) { /* in shop, usually skip */
oomx = min(COLNO - 1, omx + minr); if (*in_rooms(omx, omy, SHOPBASE) && (rn2(25) || mtmp->isshk))
oomy = min(ROWNO - 1, omy + minr); goto finish_search;
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;
xx = otmp->ox; /* distmin() gives a rectangular area */
yy = otmp->oy; hmx = min(COLNO - 1, omx + minr);
/* Nymphs take everything. Most other creatures should not hmy = min(ROWNO - 1, omy + minr);
* pick up corpses except as a special case like in lmx = max(1, omx - minr);
* searches_for_item(). We need to do this check in lmy = max(0, omy - minr);
* 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;
/* ignore obj if there's a trap and monster knows it */ for (xx = lmx; xx <= hmx; xx++) {
if ((ttmp = t_at(xx, yy)) != 0 for (yy = lmy; yy <= hmy; yy++) {
&& mon_knows_traps(mtmp, ttmp->ttyp)) { /* no object here */
if (*ggx == xx && *ggy == yy) { if (!OBJ_AT(xx, yy))
*ggx = mtmp->mux; continue;
*ggy = mtmp->muy; /* found an object closer already */
} if (minr < distmin(omx, omy, xx, yy))
continue; 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)) costly = costly_spot(xx, yy);
|| (hides_under(ptr) && !cansee(otmp->ox, otmp->oy)))
&& can_touch_safely(mtmp, otmp) /* look through the items on this location */
/* Don't get stuck circling an Elbereth */ for (otmp = gl.level.objects[xx][yy];
&& !onscary(xx, yy, mtmp)) { 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); 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; *ggx = otmp->ox;
*ggy = otmp->oy; *ggy = otmp->oy;
if (*ggx == omx && *ggy == omy) { if (*ggx == omx && *ggy == omy) {
*mmoved = MMOVE_DONE; /* actually unnecessary */ *mmoved = MMOVE_DONE; /* actually unnecessary */
return TRUE; return TRUE;
} }
/* found an item of interest; skip the rest of the pile */
break;
} }
} }
} }
} }
finish_search:
if (minr < SQSRCHRADIUS && *appr == -1) { if (minr < SQSRCHRADIUS && *appr == -1) {
if (distmin(omx, omy, mtmp->mux, mtmp->muy) <= 3) { if (distmin(omx, omy, mtmp->mux, mtmp->muy) <= 3) {
*ggx = mtmp->mux; *ggx = mtmp->mux;