OSDN Git Service

shrink mine
[nethackexpress/trunk.git] / src / dogmove.c
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. */
4
5 #include "hack.h"
6
7 #include "mfndpos.h"
8 #include "edog.h"
9
10 extern boolean notonhead;
11
12 #ifdef OVL0
13
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));
17
18 STATIC_DCL struct obj *FDECL(DROPPABLES, (struct monst *));
19 STATIC_DCL boolean FDECL(can_reach_location,(struct monst *,XCHAR_P,XCHAR_P,
20     XCHAR_P,XCHAR_P));
21 STATIC_DCL boolean FDECL(could_reach_item,(struct monst *, XCHAR_P,XCHAR_P));
22
23 STATIC_OVL struct obj *
24 DROPPABLES(mon)
25 register struct monst *mon;
26 {
27         register struct obj *obj;
28         struct obj *wep = MON_WEP(mon);
29         boolean item1 = FALSE, item2 = FALSE;
30
31         if (is_animal(mon->data) || mindless(mon->data))
32                 item1 = item2 = TRUE;
33         if (!tunnels(mon->data) || !needspick(mon->data))
34                 item1 = TRUE;
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))) {
38                         item1 = TRUE;
39                         continue;
40                 }
41                 if (!item2 && obj->otyp == UNICORN_HORN && !obj->cursed) {
42                         item2 = TRUE;
43                         continue;
44                 }
45                 if (!obj->owornmask && obj != wep) return obj;
46         }
47         return (struct obj *)0;
48 }
49
50 static NEARDATA const char nofetch[] = { BALL_CLASS, CHAIN_CLASS, ROCK_CLASS, 0 };
51
52 #endif /* OVL0 */
53
54 STATIC_OVL boolean FDECL(cursed_object_at, (int, int));
55
56 STATIC_VAR xchar gtyp, gx, gy;  /* type and position of dog's current goal */
57
58 STATIC_PTR void FDECL(wantdoor, (int, int, genericptr_t));
59
60 #ifdef OVLB
61 STATIC_OVL boolean
62 cursed_object_at(x, y)
63 int x, y;
64 {
65         struct obj *otmp;
66
67         for(otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
68                 if (otmp->cursed) return TRUE;
69         return FALSE;
70 }
71
72 int
73 dog_nutrition(mtmp, obj)
74 struct monst *mtmp;
75 struct obj *obj;
76 {
77         int nutrit;
78
79         /*
80          * It is arbitrary that the pet takes the same length of time to eat
81          * as a human, but gets more nutritional value.
82          */
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;
87             } else {
88                 mtmp->meating = objects[obj->otyp].oc_delay;
89                 nutrit = objects[obj->otyp].oc_nutrition;
90             }
91             switch(mtmp->data->msize) {
92                 case MZ_TINY: nutrit *= 8; break;
93                 case MZ_SMALL: nutrit *= 6; break;
94                 default:
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;
99             }
100             if(obj->oeaten) {
101                 mtmp->meating = eaten_stat(mtmp->meating, obj);
102                 nutrit = eaten_stat(nutrit, obj);
103             }
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;
109         } else {
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.)
114              */
115             mtmp->meating = obj->owt/20 + 1;
116             nutrit = 5*objects[obj->otyp].oc_nutrition;
117         }
118         return nutrit;
119 }
120
121 /* returns 2 if pet dies, otherwise 1 */
122 int
123 dog_eat(mtmp, obj, x, y, devour)
124 register struct monst *mtmp;
125 register struct obj * obj;
126 int x, y;
127 boolean devour;
128 {
129         register struct edog *edog = EDOG(mtmp);
130         boolean poly = FALSE, grow = FALSE, heal = FALSE;
131         int nutrit;
132
133         if(edog->hungrytime < monstermoves)
134             edog->hungrytime = monstermoves;
135         nutrit = dog_nutrition(mtmp, obj);
136         poly = polyfodder(obj);
137         grow = mlevelgain(obj);
138         heal = mhealup(obj);
139         if (devour) {
140             if (mtmp->meating > 1) mtmp->meating /= 2;
141             if (nutrit > 1) nutrit = (nutrit * 3) / 4;
142         }
143         edog->hungrytime += nutrit;
144         mtmp->mconf = 0;
145         if (edog->mhpmax_penalty) {
146             /* no longer starving */
147             mtmp->mhpmax += edog->mhpmax_penalty;
148             edog->mhpmax_penalty = 0;
149         }
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 */
153             newsym(x, y);
154             newsym(mtmp->mx, mtmp->my);
155         }
156         if (is_pool(x, y) && !Underwater) {
157             /* Don't print obj */
158             /* TODO: Reveal presence of sea monster (especially sharks) */
159         } else
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)
171 #ifdef LINT
172             edog->apport = 0;
173 #else
174             edog->apport += (int)(200L/
175                 ((long)edog->dropdist + monstermoves - edog->droptime));
176 #endif
177         if (mtmp->data == &mons[PM_RUST_MONSTER] && obj->oerodeproof) {
178             /* The object's rustproofing is gone now */
179             obj->oerodeproof = 0;
180             mtmp->mstun = 1;
181             if (canseemon(mtmp) && flags.verbose) {
182                 pline("%s spits %s out in disgust!",
183                       Monnam(mtmp), distant_name(obj,doname));
184             }
185         } else if (obj == uball) {
186             unpunish();
187             delobj(obj);
188         } else if (obj == uchain)
189             unpunish();
190         else if (obj->quan > 1L && obj->oclass == FOOD_CLASS) {
191             obj->quan--;
192             obj->owt = weight(obj);
193         } else
194             delobj(obj);
195
196         if (poly) {
197             (void) newcham(mtmp, (struct permonst *)0, FALSE,
198                            cansee(mtmp->mx, mtmp->my));
199         }
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;
203         }
204         if (heal) mtmp->mhp = mtmp->mhpmax;
205         return 1;
206 }
207
208 #endif /* OVLB */
209 #ifdef OVL0
210
211 /* hunger effects -- returns TRUE on starvation */
212 STATIC_OVL boolean
213 dog_hunger(mtmp, edog)
214 register struct monst *mtmp;
215 register struct edog *edog;
216 {
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;
224                 mtmp->mconf = 1;
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))
233                     beg(mtmp);
234                 else
235                     You_feel("worried about %s.", y_monnam(mtmp));
236                 stop_occupation();
237             } else if (monstermoves > edog->hungrytime + 750 || mtmp->mhp < 1) {
238  dog_died:
239                 if (mtmp->mleashed
240 #ifdef STEED
241                     && mtmp != u.usteed
242 #endif
243                     )
244                     Your("leash goes slack.");
245                 else if (cansee(mtmp->mx, mtmp->my))
246                     pline("%s starves.", Monnam(mtmp));
247                 else
248                     You_feel("%s for a moment.",
249                         Hallucination ? "bummed" : "sad");
250                 mondied(mtmp);
251                 return(TRUE);
252             }
253         }
254         return(FALSE);
255 }
256
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
259  */
260 STATIC_OVL int
261 dog_invent(mtmp, edog, udist)
262 register struct monst *mtmp;
263 register struct edog *edog;
264 int udist;
265 {
266         register int omx, omy;
267         struct obj *obj;
268
269         if (mtmp->msleeping || !mtmp->mcanmove) return(0);
270
271         omx = mtmp->mx;
272         omy = mtmp->my;
273
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 */
277 #ifndef GOLDOBJ
278         if(DROPPABLES(mtmp) || mtmp->mgold) {
279 #else
280         if(DROPPABLES(mtmp)) {
281 #endif
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;
288                 }
289         } else {
290             if((obj=level.objects[omx][omy]) && !index(nofetch,obj->oclass)
291 #ifdef MAIL
292                         && obj->otyp != SCR_MAIL
293 #endif
294                                                                         ){
295                 int edible = dogfood(mtmp, obj);
296
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);
302
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);
311                             newsym(omx,omy);
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);
317                             }
318                             m_dowear(mtmp, FALSE);
319                         }
320                     }
321                 }
322             }
323         }
324         return 0;
325 }
326
327 /* set dog's goal -- gtyp, gx, gy
328  * returns -1/0/1 (dog's desire to approach player) or -2 (abort move)
329  */
330 STATIC_OVL int
331 dog_goal(mtmp, edog, after, udist, whappr)
332 register struct monst *mtmp;
333 struct edog *edog;
334 int after, udist, whappr;
335 {
336         register int omx, omy;
337         boolean in_masters_sight, dog_has_minvent;
338         register struct obj *obj;
339         xchar otyp;
340         int appr;
341
342 #ifdef STEED
343         /* Steeds don't move on their own will */
344         if (mtmp == u.usteed)
345                 return (-2);
346 #endif
347
348         omx = mtmp->mx;
349         omy = mtmp->my;
350
351         in_masters_sight = couldsee(omx, omy);
352         dog_has_minvent = (DROPPABLES(mtmp) != 0);
353
354         if (!edog || mtmp->mleashed) {  /* he's not going anywhere... */
355             gtyp = APPORT;
356             gx = u.ux;
357             gy = u.uy;
358         } else {
359 #define DDIST(x,y) (dist2(x,y,omx,omy))
360 #define SQSRCHRADIUS 5
361             int min_x, max_x, min_y, max_y;
362             register int nx, ny;
363
364             gtyp = UNDEF;       /* no goal as yet */
365             gx = gy = 0;        /* suppress 'used before set' message */
366
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;
371
372             /* nearby food is the first choice, then other objects */
373             for (obj = fobj; obj; obj = obj->nobj) {
374                 nx = obj->ox;
375                 ny = obj->oy;
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)
380                         continue;
381                     /* avoid cursed items unless starving */
382                     if (cursed_object_at(nx, ny) &&
383                             !(edog->mhpmax_penalty && otyp < MANFOOD))
384                         continue;
385                     /* skip completely unreacheable goals */
386                     if (!could_reach_item(mtmp, nx, ny) ||
387                         !can_reach_location(mtmp, mtmp->mx, mtmp->my, nx, ny))
388                         continue;
389                     if (otyp < MANFOOD) {
390                         if (otyp < gtyp || DDIST(nx,ny) < DDIST(gx,gy)) {
391                             gx = nx;
392                             gy = ny;
393                             gtyp = otyp;
394                         }
395                     } else if(gtyp == UNDEF && in_masters_sight &&
396                               !dog_has_minvent &&
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)) {
401                         gx = nx;
402                         gy = ny;
403                         gtyp = APPORT;
404                     }
405                 }
406             }
407         }
408
409         /* follow player if appropriate */
410         if (gtyp == UNDEF ||
411             (gtyp != DOGFOOD && gtyp != APPORT && monstermoves < edog->hungrytime)) {
412                 gx = u.ux;
413                 gy = u.uy;
414                 if (after && udist <= 4 && gx == u.ux && gy == u.uy)
415                         return(-2);
416                 appr = (udist >= 9) ? 1 : (mtmp->mflee) ? -1 : 0;
417                 if (udist > 1) {
418                         if (!IS_ROOM(levl[u.ux][u.uy].typ) || !rn2(4) ||
419                            whappr ||
420                            (dog_has_minvent && rn2(edog->apport)))
421                                 appr = 1;
422                 }
423                 /* if you have dog food it'll follow you more closely */
424                 if (appr == 0) {
425                         obj = invent;
426                         while (obj) {
427                                 if(dogfood(mtmp, obj) == DOGFOOD) {
428                                         appr = 1;
429                                         break;
430                                 }
431                                 obj = obj->nobj;
432                         }
433                 }
434         } else
435             appr = 1;   /* gtyp != UNDEF */
436         if(mtmp->mconf)
437             appr = 0;
438
439 #define FARAWAY (COLNO + 2)             /* position outside screen */
440         if (gx == u.ux && gy == u.uy && !in_masters_sight) {
441             register coord *cp;
442
443             cp = gettrack(omx,omy);
444             if (cp) {
445                 gx = cp->x;
446                 gy = cp->y;
447                 if(edog) edog->ogoal.x = 0;
448             } else {
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))) {
452                     gx = edog->ogoal.x;
453                     gy = edog->ogoal.y;
454                     edog->ogoal.x = 0;
455                 } else {
456                     int fardist = FARAWAY * FARAWAY;
457                     gx = gy = FARAWAY; /* random */
458                     do_clear_area(omx, omy, 9, wantdoor,
459                                   (genericptr_t)&fardist);
460
461                     /* here gx == FARAWAY e.g. when dog is in a vault */
462                     if (gx == FARAWAY || (gx == omx && gy == omy)) {
463                         gx = u.ux;
464                         gy = u.uy;
465                     } else if(edog) {
466                         edog->ogoal.x = gx;
467                         edog->ogoal.y = gy;
468                     }
469                 }
470             }
471         } else if(edog) {
472             edog->ogoal.x = 0;
473         }
474         return appr;
475 }
476
477 /* return 0 (no move), 1 (move) or 2 (dead) */
478 int
479 dog_move(mtmp, after)
480 register struct monst *mtmp;
481 register int after;     /* this is extra fast monster movement */
482 {
483         int omx, omy;           /* original mtmp position */
484         int appr, whappr, udist;
485         int i, j, k;
486         register struct edog *edog = EDOG(mtmp);
487         struct obj *obj = (struct obj *) 0;
488         xchar otyp;
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;
494         coord poss[9];
495         long info[9], allowflags;
496 #define GDIST(x,y) (dist2(x,y,gx,gy))
497
498         /*
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.)
504          */
505         has_edog = !mtmp->isminion;
506
507         omx = mtmp->mx;
508         omy = mtmp->my;
509         if (has_edog && dog_hunger(mtmp, edog)) return(2);      /* starved */
510
511         udist = distu(omx,omy);
512 #ifdef STEED
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);
517                 return (1);
518             }
519             udist = 1;
520         } else
521 #endif
522         /* maybe we tamed him while being swallowed --jgm */
523         if (!udist) return(0);
524
525         nix = omx;      /* set before newdogpos */
526         niy = omy;
527         cursemsg[0] = FALSE;    /* lint suppression */
528         info[0] = 0;            /* ditto */
529
530         if (has_edog) {
531             j = dog_invent(mtmp, edog, udist);
532             if (j == 2) return 2;               /* died */
533             else if (j == 1) goto newdogpos;    /* eating something */
534
535             whappr = (monstermoves - edog->whistletime < 5);
536         } else
537             whappr = 0;
538
539         appr = dog_goal(mtmp, has_edog ? edog : (struct edog *)0,
540                                                         after, udist, whappr);
541         if (appr == -2) return(0);
542
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;
549             if (!has_edog) {
550                 coord mm;
551                 /* Guardian angel refuses to be conflicted; rather,
552                  * it disappears, angrily, and sends in some nasties
553                  */
554                 if (canspotmon(mtmp)) {
555                     pline("%s rebukes you, saying:", Monnam(mtmp));
556                     verbalize("Since you desire conflict, have some more!");
557                 }
558                 mongone(mtmp);
559                 i = rnd(4);
560                 while(i--) {
561                     mm.x = u.ux;
562                     mm.y = u.uy;
563                     if(enexto(&mm, mm.x, mm.y, &mons[PM_ANGEL]))
564                         (void) mk_roamer(&mons[PM_ANGEL], u.ualign.type,
565                                          mm.x, mm.y, FALSE);
566                 }
567                 return(2);
568
569             }
570         }
571         if (!Conflict && !mtmp->mconf &&
572             mtmp == u.ustuck && !sticks(youmonst.data)) {
573             unstuck(mtmp);      /* swallowed case handled above */
574             You("get released!");
575         }
576         if (!nohands(mtmp->data) && !verysmall(mtmp->data)) {
577                 allowflags |= OPENDOOR;
578                 if (m_carrying(mtmp, SKELETON_KEY)) allowflags |= BUSTDOOR;
579         }
580         if (is_giant(mtmp->data)) allowflags |= BUSTDOOR;
581         if (tunnels(mtmp->data)) allowflags |= ALLOW_DIG;
582         cnt = mfndpos(mtmp, poss, info, allowflags);
583
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.
587          */
588         uncursedcnt = 0;
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;
593                 uncursedcnt++;
594         }
595
596         chcnt = 0;
597         chi = -1;
598         nidist = GDIST(nix,niy);
599
600         for (i = 0; i < cnt; i++) {
601                 nx = poss[i].x;
602                 ny = poss[i].y;
603                 cursemsg[i] = FALSE;
604
605                 /* if leashed, we drag him along. */
606                 if (mtmp->mleashed && distu(nx, ny) > 4) continue;
607
608                 /* if a guardian, try to stay close by choice */
609                 if (!has_edog &&
610                     (j = distu(nx, ny)) > 16 && j >= udist) continue;
611
612                 if ((info[i] & ALLOW_M) && MON_AT(nx, ny)) {
613                     int mstatus;
614                     register struct monst *mtmp2 = m_at(nx,ny);
615
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)))
628                         continue;
629
630                     if (after) return(0); /* hit only once each move */
631
632                     notonhead = 0;
633                     mstatus = mattackm(mtmp, mtmp2);
634
635                     /* aggressor (pet) died */
636                     if (mstatus & MM_AGR_DIED) return 2;
637
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;
645                     }
646
647                     return 0;
648                 }
649
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.)
656                      */
657                     struct trap *trap;
658
659                     if ((info[i] & ALLOW_TRAPS) && (trap = t_at(nx,ny))) {
660                         if (mtmp->mleashed) {
661                             if (flags.soundok) whimper(mtmp);
662                         } else
663                             /* 1/40 chance of stepping on it anyway, in case
664                              * it has to pass one to follow the player...
665                              */
666                             if (trap->tseen && rn2(40)) continue;
667                     }
668                 }
669
670                 /* dog eschews cursed objects, but likes dog food */
671                 /* (minion isn't interested; `cursemsg' stays FALSE) */
672                 if (has_edog)
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 */
679                         nix = nx;
680                         niy = ny;
681                         chi = i;
682                         do_eat = TRUE;
683                         cursemsg[i] = FALSE;    /* not reluctant */
684                         goto newdogpos;
685                     }
686                 }
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;
691
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;
697
698                 j = ((ndist = GDIST(nx,ny)) - nidist) * appr;
699                 if ((j == 0 && !rn2(++chcnt)) || j < 0 ||
700                         (j > 0 && !whappr &&
701                                 ((omx == nix && omy == niy && !rn2(3))
702                                         || !rn2(12))
703                         )) {
704                         nix = nx;
705                         niy = ny;
706                         nidist = ndist;
707                         if(j < 0) chcnt = 0;
708                         chi = i;
709                 }
710         nxti:   ;
711         }
712 newdogpos:
713         if (nix != omx || niy != omy) {
714                 struct obj *mw_tmp;
715
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);
721                         }
722                         (void) mattacku(mtmp);
723                         return(0);
724                 }
725                 if (!m_in_out_region(mtmp, nix, niy))
726                     return 1;
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;
740                     }
741                     if (mtmp->weapon_check >= NEED_PICK_AXE &&
742                         mon_wield_item(mtmp))
743                         return 0;
744                 }
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.
756                  */
757                 if (do_eat) {
758                     if (dog_eat(mtmp, obj, omx, omy, FALSE) == 2) return 2;
759                 }
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.
763                  */
764                 coord cc;
765
766                 nx = sgn(omx - u.ux);
767                 ny = sgn(omy - u.uy);
768                 cc.x = u.ux + nx;
769                 cc.y = u.uy + ny;
770                 if (goodpos(cc.x, cc.y, mtmp, 0)) goto dognext;
771
772                 i  = xytod(nx, ny);
773                 for (j = (i + 7)%8; j < (i + 1)%8; j++) {
774                         dtoxy(&cc, j);
775                         if (goodpos(cc.x, cc.y, mtmp, 0)) goto dognext;
776                 }
777                 for (j = (i + 6)%8; j < (i + 2)%8; j++) {
778                         dtoxy(&cc, j);
779                         if (goodpos(cc.x, cc.y, mtmp, 0)) goto dognext;
780                 }
781                 cc.x = mtmp->mx;
782                 cc.y = mtmp->my;
783 dognext:
784                 if (!m_in_out_region(mtmp, nix, niy))
785                   return 1;
786                 remove_monster(mtmp->mx, mtmp->my);
787                 place_monster(mtmp, cc.x, cc.y);
788                 newsym(cc.x,cc.y);
789                 set_apparxy(mtmp);
790         }
791         return(1);
792 }
793
794 /* check if a monster could pick up objects from a location */
795 STATIC_OVL boolean
796 could_reach_item(mon, nx, ny)
797 struct monst *mon;
798 xchar nx, ny;
799 {
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)))
803         return TRUE;
804     return FALSE;
805 }
806
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
812  * deep.
813  */
814 STATIC_OVL boolean
815 can_reach_location(mon, mx, my, fx, fy)
816 struct monst *mon;
817 xchar mx, my, fx, fy;
818 {
819     int i, j;
820     int dist;
821
822     if (mx == fx && my == fy) return TRUE;
823     if (!isok(mx, my)) return FALSE; /* should not happen */
824     
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++) {
828             if (!isok(i, j))
829                 continue;
830             if (dist2(i, j, fx, fy) >= dist)
831                 continue;
832             if (IS_ROCK(levl[i][j].typ) && !passes_walls(mon->data) &&
833                                     (!may_dig(i,j) || !tunnels(mon->data)))
834                 continue;
835             if (IS_DOOR(levl[i][j].typ) &&
836                                 (levl[i][j].doormask & (D_CLOSED | D_LOCKED)))
837                 continue;
838             if (!could_reach_item(mon, i, j))
839                 continue;
840             if (can_reach_location(mon, i, j, fx, fy))
841                 return TRUE;
842         }
843     }
844     return FALSE;
845 }
846
847 #endif /* OVL0 */
848 #ifdef OVLB
849
850 /*ARGSUSED*/    /* do_clear_area client */
851 STATIC_PTR void
852 wantdoor(x, y, distance)
853 int x, y;
854 genericptr_t distance;
855 {
856     int ndist;
857
858     if (*(int*)distance > (ndist = distu(x, y))) {
859         gx = x;
860         gy = y;
861         *(int*)distance = ndist;
862     }
863 }
864
865 #endif /* OVLB */
866
867 /*dogmove.c*/