From 201ee8383e7e479d7af480606643f57a1f6f89bd Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Mon, 3 Apr 2023 20:53:16 +0300 Subject: [PATCH] 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. --- src/monmove.c | 130 +++++++++++++++++++++++++++++--------------------- 1 file changed, 75 insertions(+), 55 deletions(-) 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;