1 /* SCCS Id: @(#)dogmove.c 3.4 2002/09/10 */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
10 extern boolean notonhead;
14 STATIC_DCL boolean FDECL(dog_hunger,(struct monst *,struct edog *));
15 STATIC_DCL int FDECL(dog_invent,(struct monst *,struct edog *,int));
16 STATIC_DCL int FDECL(dog_goal,(struct monst *,struct edog *,int,int,int));
18 STATIC_DCL struct obj *FDECL(DROPPABLES, (struct monst *));
19 STATIC_DCL boolean FDECL(can_reach_location,(struct monst *,XCHAR_P,XCHAR_P,
21 STATIC_DCL boolean FDECL(could_reach_item,(struct monst *, XCHAR_P,XCHAR_P));
23 STATIC_OVL struct obj *
25 register struct monst *mon;
27 register struct obj *obj;
28 struct obj *wep = MON_WEP(mon);
29 boolean item1 = FALSE, item2 = FALSE;
31 if (is_animal(mon->data) || mindless(mon->data))
33 if (!tunnels(mon->data) || !needspick(mon->data))
35 for(obj = mon->minvent; obj; obj = obj->nobj) {
36 if (!item1 && is_pick(obj) && (obj->otyp != DWARVISH_MATTOCK
37 || !which_armor(mon, W_ARMS))) {
41 if (!item2 && obj->otyp == UNICORN_HORN && !obj->cursed) {
45 if (!obj->owornmask && obj != wep) return obj;
47 return (struct obj *)0;
50 static NEARDATA const char nofetch[] = { BALL_CLASS, CHAIN_CLASS, ROCK_CLASS, 0 };
54 STATIC_OVL boolean FDECL(cursed_object_at, (int, int));
56 STATIC_VAR xchar gtyp, gx, gy; /* type and position of dog's current goal */
58 STATIC_PTR void FDECL(wantdoor, (int, int, genericptr_t));
62 cursed_object_at(x, y)
67 for(otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
68 if (otmp->cursed) return TRUE;
73 dog_nutrition(mtmp, obj)
80 * It is arbitrary that the pet takes the same length of time to eat
81 * as a human, but gets more nutritional value.
83 if (obj->oclass == FOOD_CLASS) {
84 if(obj->otyp == CORPSE) {
85 mtmp->meating = 3 + (mons[obj->corpsenm].cwt >> 6);
86 nutrit = mons[obj->corpsenm].cnutrit;
88 mtmp->meating = objects[obj->otyp].oc_delay;
89 nutrit = objects[obj->otyp].oc_nutrition;
91 switch(mtmp->data->msize) {
92 case MZ_TINY: nutrit *= 8; break;
93 case MZ_SMALL: nutrit *= 6; break;
95 case MZ_MEDIUM: nutrit *= 5; break;
96 case MZ_LARGE: nutrit *= 4; break;
97 case MZ_HUGE: nutrit *= 3; break;
98 case MZ_GIGANTIC: nutrit *= 2; break;
101 mtmp->meating = eaten_stat(mtmp->meating, obj);
102 nutrit = eaten_stat(nutrit, obj);
104 } else if (obj->oclass == COIN_CLASS) {
105 mtmp->meating = (int)(obj->quan/2000) + 1;
106 if (mtmp->meating < 0) mtmp->meating = 1;
107 nutrit = (int)(obj->quan/20);
108 if (nutrit < 0) nutrit = 0;
110 /* Unusual pet such as gelatinous cube eating odd stuff.
111 * meating made consistent with wild monsters in mon.c.
112 * nutrit made consistent with polymorphed player nutrit in
113 * eat.c. (This also applies to pets eating gold.)
115 mtmp->meating = obj->owt/20 + 1;
116 nutrit = 5*objects[obj->otyp].oc_nutrition;
121 /* returns 2 if pet dies, otherwise 1 */
123 dog_eat(mtmp, obj, x, y, devour)
124 register struct monst *mtmp;
125 register struct obj * obj;
129 register struct edog *edog = EDOG(mtmp);
130 boolean poly = FALSE, grow = FALSE, heal = FALSE;
133 if(edog->hungrytime < monstermoves)
134 edog->hungrytime = monstermoves;
135 nutrit = dog_nutrition(mtmp, obj);
136 poly = polyfodder(obj);
137 grow = mlevelgain(obj);
140 if (mtmp->meating > 1) mtmp->meating /= 2;
141 if (nutrit > 1) nutrit = (nutrit * 3) / 4;
143 edog->hungrytime += nutrit;
145 if (edog->mhpmax_penalty) {
146 /* no longer starving */
147 mtmp->mhpmax += edog->mhpmax_penalty;
148 edog->mhpmax_penalty = 0;
150 if (mtmp->mflee && mtmp->mfleetim > 1) mtmp->mfleetim /= 2;
151 if (mtmp->mtame < 20) mtmp->mtame++;
152 if (x != mtmp->mx || y != mtmp->my) { /* moved & ate on same turn */
154 newsym(mtmp->mx, mtmp->my);
156 if (is_pool(x, y) && !Underwater) {
157 /* Don't print obj */
158 /* TODO: Reveal presence of sea monster (especially sharks) */
160 /* hack: observe the action if either new or old location is in view */
161 /* However, invisible monsters should still be "it" even though out of
162 sight locations should not. */
163 if (cansee(x, y) || cansee(mtmp->mx, mtmp->my))
164 pline("%s %s %s.", mon_visible(mtmp) ? noit_Monnam(mtmp) : "It",
165 devour ? "devours" : "eats",
166 (obj->oclass == FOOD_CLASS) ?
167 singular(obj, doname) : doname(obj));
168 /* It's a reward if it's DOGFOOD and the player dropped/threw it. */
169 /* We know the player had it if invlet is set -dlc */
170 if(dogfood(mtmp,obj) == DOGFOOD && obj->invlet)
174 edog->apport += (int)(200L/
175 ((long)edog->dropdist + monstermoves - edog->droptime));
177 if (mtmp->data == &mons[PM_RUST_MONSTER] && obj->oerodeproof) {
178 /* The object's rustproofing is gone now */
179 obj->oerodeproof = 0;
181 if (canseemon(mtmp) && flags.verbose) {
182 pline("%s spits %s out in disgust!",
183 Monnam(mtmp), distant_name(obj,doname));
185 } else if (obj == uball) {
188 } else if (obj == uchain)
190 else if (obj->quan > 1L && obj->oclass == FOOD_CLASS) {
192 obj->owt = weight(obj);
197 (void) newcham(mtmp, (struct permonst *)0, FALSE,
198 cansee(mtmp->mx, mtmp->my));
200 /* limit "instant" growth to prevent potential abuse */
201 if (grow && (int) mtmp->m_lev < (int)mtmp->data->mlevel + 15) {
202 if (!grow_up(mtmp, (struct monst *)0)) return 2;
204 if (heal) mtmp->mhp = mtmp->mhpmax;
211 /* hunger effects -- returns TRUE on starvation */
213 dog_hunger(mtmp, edog)
214 register struct monst *mtmp;
215 register struct edog *edog;
217 if (monstermoves > edog->hungrytime + 500) {
218 if (!carnivorous(mtmp->data) && !herbivorous(mtmp->data)) {
219 edog->hungrytime = monstermoves + 500;
220 /* but not too high; it might polymorph */
221 } else if (!edog->mhpmax_penalty) {
222 /* starving pets are limited in healing */
223 int newmhpmax = mtmp->mhpmax / 3;
225 edog->mhpmax_penalty = mtmp->mhpmax - newmhpmax;
226 mtmp->mhpmax = newmhpmax;
227 if (mtmp->mhp > mtmp->mhpmax)
228 mtmp->mhp = mtmp->mhpmax;
229 if (mtmp->mhp < 1) goto dog_died;
230 if (cansee(mtmp->mx, mtmp->my))
231 pline("%s is confused from hunger.", Monnam(mtmp));
232 else if (couldsee(mtmp->mx, mtmp->my))
235 You_feel("worried about %s.", y_monnam(mtmp));
237 } else if (monstermoves > edog->hungrytime + 750 || mtmp->mhp < 1) {
244 Your("leash goes slack.");
245 else if (cansee(mtmp->mx, mtmp->my))
246 pline("%s starves.", Monnam(mtmp));
248 You_feel("%s for a moment.",
249 Hallucination ? "bummed" : "sad");
257 /* do something with object (drop, pick up, eat) at current position
258 * returns 1 if object eaten (since that counts as dog's move), 2 if died
261 dog_invent(mtmp, edog, udist)
262 register struct monst *mtmp;
263 register struct edog *edog;
266 register int omx, omy;
269 if (mtmp->msleeping || !mtmp->mcanmove) return(0);
274 /* if we are carrying sth then we drop it (perhaps near @) */
275 /* Note: if apport == 1 then our behaviour is independent of udist */
276 /* Use udist+1 so steed won't cause divide by zero */
278 if(DROPPABLES(mtmp) || mtmp->mgold) {
280 if(DROPPABLES(mtmp)) {
282 if (!rn2(udist+1) || !rn2(edog->apport))
283 if(rn2(10) < edog->apport){
284 relobj(mtmp, (int)mtmp->minvis, TRUE);
285 if(edog->apport > 1) edog->apport--;
286 edog->dropdist = udist; /* hpscdi!jon */
287 edog->droptime = monstermoves;
290 if((obj=level.objects[omx][omy]) && !index(nofetch,obj->oclass)
292 && obj->otyp != SCR_MAIL
295 int edible = dogfood(mtmp, obj);
297 if ((edible <= CADAVER ||
298 /* starving pet is more aggressive about eating */
299 (edog->mhpmax_penalty && edible == ACCFOOD)) &&
300 could_reach_item(mtmp, obj->ox, obj->oy))
301 return dog_eat(mtmp, obj, omx, omy, FALSE);
303 if(can_carry(mtmp, obj) && !obj->cursed &&
304 could_reach_item(mtmp, obj->ox, obj->oy)) {
305 if(rn2(20) < edog->apport+3) {
306 if (rn2(udist) || !rn2(edog->apport)) {
307 if (cansee(omx, omy) && flags.verbose)
308 pline("%s picks up %s.", Monnam(mtmp),
309 distant_name(obj, doname));
310 obj_extract_self(obj);
312 (void) mpickobj(mtmp,obj);
313 if (attacktype(mtmp->data, AT_WEAP) &&
314 mtmp->weapon_check == NEED_WEAPON) {
315 mtmp->weapon_check = NEED_HTH_WEAPON;
316 (void) mon_wield_item(mtmp);
318 m_dowear(mtmp, FALSE);
327 /* set dog's goal -- gtyp, gx, gy
328 * returns -1/0/1 (dog's desire to approach player) or -2 (abort move)
331 dog_goal(mtmp, edog, after, udist, whappr)
332 register struct monst *mtmp;
334 int after, udist, whappr;
336 register int omx, omy;
337 boolean in_masters_sight, dog_has_minvent;
338 register struct obj *obj;
343 /* Steeds don't move on their own will */
344 if (mtmp == u.usteed)
351 in_masters_sight = couldsee(omx, omy);
352 dog_has_minvent = (DROPPABLES(mtmp) != 0);
354 if (!edog || mtmp->mleashed) { /* he's not going anywhere... */
359 #define DDIST(x,y) (dist2(x,y,omx,omy))
360 #define SQSRCHRADIUS 5
361 int min_x, max_x, min_y, max_y;
364 gtyp = UNDEF; /* no goal as yet */
365 gx = gy = 0; /* suppress 'used before set' message */
367 if ((min_x = omx - SQSRCHRADIUS) < 1) min_x = 1;
368 if ((max_x = omx + SQSRCHRADIUS) >= COLNO) max_x = COLNO - 1;
369 if ((min_y = omy - SQSRCHRADIUS) < 0) min_y = 0;
370 if ((max_y = omy + SQSRCHRADIUS) >= ROWNO) max_y = ROWNO - 1;
372 /* nearby food is the first choice, then other objects */
373 for (obj = fobj; obj; obj = obj->nobj) {
376 if (nx >= min_x && nx <= max_x && ny >= min_y && ny <= max_y) {
377 otyp = dogfood(mtmp, obj);
378 /* skip inferior goals */
379 if (otyp > gtyp || otyp == UNDEF)
381 /* avoid cursed items unless starving */
382 if (cursed_object_at(nx, ny) &&
383 !(edog->mhpmax_penalty && otyp < MANFOOD))
385 /* skip completely unreacheable goals */
386 if (!could_reach_item(mtmp, nx, ny) ||
387 !can_reach_location(mtmp, mtmp->mx, mtmp->my, nx, ny))
389 if (otyp < MANFOOD) {
390 if (otyp < gtyp || DDIST(nx,ny) < DDIST(gx,gy)) {
395 } else if(gtyp == UNDEF && in_masters_sight &&
397 (!levl[omx][omy].lit || levl[u.ux][u.uy].lit) &&
398 (otyp == MANFOOD || m_cansee(mtmp, nx, ny)) &&
399 edog->apport > rn2(8) &&
400 can_carry(mtmp,obj)) {
409 /* follow player if appropriate */
411 (gtyp != DOGFOOD && gtyp != APPORT && monstermoves < edog->hungrytime)) {
414 if (after && udist <= 4 && gx == u.ux && gy == u.uy)
416 appr = (udist >= 9) ? 1 : (mtmp->mflee) ? -1 : 0;
418 if (!IS_ROOM(levl[u.ux][u.uy].typ) || !rn2(4) ||
420 (dog_has_minvent && rn2(edog->apport)))
423 /* if you have dog food it'll follow you more closely */
427 if(dogfood(mtmp, obj) == DOGFOOD) {
435 appr = 1; /* gtyp != UNDEF */
439 #define FARAWAY (COLNO + 2) /* position outside screen */
440 if (gx == u.ux && gy == u.uy && !in_masters_sight) {
443 cp = gettrack(omx,omy);
447 if(edog) edog->ogoal.x = 0;
449 /* assume master hasn't moved far, and reuse previous goal */
450 if(edog && edog->ogoal.x &&
451 ((edog->ogoal.x != omx) || (edog->ogoal.y != omy))) {
456 int fardist = FARAWAY * FARAWAY;
457 gx = gy = FARAWAY; /* random */
458 do_clear_area(omx, omy, 9, wantdoor,
459 (genericptr_t)&fardist);
461 /* here gx == FARAWAY e.g. when dog is in a vault */
462 if (gx == FARAWAY || (gx == omx && gy == omy)) {
477 /* return 0 (no move), 1 (move) or 2 (dead) */
479 dog_move(mtmp, after)
480 register struct monst *mtmp;
481 register int after; /* this is extra fast monster movement */
483 int omx, omy; /* original mtmp position */
484 int appr, whappr, udist;
486 register struct edog *edog = EDOG(mtmp);
487 struct obj *obj = (struct obj *) 0;
489 boolean has_edog, cursemsg[9], do_eat = FALSE;
490 xchar nix, niy; /* position mtmp is (considering) moving to */
491 register int nx, ny; /* temporary coordinates */
492 xchar cnt, uncursedcnt, chcnt;
493 int chi = -1, nidist, ndist;
495 long info[9], allowflags;
496 #define GDIST(x,y) (dist2(x,y,gx,gy))
499 * Tame Angels have isminion set and an ispriest structure instead of
500 * an edog structure. Fortunately, guardian Angels need not worry
501 * about mundane things like eating and fetching objects, and can
502 * spend all their energy defending the player. (They are the only
503 * monsters with other structures that can be tame.)
505 has_edog = !mtmp->isminion;
509 if (has_edog && dog_hunger(mtmp, edog)) return(2); /* starved */
511 udist = distu(omx,omy);
513 /* Let steeds eat and maybe throw rider during Conflict */
514 if (mtmp == u.usteed) {
515 if (Conflict && !resist(mtmp, RING_CLASS, 0, 0)) {
516 dismount_steed(DISMOUNT_THROWN);
522 /* maybe we tamed him while being swallowed --jgm */
523 if (!udist) return(0);
525 nix = omx; /* set before newdogpos */
527 cursemsg[0] = FALSE; /* lint suppression */
528 info[0] = 0; /* ditto */
531 j = dog_invent(mtmp, edog, udist);
532 if (j == 2) return 2; /* died */
533 else if (j == 1) goto newdogpos; /* eating something */
535 whappr = (monstermoves - edog->whistletime < 5);
539 appr = dog_goal(mtmp, has_edog ? edog : (struct edog *)0,
540 after, udist, whappr);
541 if (appr == -2) return(0);
543 allowflags = ALLOW_M | ALLOW_TRAPS | ALLOW_SSM | ALLOW_SANCT;
544 if (passes_walls(mtmp->data)) allowflags |= (ALLOW_ROCK | ALLOW_WALL);
545 if (passes_bars(mtmp->data)) allowflags |= ALLOW_BARS;
546 if (throws_rocks(mtmp->data)) allowflags |= ALLOW_ROCK;
547 if (Conflict && !resist(mtmp, RING_CLASS, 0, 0)) {
548 allowflags |= ALLOW_U;
551 /* Guardian angel refuses to be conflicted; rather,
552 * it disappears, angrily, and sends in some nasties
554 if (canspotmon(mtmp)) {
555 pline("%s rebukes you, saying:", Monnam(mtmp));
556 verbalize("Since you desire conflict, have some more!");
563 if(enexto(&mm, mm.x, mm.y, &mons[PM_ANGEL]))
564 (void) mk_roamer(&mons[PM_ANGEL], u.ualign.type,
571 if (!Conflict && !mtmp->mconf &&
572 mtmp == u.ustuck && !sticks(youmonst.data)) {
573 unstuck(mtmp); /* swallowed case handled above */
574 You("get released!");
576 if (!nohands(mtmp->data) && !verysmall(mtmp->data)) {
577 allowflags |= OPENDOOR;
578 if (m_carrying(mtmp, SKELETON_KEY)) allowflags |= BUSTDOOR;
580 if (is_giant(mtmp->data)) allowflags |= BUSTDOOR;
581 if (tunnels(mtmp->data)) allowflags |= ALLOW_DIG;
582 cnt = mfndpos(mtmp, poss, info, allowflags);
584 /* Normally dogs don't step on cursed items, but if they have no
585 * other choice they will. This requires checking ahead of time
586 * to see how many uncursed item squares are around.
589 for (i = 0; i < cnt; i++) {
590 nx = poss[i].x; ny = poss[i].y;
591 if (MON_AT(nx,ny) && !(info[i] & ALLOW_M)) continue;
592 if (cursed_object_at(nx, ny)) continue;
598 nidist = GDIST(nix,niy);
600 for (i = 0; i < cnt; i++) {
605 /* if leashed, we drag him along. */
606 if (mtmp->mleashed && distu(nx, ny) > 4) continue;
608 /* if a guardian, try to stay close by choice */
610 (j = distu(nx, ny)) > 16 && j >= udist) continue;
612 if ((info[i] & ALLOW_M) && MON_AT(nx, ny)) {
614 register struct monst *mtmp2 = m_at(nx,ny);
616 if ((int)mtmp2->m_lev >= (int)mtmp->m_lev+2 ||
617 (mtmp2->data == &mons[PM_FLOATING_EYE] && rn2(10) &&
618 mtmp->mcansee && haseyes(mtmp->data) && mtmp2->mcansee
619 && (perceives(mtmp->data) || !mtmp2->minvis)) ||
620 (mtmp2->data==&mons[PM_GELATINOUS_CUBE] && rn2(10)) ||
621 (max_passive_dmg(mtmp2, mtmp) >= mtmp->mhp) ||
622 ((mtmp->mhp*4 < mtmp->mhpmax
623 || mtmp2->data->msound == MS_GUARDIAN
624 || mtmp2->data->msound == MS_LEADER) &&
625 mtmp2->mpeaceful && !Conflict) ||
626 (touch_petrifies(mtmp2->data) &&
627 !resists_ston(mtmp)))
630 if (after) return(0); /* hit only once each move */
633 mstatus = mattackm(mtmp, mtmp2);
635 /* aggressor (pet) died */
636 if (mstatus & MM_AGR_DIED) return 2;
638 if ((mstatus & MM_HIT) && !(mstatus & MM_DEF_DIED) &&
639 rn2(4) && mtmp2->mlstmv != monstermoves &&
640 !onscary(mtmp->mx, mtmp->my, mtmp2) &&
641 /* monnear check needed: long worms hit on tail */
642 monnear(mtmp2, mtmp->mx, mtmp->my)) {
643 mstatus = mattackm(mtmp2, mtmp); /* return attack */
644 if (mstatus & MM_DEF_DIED) return 2;
650 { /* Dog avoids harmful traps, but perhaps it has to pass one
651 * in order to follow player. (Non-harmful traps do not
652 * have ALLOW_TRAPS in info[].) The dog only avoids the
653 * trap if you've seen it, unlike enemies who avoid traps
654 * if they've seen some trap of that type sometime in the
655 * past. (Neither behavior is really realistic.)
659 if ((info[i] & ALLOW_TRAPS) && (trap = t_at(nx,ny))) {
660 if (mtmp->mleashed) {
661 if (flags.soundok) whimper(mtmp);
663 /* 1/40 chance of stepping on it anyway, in case
664 * it has to pass one to follow the player...
666 if (trap->tseen && rn2(40)) continue;
670 /* dog eschews cursed objects, but likes dog food */
671 /* (minion isn't interested; `cursemsg' stays FALSE) */
673 for (obj = level.objects[nx][ny]; obj; obj = obj->nexthere) {
674 if (obj->cursed) cursemsg[i] = TRUE;
675 else if ((otyp = dogfood(mtmp, obj)) < MANFOOD &&
676 (otyp < ACCFOOD || edog->hungrytime <= monstermoves)) {
677 /* Note: our dog likes the food so much that he
678 * might eat it even when it conceals a cursed object */
683 cursemsg[i] = FALSE; /* not reluctant */
687 /* didn't find something to eat; if we saw a cursed item and
688 aren't being forced to walk on it, usually keep looking */
689 if (cursemsg[i] && !mtmp->mleashed && uncursedcnt > 0 &&
690 rn2(13 * uncursedcnt)) continue;
692 /* lessen the chance of backtracking to previous position(s) */
693 k = has_edog ? uncursedcnt : cnt;
694 for (j = 0; j < MTSZ && j < k - 1; j++)
695 if (nx == mtmp->mtrack[j].x && ny == mtmp->mtrack[j].y)
696 if (rn2(MTSZ * (k - j))) goto nxti;
698 j = ((ndist = GDIST(nx,ny)) - nidist) * appr;
699 if ((j == 0 && !rn2(++chcnt)) || j < 0 ||
701 ((omx == nix && omy == niy && !rn2(3))
713 if (nix != omx || niy != omy) {
716 if (info[chi] & ALLOW_U) {
717 if (mtmp->mleashed) { /* play it safe */
718 pline("%s breaks loose of %s leash!",
719 Monnam(mtmp), mhis(mtmp));
720 m_unleash(mtmp, FALSE);
722 (void) mattacku(mtmp);
725 if (!m_in_out_region(mtmp, nix, niy))
727 if (((IS_ROCK(levl[nix][niy].typ) && may_dig(nix,niy)) ||
728 closed_door(nix, niy)) &&
729 mtmp->weapon_check != NO_WEAPON_WANTED &&
730 tunnels(mtmp->data) && needspick(mtmp->data)) {
731 if (closed_door(nix, niy)) {
732 if (!(mw_tmp = MON_WEP(mtmp)) ||
733 !is_pick(mw_tmp) || !is_axe(mw_tmp))
734 mtmp->weapon_check = NEED_PICK_OR_AXE;
735 } else if (IS_TREE(levl[nix][niy].typ)) {
736 if (!(mw_tmp = MON_WEP(mtmp)) || !is_axe(mw_tmp))
737 mtmp->weapon_check = NEED_AXE;
738 } else if (!(mw_tmp = MON_WEP(mtmp)) || !is_pick(mw_tmp)) {
739 mtmp->weapon_check = NEED_PICK_AXE;
741 if (mtmp->weapon_check >= NEED_PICK_AXE &&
742 mon_wield_item(mtmp))
745 /* insert a worm_move() if worms ever begin to eat things */
746 remove_monster(omx, omy);
747 place_monster(mtmp, nix, niy);
748 if (cursemsg[chi] && (cansee(omx,omy) || cansee(nix,niy)))
749 pline("%s moves only reluctantly.", Monnam(mtmp));
750 for (j=MTSZ-1; j>0; j--) mtmp->mtrack[j] = mtmp->mtrack[j-1];
751 mtmp->mtrack[0].x = omx;
752 mtmp->mtrack[0].y = omy;
753 /* We have to know if the pet's gonna do a combined eat and
754 * move before moving it, but it can't eat until after being
755 * moved. Thus the do_eat flag.
758 if (dog_eat(mtmp, obj, omx, omy, FALSE) == 2) return 2;
760 } else if (mtmp->mleashed && distu(omx, omy) > 4) {
761 /* an incredible kludge, but the only way to keep pooch near
762 * after it spends time eating or in a trap, etc.
766 nx = sgn(omx - u.ux);
767 ny = sgn(omy - u.uy);
770 if (goodpos(cc.x, cc.y, mtmp, 0)) goto dognext;
773 for (j = (i + 7)%8; j < (i + 1)%8; j++) {
775 if (goodpos(cc.x, cc.y, mtmp, 0)) goto dognext;
777 for (j = (i + 6)%8; j < (i + 2)%8; j++) {
779 if (goodpos(cc.x, cc.y, mtmp, 0)) goto dognext;
784 if (!m_in_out_region(mtmp, nix, niy))
786 remove_monster(mtmp->mx, mtmp->my);
787 place_monster(mtmp, cc.x, cc.y);
794 /* check if a monster could pick up objects from a location */
796 could_reach_item(mon, nx, ny)
800 if ((!is_pool(nx,ny) || is_swimmer(mon->data)) &&
801 (!is_lava(nx,ny) || likes_lava(mon->data)) &&
802 (!sobj_at(BOULDER,nx,ny) || throws_rocks(mon->data)))
807 /* Hack to prevent a dog from being endlessly stuck near an object that
808 * it can't reach, such as caught in a teleport scroll niche. It recursively
809 * checks to see if the squares in between are good. The checking could be a
810 * little smarter; a full check would probably be useful in m_move() too.
811 * Since the maximum food distance is 5, this should never be more than 5 calls
815 can_reach_location(mon, mx, my, fx, fy)
817 xchar mx, my, fx, fy;
822 if (mx == fx && my == fy) return TRUE;
823 if (!isok(mx, my)) return FALSE; /* should not happen */
825 dist = dist2(mx, my, fx, fy);
826 for(i=mx-1; i<=mx+1; i++) {
827 for(j=my-1; j<=my+1; j++) {
830 if (dist2(i, j, fx, fy) >= dist)
832 if (IS_ROCK(levl[i][j].typ) && !passes_walls(mon->data) &&
833 (!may_dig(i,j) || !tunnels(mon->data)))
835 if (IS_DOOR(levl[i][j].typ) &&
836 (levl[i][j].doormask & (D_CLOSED | D_LOCKED)))
838 if (!could_reach_item(mon, i, j))
840 if (can_reach_location(mon, i, j, fx, fy))
850 /*ARGSUSED*/ /* do_clear_area client */
852 wantdoor(x, y, distance)
854 genericptr_t distance;
858 if (*(int*)distance > (ndist = distu(x, y))) {
861 *(int*)distance = ndist;