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
10extern boolean notonhead;
11
12#ifdef OVL0
13
14STATIC_DCL boolean FDECL(dog_hunger,(struct monst *,struct edog *));
15STATIC_DCL int FDECL(dog_invent,(struct monst *,struct edog *,int));
16STATIC_DCL int FDECL(dog_goal,(struct monst *,struct edog *,int,int,int));
17
18STATIC_DCL struct obj *FDECL(DROPPABLES, (struct monst *));
19STATIC_DCL boolean FDECL(can_reach_location,(struct monst *,XCHAR_P,XCHAR_P,
20    XCHAR_P,XCHAR_P));
21STATIC_DCL boolean FDECL(could_reach_item,(struct monst *, XCHAR_P,XCHAR_P));
22
23STATIC_OVL struct obj *
24DROPPABLES(mon)
25register 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
50static NEARDATA const char nofetch[] = { BALL_CLASS, CHAIN_CLASS, ROCK_CLASS, 0 };
51
52#endif /* OVL0 */
53
54STATIC_OVL boolean FDECL(cursed_object_at, (int, int));
55
56STATIC_VAR xchar gtyp, gx, gy;	/* type and position of dog's current goal */
57
58STATIC_PTR void FDECL(wantdoor, (int, int, genericptr_t));
59
60#ifdef OVLB
61STATIC_OVL boolean
62cursed_object_at(x, y)
63int 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
72int
73dog_nutrition(mtmp, obj)
74struct monst *mtmp;
75struct 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 */
122int
123dog_eat(mtmp, obj, x, y, devour)
124register struct monst *mtmp;
125register struct obj * obj;
126int x, y;
127boolean 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 */
212STATIC_OVL boolean
213dog_hunger(mtmp, edog)
214register struct monst *mtmp;
215register 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 */
260STATIC_OVL int
261dog_invent(mtmp, edog, udist)
262register struct monst *mtmp;
263register struct edog *edog;
264int 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 */
330STATIC_OVL int
331dog_goal(mtmp, edog, after, udist, whappr)
332register struct monst *mtmp;
333struct edog *edog;
334int 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) */
478int
479dog_move(mtmp, after)
480register struct monst *mtmp;
481register 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	}
712newdogpos:
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;
783dognext:
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 */
795STATIC_OVL boolean
796could_reach_item(mon, nx, ny)
797struct monst *mon;
798xchar 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 */
814STATIC_OVL boolean
815can_reach_location(mon, mx, my, fx, fy)
816struct monst *mon;
817xchar 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 */
851STATIC_PTR void
852wantdoor(x, y, distance)
853int x, y;
854genericptr_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*/
868