1    | /*	SCCS Id: @(#)dogmove.c	3.3	97/05/25	*/
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   | 
20   | STATIC_OVL struct obj *
21   | DROPPABLES(mon)
22   | register struct monst *mon;
23   | {
24   | 	register struct obj *obj;
25   | 	struct obj *wep = MON_WEP(mon);
26   | 	boolean item1 = FALSE, item2 = FALSE;
27   | 
28   | 	if (is_animal(mon->data) || mindless(mon->data))
29   | 		item1 = item2 = TRUE;
30   | 	if (!tunnels(mon->data) || !needspick(mon->data))
31   | 		item1 = TRUE;
32   | 	for(obj = mon->minvent; obj; obj = obj->nobj) {
33   | 		if (!item1 && is_pick(obj) && (obj->otyp != DWARVISH_MATTOCK
34   | 						|| !which_armor(mon, W_ARMS))) {
35   | 			item1 = TRUE;
36   | 			continue;
37   | 		}
38   | 		if (!item2 && obj->otyp == UNICORN_HORN && !obj->cursed) {
39   | 			item2 = TRUE;
40   | 			continue;
41   | 		}
42   | 		if (!obj->owornmask && obj != wep) return obj;
43   | 	}
44   | 	return (struct obj *)0;
45   | }
46   | 
47   | static NEARDATA const char nofetch[] = { BALL_CLASS, CHAIN_CLASS, ROCK_CLASS, 0 };
48   | 
49   | #endif /* OVL0 */
50   | 
51   | STATIC_OVL boolean FDECL(cursed_object_at, (int, int));
52   | 
53   | STATIC_VAR xchar gtyp, gx, gy;	/* type and position of dog's current goal */
54   | 
55   | STATIC_PTR void FDECL(wantdoor, (int, int, genericptr_t));
56   | 
57   | #ifdef OVLB
58   | STATIC_OVL boolean
59   | cursed_object_at(x, y)
60   | int x, y;
61   | {
62   | 	struct obj *otmp;
63   | 
64   | 	for(otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
65   | 		if (otmp->cursed) return TRUE;
66   | 	return FALSE;
67   | }
68   | 
69   | int
70   | dog_nutrition(mtmp, obj)
71   | struct monst *mtmp;
72   | struct obj *obj;
73   | {
74   | 	int nutrit;
75   | 
76   | 	/*
77   | 	 * It is arbitrary that the pet takes the same length of time to eat
78   | 	 * as a human, but gets more nutritional value.
79   | 	 */
80   | 	if (obj->oclass == FOOD_CLASS) {
81   | 	    if(obj->otyp == CORPSE) {
82   | 		mtmp->meating = 3 + (mons[obj->corpsenm].cwt >> 6);
83   | 		nutrit = mons[obj->corpsenm].cnutrit;
84   | 	    } else {
85   | 		mtmp->meating = objects[obj->otyp].oc_delay;
86   | 		nutrit = objects[obj->otyp].oc_nutrition;
87   | 	    }
88   | 	    switch(mtmp->data->msize) {
89   | 		case MZ_TINY: nutrit *= 8; break;
90   | 		case MZ_SMALL: nutrit *= 6; break;
91   | 		default:
92   | 		case MZ_MEDIUM: nutrit *= 5; break;
93   | 		case MZ_LARGE: nutrit *= 4; break;
94   | 		case MZ_HUGE: nutrit *= 3; break;
95   | 		case MZ_GIGANTIC: nutrit *= 2; break;
96   | 	    }
97   | 	    if(obj->oeaten) {
98   | 		mtmp->meating = eaten_stat(mtmp->meating, obj);
99   | 		nutrit = eaten_stat(nutrit, obj);
100  | 	    }
101  | 	} else if (obj->oclass == GOLD_CLASS) {
102  | 	    mtmp->meating = (int)(obj->quan/2000) + 1;
103  | 	    if (mtmp->meating < 0) mtmp->meating = 1;
104  | 	    nutrit = (int)(obj->quan/20);
105  | 	    if (nutrit < 0) nutrit = 0;
106  | 	} else {
107  | 	    /* Unusual pet such as gelatinous cube eating odd stuff.
108  | 	     * meating made consistent with wild monsters in mon.c.
109  | 	     * nutrit made consistent with polymorphed player nutrit in
110  | 	     * eat.c.  (This also applies to pets eating gold.)
111  | 	     */
112  | 	    mtmp->meating = obj->owt/20 + 1;
113  | 	    nutrit = 5*objects[obj->otyp].oc_nutrition;
114  | 	}
115  | 	return nutrit;
116  | }
117  | 
118  | /* returns 2 if pet dies, otherwise 1 */
119  | int
120  | dog_eat(mtmp, obj, x, y, devour)
121  | register struct monst *mtmp;
122  | register struct obj * obj;
123  | int x, y;
124  | boolean devour;
125  | {
126  | 	register struct edog *edog = EDOG(mtmp);
127  | 	boolean poly = FALSE, grow = FALSE, heal = FALSE;
128  | 	int nutrit;
129  | 
130  | 	if(edog->hungrytime < monstermoves)
131  | 	    edog->hungrytime = monstermoves;
132  | 	nutrit = dog_nutrition(mtmp, obj);
133  | 	poly = polyfodder(obj);
134  | 	grow = mlevelgain(obj);
135  | 	heal = mhealup(obj);
136  | 	if (devour) {
137  | 	    if (mtmp->meating > 1) mtmp->meating /= 2;
138  | 	    if (nutrit > 1) nutrit = (nutrit * 3) / 4;
139  | 	}
140  | 	edog->hungrytime += nutrit;
141  | 	mtmp->mconf = 0;
142  | 	if (edog->mhpmax_penalty) {
143  | 	    /* no longer starving */
144  | 	    mtmp->mhpmax += edog->mhpmax_penalty;
145  | 	    edog->mhpmax_penalty = 0;
146  | 	}
147  | 	if (mtmp->mflee && mtmp->mfleetim > 1) mtmp->mfleetim /= 2;
148  | 	if (mtmp->mtame < 20) mtmp->mtame++;
149  | 	if (x != mtmp->mx || y != mtmp->my) {	/* moved & ate on same turn */
150  | 	    newsym(x, y);
151  | 	    newsym(mtmp->mx, mtmp->my);
152  | 	}
153  | 	if (is_pool(x, y) && !Underwater) {
154  | 	    /* Don't print obj */
155  | 	    /* TODO: Reveal presence of sea monster (especially sharks) */
156  | 	} else
157  | 	/* hack: observe the action if either new or old location is in view */
158  | 	if (cansee(x, y) || cansee(mtmp->mx, mtmp->my))
159  | 	    pline("%s %s %s.", Monnam(mtmp),
160  | 		  devour ? "devours" : "eats",
161  | 		  (obj->oclass == FOOD_CLASS) ?
162  | 			singular(obj, doname) : doname(obj));
163  | 	/* It's a reward if it's DOGFOOD and the player dropped/threw it. */
164  | 	/* We know the player had it if invlet is set -dlc */
165  | 	if(dogfood(mtmp,obj) == DOGFOOD && obj->invlet)
166  | #ifdef LINT
167  | 	    edog->apport = 0;
168  | #else
169  | 	    edog->apport += (int)(200L/
170  | 		((long)edog->dropdist + monstermoves - edog->droptime));
171  | #endif
172  | 	if (mtmp->data == &mons[PM_RUST_MONSTER] && obj->oerodeproof) {
173  | 	    /* The object's rustproofing is gone now */
174  | 	    obj->oerodeproof = 0;
175  | 	    mtmp->mstun = 1;
176  | 	    if (canseemon(mtmp) && flags.verbose) {
177  | 		pline("%s spits %s out in disgust!",
178  | 		      Monnam(mtmp), distant_name(obj,doname));
179  | 	    }
180  | 	} else if (obj == uball) {
181  | 	    unpunish();
182  | 	    delobj(obj);
183  | 	} else if (obj == uchain)
184  | 	    unpunish();
185  | 	else if (obj->quan > 1L && obj->oclass == FOOD_CLASS)
186  | 	    obj->quan--;
187  | 	else
188  | 	    delobj(obj);
189  | 
190  | 	if (poly) {
191  | 	    char oldpet[BUFSZ];
192  | #ifdef STEED
193  | 	    long mw = mtmp->misc_worn_check;
194  | 
195  | 	    mtmp->misc_worn_check &= ~W_SADDLE;
196  | #endif
197  | 	    Strcpy(oldpet, Monnam(mtmp));
198  | #ifdef STEED
199  | 	    mtmp->misc_worn_check = mw;
200  | #endif
201  | 	    if (newcham(mtmp, (struct permonst *)0) &&
202  | 			cansee(mtmp->mx, mtmp->my)) {
203  | 		uchar save_mnamelth = mtmp->mnamelth;
204  | 		mtmp->mnamelth = 0;
205  | 		pline("%s turns into %s!", oldpet, a_monnam(mtmp));
206  | 		mtmp->mnamelth = save_mnamelth;
207  | 	    }
208  | 	}
209  | 	/* limit "instant" growth to prevent potential abuse */
210  | 	if (grow && (int) mtmp->m_lev < (int)mtmp->data->mlevel + 15) {
211  | 	    if (!grow_up(mtmp, (struct monst *)0)) return 2;
212  | 	}
213  | 	if (heal) mtmp->mhp = mtmp->mhpmax;
214  | 	return 1;
215  | }
216  | 
217  | #endif /* OVLB */
218  | #ifdef OVL0
219  | 
220  | /* hunger effects -- returns TRUE on starvation */
221  | STATIC_OVL boolean
222  | dog_hunger(mtmp, edog)
223  | register struct monst *mtmp;
224  | register struct edog *edog;
225  | {
226  | 	if (monstermoves > edog->hungrytime + 500) {
227  | 	    if (!carnivorous(mtmp->data) && !herbivorous(mtmp->data)) {
228  | 		edog->hungrytime = monstermoves + 500;
229  | 		/* but not too high; it might polymorph */
230  | 	    } else if (!edog->mhpmax_penalty) {
231  | 		/* starving pets are limited in healing */
232  | 		int newmhpmax = mtmp->mhpmax / 3;
233  | 		mtmp->mconf = 1;
234  | 		edog->mhpmax_penalty = mtmp->mhpmax - newmhpmax;
235  | 		mtmp->mhpmax = newmhpmax;
236  | 		if (mtmp->mhp > mtmp->mhpmax)
237  | 		    mtmp->mhp = mtmp->mhpmax;
238  | 		if (mtmp->mhp < 1) goto dog_died;
239  | 		if (cansee(mtmp->mx, mtmp->my))
240  | 		    pline("%s is confused from hunger.", Monnam(mtmp));
241  | 		else if (couldsee(mtmp->mx, mtmp->my))
242  | 		    beg(mtmp);
243  | 		else
244  | 		    You_feel("worried about %s.", y_monnam(mtmp));
245  | 	    } else if (monstermoves > edog->hungrytime + 750 || mtmp->mhp < 1) {
246  | 	    dog_died:
247  | 		if (mtmp->mleashed)
248  | 		    Your("leash goes slack.");
249  | 		else if (cansee(mtmp->mx, mtmp->my))
250  | 		    pline("%s dies%s.", Monnam(mtmp),
251  | 			    (mtmp->mhp >= 1) ? "" : " from hunger");
252  | 		else
253  | 		    You_feel("%s for a moment.",
254  | 			Hallucination ? "bummed" : "sad");
255  | 		mondied(mtmp);
256  | 		return(TRUE);
257  | 	    }
258  | 	}
259  | 	return(FALSE);
260  | }
261  | 
262  | /* do something with object (drop, pick up, eat) at current position
263  |  * returns 1 if object eaten (since that counts as dog's move), 2 if died
264  |  */
265  | STATIC_OVL int
266  | dog_invent(mtmp, edog, udist)
267  | register struct monst *mtmp;
268  | register struct edog *edog;
269  | int udist;
270  | {
271  | 	register int omx, omy;
272  | 	struct obj *obj;
273  | 
274  | 	if (mtmp->msleeping || !mtmp->mcanmove) return(0);
275  | 
276  | 	omx = mtmp->mx;
277  | 	omy = mtmp->my;
278  | 
279  | 	/* if we are carrying sth then we drop it (perhaps near @) */
280  | 	/* Note: if apport == 1 then our behaviour is independent of udist */
281  | 	/* Use udist+1 so steed won't cause divide by zero */
282  | 	if(DROPPABLES(mtmp) || mtmp->mgold) {
283  | 	    if (!rn2(udist+1) || !rn2(edog->apport))
284  | 		if(rn2(10) < edog->apport){
285  | 		    relobj(mtmp, (int)mtmp->minvis, TRUE);
286  | 		    if(edog->apport > 1) edog->apport--;
287  | 		    edog->dropdist = udist;		/* hpscdi!jon */
288  | 		    edog->droptime = monstermoves;
289  | 		}
290  | 	} else {
291  | 	    if((obj=level.objects[omx][omy]) && !index(nofetch,obj->oclass)
292  | #ifdef MAIL
293  | 			&& obj->otyp != SCR_MAIL
294  | #endif
295  | 									){
296  | 		if (dogfood(mtmp, obj) <= CADAVER)
297  | 		    return dog_eat(mtmp, obj, omx, omy, FALSE);
298  | 
299  | 		if(can_carry(mtmp, obj) && !obj->cursed &&
300  | 			!is_pool(mtmp->mx,mtmp->my)) {
301  | 		    if(rn2(20) < edog->apport+3) {
302  | 			if (rn2(udist) || !rn2(edog->apport)) {
303  | 			    if (cansee(omx, omy) && flags.verbose)
304  | 				pline("%s picks up %s.", Monnam(mtmp),
305  | 				    distant_name(obj, doname));
306  | 			    obj_extract_self(obj);
307  | 			    newsym(omx,omy);
308  | 			    (void) mpickobj(mtmp,obj);
309  | 			    if (attacktype(mtmp->data, AT_WEAP) &&
310  | 					mtmp->weapon_check == NEED_WEAPON) {
311  | 				mtmp->weapon_check = NEED_HTH_WEAPON;
312  | 				(void) mon_wield_item(mtmp);
313  | 			    }
314  | 			    m_dowear(mtmp, FALSE);
315  | 			}
316  | 		    }
317  | 		}
318  | 	    }
319  | 	}
320  | 	return 0;
321  | }
322  | 
323  | /* set dog's goal -- gtyp, gx, gy
324  |  * returns -1/0/1 (dog's desire to approach player) or -2 (abort move)
325  |  */
326  | STATIC_OVL int
327  | dog_goal(mtmp, edog, after, udist, whappr)
328  | register struct monst *mtmp;
329  | struct edog *edog;
330  | int after, udist, whappr;
331  | {
332  | 	register int omx, omy;
333  | 	boolean in_masters_sight;
334  | 	register struct obj *obj;
335  | 	xchar otyp;
336  | 	int appr;
337  | 
338  | 
339  | #ifdef STEED
340  | 	/* Steeds don't move on their own will */
341  | 	if (mtmp == u.usteed)
342  | 		return (-2);
343  | #endif
344  | 
345  | 	omx = mtmp->mx;
346  | 	omy = mtmp->my;
347  | 
348  | 	in_masters_sight = couldsee(omx, omy);
349  | 
350  | 	if (!edog || mtmp->mleashed) {	/* he's not going anywhere... */
351  | 	    gtyp = APPORT;
352  | 	    gx = u.ux;
353  | 	    gy = u.uy;
354  | 	} else {
355  | #define DDIST(x,y) (dist2(x,y,omx,omy))
356  | #define SQSRCHRADIUS 5
357  | 	    int min_x, max_x, min_y, max_y;
358  | 	    register int nx, ny;
359  | 
360  | 	    gtyp = UNDEF;	/* no goal as yet */
361  | 	    gx = gy = 0;	/* suppress 'used before set' message */
362  | 
363  | 	    if ((min_x = omx - SQSRCHRADIUS) < 0) min_x = 0;
364  | 	    if ((max_x = omx + SQSRCHRADIUS) >= COLNO) max_x = COLNO - 1;
365  | 	    if ((min_y = omy - SQSRCHRADIUS) < 0) min_y = 0;
366  | 	    if ((max_y = omy + SQSRCHRADIUS) >= ROWNO) max_y = ROWNO - 1;
367  | 
368  | 	    /* nearby food is the first choice, then other objects */
369  | 	    for (obj = fobj; obj; obj = obj->nobj) {
370  | 		nx = obj->ox;
371  | 		ny = obj->oy;
372  | 		if (nx >= min_x && nx <= max_x && ny >= min_y && ny <= max_y) {
373  | 		    otyp = dogfood(mtmp, obj);
374  | 		    if (otyp > gtyp || otyp == UNDEF)
375  | 			continue;
376  | 		    if (cursed_object_at(nx, ny))
377  | 			continue;
378  | 		    if (otyp < MANFOOD) {
379  | 			if (otyp < gtyp || DDIST(nx,ny) < DDIST(gx,gy)) {
380  | 			    gx = nx;
381  | 			    gy = ny;
382  | 			    gtyp = otyp;
383  | 			}
384  | 		    } else if(gtyp == UNDEF && in_masters_sight &&
385  | 			      !mtmp->minvent &&
386  | 			      (!levl[omx][omy].lit || levl[u.ux][u.uy].lit) &&
387  | 			      (otyp == MANFOOD || m_cansee(mtmp, nx, ny)) &&
388  | 			      edog->apport > rn2(8) &&
389  | 			      can_carry(mtmp,obj)) {
390  | 			gx = nx;
391  | 			gy = ny;
392  | 			gtyp = APPORT;
393  | 		    }
394  | 		}
395  | 	    }
396  | 	}
397  | 
398  | 	/* follow player if appropriate */
399  | 	if (gtyp == UNDEF ||
400  | 	    (gtyp != DOGFOOD && gtyp != APPORT && monstermoves < edog->hungrytime)) {
401  | 		gx = u.ux;
402  | 		gy = u.uy;
403  | 		if (after && udist <= 4 && gx == u.ux && gy == u.uy)
404  | 			return(-2);
405  | 		appr = (udist >= 9) ? 1 : (mtmp->mflee) ? -1 : 0;
406  | 		if (udist > 1) {
407  | 			if (!IS_ROOM(levl[u.ux][u.uy].typ) || !rn2(4) ||
408  | 			   whappr ||
409  | 			   (mtmp->minvent && rn2(edog->apport)))
410  | 				appr = 1;
411  | 		}
412  | 		/* if you have dog food it'll follow you more closely */
413  | 		if (appr == 0) {
414  | 			obj = invent;
415  | 			while (obj) {
416  | 				if(dogfood(mtmp, obj) == DOGFOOD) {
417  | 					appr = 1;
418  | 					break;
419  | 				}
420  | 				obj = obj->nobj;
421  | 			}
422  | 		}
423  | 	} else
424  | 	    appr = 1;	/* gtyp != UNDEF */
425  | 	if(mtmp->mconf)
426  | 	    appr = 0;
427  | 
428  | #define FARAWAY (COLNO + 2)		/* position outside screen */
429  | 	if (gx == u.ux && gy == u.uy && !in_masters_sight) {
430  | 	    register coord *cp;
431  | 
432  | 	    cp = gettrack(omx,omy);
433  | 	    if (cp) {
434  | 		gx = cp->x;
435  | 		gy = cp->y;
436  | 		if(edog) edog->ogoal.x = 0;
437  | 	    } else {
438  | 		/* assume master hasn't moved far, and reuse previous goal */
439  | 		if(edog && edog->ogoal.x &&
440  | 		   ((edog->ogoal.x != omx) || (edog->ogoal.y != omy))) {
441  | 		    gx = edog->ogoal.x;
442  | 		    gy = edog->ogoal.y;
443  | 		    edog->ogoal.x = 0;
444  | 		} else {
445  | 		    int fardist = FARAWAY * FARAWAY;
446  | 		    gx = gy = FARAWAY; /* random */
447  | 		    do_clear_area(omx, omy, 9, wantdoor,
448  | 				  (genericptr_t)&fardist);
449  | 
450  | 		    /* here gx == FARAWAY e.g. when dog is in a vault */
451  | 		    if (gx == FARAWAY || (gx == omx && gy == omy)) {
452  | 			gx = u.ux;
453  | 			gy = u.uy;
454  | 		    } else if(edog) {
455  | 			edog->ogoal.x = gx;
456  | 			edog->ogoal.y = gy;
457  | 		    }
458  | 		}
459  | 	    }
460  | 	} else if(edog) {
461  | 	    edog->ogoal.x = 0;
462  | 	}
463  | 	return appr;
464  | }
465  | 
466  | /* return 0 (no move), 1 (move) or 2 (dead) */
467  | int
468  | dog_move(mtmp, after)
469  | register struct monst *mtmp;
470  | register int after;	/* this is extra fast monster movement */
471  | {
472  | 	int omx, omy;		/* original mtmp position */
473  | 	int appr, whappr, udist;
474  | 	int i, j, k;
475  | 	register struct edog *edog = EDOG(mtmp);
476  | 	struct obj *obj = (struct obj *) 0;
477  | 	xchar otyp;
478  | 	boolean has_edog, cursemsg[9], do_eat = FALSE;
479  | 	xchar nix, niy;		/* position mtmp is (considering) moving to */
480  | 	register int nx, ny;	/* temporary coordinates */
481  | 	xchar cnt, uncursedcnt, chcnt;
482  | 	int chi = -1, nidist, ndist;
483  | 	coord poss[9];
484  | 	long info[9], allowflags;
485  | #define GDIST(x,y) (dist2(x,y,gx,gy))
486  | 
487  | 	/*
488  | 	 * Tame Angels have isminion set and an ispriest structure instead of
489  | 	 * an edog structure.  Fortunately, guardian Angels need not worry
490  | 	 * about mundane things like eating and fetching objects, and can
491  | 	 * spend all their energy defending the player.  (They are the only
492  | 	 * monsters with other structures that can be tame.)
493  | 	 */
494  | 	has_edog = !mtmp->isminion;
495  | 
496  | 	omx = mtmp->mx;
497  | 	omy = mtmp->my;
498  | 	if (has_edog && dog_hunger(mtmp, edog)) return(2);	/* starved */
499  | 
500  | 	udist = distu(omx,omy);
501  | #ifdef STEED
502  | 	/* Let steeds eat and maybe throw rider during Conflict */
503  | 	if (mtmp == u.usteed) {
504  | 	    if (Conflict && !resist(mtmp, RING_CLASS, 0, 0)) {
505  | 		dismount_steed(DISMOUNT_THROWN);
506  | 		return (1);
507  | 	    }
508  | 	    udist = 1;
509  | 	} else
510  | #endif
511  | 	/* maybe we tamed him while being swallowed --jgm */
512  | 	if (!udist) return(0);
513  | 
514  | 	nix = omx;	/* set before newdogpos */
515  | 	niy = omy;
516  | 	cursemsg[0] = FALSE;	/* lint suppression */
517  | 	info[0] = 0;		/* ditto */
518  | 
519  | 	if (has_edog) {
520  | 	    j = dog_invent(mtmp, edog, udist);
521  | 	    if (j == 2) return 2;		/* died */
522  | 	    else if (j == 1) goto newdogpos;	/* eating something */
523  | 
524  | 	    whappr = (monstermoves - edog->whistletime < 5);
525  | 	} else
526  | 	    whappr = 0;
527  | 
528  | 	appr = dog_goal(mtmp, has_edog ? edog : (struct edog *)0,
529  | 							after, udist, whappr);
530  | 	if (appr == -2) return(0);
531  | 
532  | 	allowflags = ALLOW_M | ALLOW_TRAPS | ALLOW_SSM | ALLOW_SANCT;
533  | 	if (passes_walls(mtmp->data)) allowflags |= (ALLOW_ROCK|ALLOW_WALL);
534  | 	if (throws_rocks(mtmp->data)) allowflags |= ALLOW_ROCK;
535  | 	if (Conflict && !resist(mtmp, RING_CLASS, 0, 0)) {
536  | 	    allowflags |= ALLOW_U;
537  | 	    if (!has_edog) {
538  | 		coord mm;
539  | 		/* Guardian angel refuses to be conflicted; rather,
540  | 		 * it disappears, angrily, and sends in some nasties
541  | 		 */
542  | 		if (canspotmon(mtmp)) {
543  | 		    pline("%s rebukes you, saying:", Monnam(mtmp));
544  | 		    verbalize("Since you desire conflict, have some more!");
545  | 		}
546  | 		mongone(mtmp);
547  | 		i = rnd(4);
548  | 		while(i--) {
549  | 		    mm.x = u.ux;
550  | 		    mm.y = u.uy;
551  | 		    if(enexto(&mm, mm.x, mm.y, &mons[PM_ANGEL]))
552  | 			(void) mk_roamer(&mons[PM_ANGEL], u.ualign.type,
553  | 					 mm.x, mm.y, FALSE);
554  | 		}
555  | 		return(2);
556  | 
557  | 	    }
558  | 	}
559  | 	if (!nohands(mtmp->data) && !verysmall(mtmp->data)) {
560  | 		allowflags |= OPENDOOR;
561  | 		if (m_carrying(mtmp, SKELETON_KEY)) allowflags |= BUSTDOOR;
562  | 	}
563  | 	if (is_giant(mtmp->data)) allowflags |= BUSTDOOR;
564  | 	if (tunnels(mtmp->data) && (!needspick(mtmp->data) ||
565  | 					m_carrying(mtmp, PICK_AXE) ||
566  | 					m_carrying(mtmp, DWARVISH_MATTOCK)))
567  | 		allowflags |= ALLOW_DIG;
568  | 	cnt = mfndpos(mtmp, poss, info, allowflags);
569  | 
570  | 	/* Normally dogs don't step on cursed items, but if they have no
571  | 	 * other choice they will.  This requires checking ahead of time
572  | 	 * to see how many uncursed item squares are around.
573  | 	 */
574  | 	uncursedcnt = 0;
575  | 	for (i = 0; i < cnt; i++) {
576  | 		nx = poss[i].x; ny = poss[i].y;
577  | 		if (MON_AT(nx,ny) && !(info[i] & ALLOW_M)) continue;
578  | 		if (cursed_object_at(nx, ny)) continue;
579  | 		uncursedcnt++;
580  | 	}
581  | 
582  | 	chcnt = 0;
583  | 	chi = -1;
584  | 	nidist = GDIST(nix,niy);
585  | 
586  | 	for (i = 0; i < cnt; i++) {
587  | 		nx = poss[i].x;
588  | 		ny = poss[i].y;
589  | 		cursemsg[i] = FALSE;
590  | 
591  | 		/* if leashed, we drag him along. */
592  | 		if (mtmp->mleashed && distu(nx, ny) > 4) continue;
593  | 
594  | 		/* if a guardian, try to stay close by choice */
595  | 		if (!has_edog &&
596  | 		    (j = distu(nx, ny)) > 16 && j >= udist) continue;
597  | 
598  | 		if ((info[i] & ALLOW_M) && MON_AT(nx, ny)) {
599  | 		    int mstatus;
600  | 		    register struct monst *mtmp2 = m_at(nx,ny);
601  | 
602  | 		    if ((int)mtmp2->m_lev >= (int)mtmp->m_lev+2 ||
603  | 			(mtmp2->data == &mons[PM_FLOATING_EYE] && rn2(10) &&
604  | 			 mtmp->mcansee && haseyes(mtmp->data) && mtmp2->mcansee
605  | 			 && (perceives(mtmp->data) || !mtmp2->minvis)) ||
606  | 			(mtmp2->data==&mons[PM_GELATINOUS_CUBE] && rn2(10)) ||
607  | 			(max_passive_dmg(mtmp2, mtmp) >= mtmp->mhp) ||
608  | 			((mtmp->mhp*4 < mtmp->mhpmax
609  | 			  || mtmp2->data->msound == MS_GUARDIAN
610  | 			  || mtmp2->data->msound == MS_LEADER) &&
611  | 			 mtmp2->mpeaceful && !Conflict) ||
612  | 			   (touch_petrifies(mtmp2->data) &&
613  | 				!resists_ston(mtmp)))
614  | 			continue;
615  | 
616  | 		    if (after) return(0); /* hit only once each move */
617  | 
618  | 		    notonhead = 0;
619  | 		    mstatus = mattackm(mtmp, mtmp2);
620  | 
621  | 		    /* aggressor (pet) died */
622  | 		    if (mstatus & MM_AGR_DIED) return 2;
623  | 
624  | 		    if ((mstatus & MM_HIT) && !(mstatus & MM_DEF_DIED) &&
625  | 			    rn2(4) && mtmp2->mlstmv != monstermoves &&
626  | 			    !onscary(mtmp->mx, mtmp->my, mtmp2) &&
627  | 			    /* monnear check needed: long worms hit on tail */
628  | 			    monnear(mtmp2, mtmp->mx, mtmp->my)) {
629  | 			mstatus = mattackm(mtmp2, mtmp);  /* return attack */
630  | 			if (mstatus & MM_DEF_DIED) return 2;
631  | 		    }
632  | 
633  | 		    return 0;
634  | 		}
635  | 
636  | 		{   /* Dog avoids harmful traps, but perhaps it has to pass one
637  | 		     * in order to follow player.  (Non-harmful traps do not
638  | 		     * have ALLOW_TRAPS in info[].)  The dog only avoids the
639  | 		     * trap if you've seen it, unlike enemies who avoid traps
640  | 		     * if they've seen some trap of that type sometime in the
641  | 		     * past.  (Neither behavior is really realistic.)
642  | 		     */
643  | 		    struct trap *trap;
644  | 
645  | 		    if ((info[i] & ALLOW_TRAPS) && (trap = t_at(nx,ny))) {
646  | 			if (mtmp->mleashed) {
647  | 			    if (flags.soundok) whimper(mtmp);
648  | 			} else
649  | 			    /* 1/40 chance of stepping on it anyway, in case
650  | 			     * it has to pass one to follow the player...
651  | 			     */
652  | 			    if (trap->tseen && rn2(40)) continue;
653  | 		    }
654  | 		}
655  | 
656  | 		/* dog eschews cursed objects, but likes dog food */
657  | 		/* (minion isn't interested; `cursemsg' stays FALSE) */
658  | 		if (has_edog)
659  | 		for (obj = level.objects[nx][ny]; obj; obj = obj->nexthere) {
660  | 		    if (obj->cursed) cursemsg[i] = TRUE;
661  | 		    else if ((otyp = dogfood(mtmp, obj)) < MANFOOD &&
662  | 			     (otyp < ACCFOOD || edog->hungrytime <= monstermoves)) {
663  | 			/* Note: our dog likes the food so much that he
664  | 			 * might eat it even when it conceals a cursed object */
665  | 			nix = nx;
666  | 			niy = ny;
667  | 			chi = i;
668  | 			do_eat = TRUE;
669  | 			cursemsg[i] = FALSE;	/* not reluctant */
670  | 			goto newdogpos;
671  | 		    }
672  | 		}
673  | 		/* didn't find something to eat; if we saw a cursed item and
674  | 		   aren't being forced to walk on it, usually keep looking */
675  | 		if (cursemsg[i] && !mtmp->mleashed && uncursedcnt > 0 &&
676  | 		    rn2(13 * uncursedcnt)) continue;
677  | 
678  | 		/* lessen the chance of backtracking to previous position(s) */
679  | 		k = has_edog ? uncursedcnt : cnt;
680  | 		for (j = 0; j < MTSZ && j < k - 1; j++)
681  | 			if (nx == mtmp->mtrack[j].x && ny == mtmp->mtrack[j].y)
682  | 				if (rn2(MTSZ * (k - j))) goto nxti;
683  | 
684  | 		j = ((ndist = GDIST(nx,ny)) - nidist) * appr;
685  | 		if ((j == 0 && !rn2(++chcnt)) || j < 0 ||
686  | 			(j > 0 && !whappr &&
687  | 				((omx == nix && omy == niy && !rn2(3))
688  | 					|| !rn2(12))
689  | 			)) {
690  | 			nix = nx;
691  | 			niy = ny;
692  | 			nidist = ndist;
693  | 			if(j < 0) chcnt = 0;
694  | 			chi = i;
695  | 		}
696  | 	nxti:	;
697  | 	}
698  | newdogpos:
699  | 	if (nix != omx || niy != omy) {
700  | 		struct obj *mw_tmp;
701  | 
702  | 		if (info[chi] & ALLOW_U) {
703  | 			if (mtmp->mleashed) { /* play it safe */
704  | 				pline("%s breaks loose of %s leash!",
705  | 				      Monnam(mtmp), his[pronoun_gender(mtmp)]);
706  | 				m_unleash(mtmp);
707  | 			}
708  | 			(void) mattacku(mtmp);
709  | 			return(0);
710  | 		}
711  | 		if (!m_in_out_region(mtmp, nix, niy))
712  | 		    return 1;
713  | 		if(IS_ROCK(levl[nix][niy].typ) && may_dig(nix,niy) &&
714  | 		    mtmp->weapon_check != NO_WEAPON_WANTED &&
715  | 		    tunnels(mtmp->data) && needspick(mtmp->data) &&
716  | 			(!(mw_tmp = MON_WEP(mtmp)) || !is_pick(mw_tmp))) {
717  | 		    mtmp->weapon_check = NEED_PICK_AXE;
718  | 		    if (mon_wield_item(mtmp))
719  | 			return 0;
720  | 		}
721  | 		/* insert a worm_move() if worms ever begin to eat things */
722  | 		remove_monster(omx, omy);
723  | 		place_monster(mtmp, nix, niy);
724  | 		if (cursemsg[chi] && (cansee(omx,omy) || cansee(nix,niy)))
725  | 			pline("%s moves only reluctantly.", Monnam(mtmp));
726  | 		for (j=MTSZ-1; j>0; j--) mtmp->mtrack[j] = mtmp->mtrack[j-1];
727  | 		mtmp->mtrack[0].x = omx;
728  | 		mtmp->mtrack[0].y = omy;
729  | 		/* We have to know if the pet's gonna do a combined eat and
730  | 		 * move before moving it, but it can't eat until after being
731  | 		 * moved.  Thus the do_eat flag.
732  | 		 */
733  | 		if (do_eat) {
734  | 		    if (dog_eat(mtmp, obj, omx, omy, FALSE) == 2) return 2;
735  | 		}
736  | 	} else if (mtmp->mleashed && distu(omx, omy) > 4) {
737  | 		/* an incredible kludge, but the only way to keep pooch near
738  | 		 * after it spends time eating or in a trap, etc.
739  | 		 */
740  | 		coord cc;
741  | 
742  | 		nx = sgn(omx - u.ux);
743  | 		ny = sgn(omy - u.uy);
744  | 		cc.x = u.ux + nx;
745  | 		cc.y = u.uy + ny;
746  | 		if (goodpos(cc.x, cc.y, mtmp)) goto dognext;
747  | 
748  | 		i  = xytod(nx, ny);
749  | 		for (j = (i + 7)%8; j < (i + 1)%8; j++) {
750  | 			dtoxy(&cc, j);
751  | 			if (goodpos(cc.x, cc.y, mtmp)) goto dognext;
752  | 		}
753  | 		for (j = (i + 6)%8; j < (i + 2)%8; j++) {
754  | 			dtoxy(&cc, j);
755  | 			if (goodpos(cc.x, cc.y, mtmp)) goto dognext;
756  | 		}
757  | 		cc.x = mtmp->mx;
758  | 		cc.y = mtmp->my;
759  | dognext:
760  | 		if (!m_in_out_region(mtmp, nix, niy))
761  | 		  return 1;
762  | 		remove_monster(mtmp->mx, mtmp->my);
763  | 		place_monster(mtmp, cc.x, cc.y);
764  | 		newsym(cc.x,cc.y);
765  | 		set_apparxy(mtmp);
766  | 	}
767  | 	return(1);
768  | }
769  | 
770  | #endif /* OVL0 */
771  | #ifdef OVLB
772  | 
773  | /*ARGSUSED*/	/* do_clear_area client */
774  | STATIC_PTR void
775  | wantdoor(x, y, distance)
776  | int x, y;
777  | genericptr_t distance;
778  | {
779  |     int ndist;
780  | 
781  |     if (*(int*)distance > (ndist = distu(x, y))) {
782  | 	gx = x;
783  | 	gy = y;
784  | 	*(int*)distance = ndist;
785  |     }
786  | }
787  | 
788  | #endif /* OVLB */
789  | 
790  | /*dogmove.c*/