1    | /*	SCCS Id: @(#)dog.c	3.3	1999/10/20	*/
2    | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3    | /* NetHack may be freely redistributed.  See license for details. */
4    | 
5    | #include "hack.h"
6    | #include "edog.h"
7    | 
8    | #ifdef OVLB
9    | 
10   | STATIC_DCL int NDECL(pet_type);
11   | 
12   | void
13   | initedog(mtmp)
14   | register struct monst *mtmp;
15   | {
16   | 	mtmp->mtame = is_domestic(mtmp->data) ? 10 : 5;
17   | 	mtmp->mpeaceful = 1;
18   | 	set_malign(mtmp); /* recalc alignment now that it's tamed */
19   | 	mtmp->mleashed = 0;
20   | 	mtmp->meating = 0;
21   | 	EDOG(mtmp)->droptime = 0;
22   | 	EDOG(mtmp)->dropdist = 10000;
23   | 	EDOG(mtmp)->apport = 10;
24   | 	EDOG(mtmp)->whistletime = 0;
25   | 	EDOG(mtmp)->hungrytime = 1000 + monstermoves;
26   | 	EDOG(mtmp)->ogoal.x = -1;	/* force error if used before set */
27   | 	EDOG(mtmp)->ogoal.y = -1;
28   | 	EDOG(mtmp)->abuse = 0;
29   | 	EDOG(mtmp)->revivals = 0;
30   | 	EDOG(mtmp)->mhpmax_penalty = 0;
31   | 	EDOG(mtmp)->killed_by_u = 0;
32   | }
33   | 
34   | STATIC_OVL int
35   | pet_type()
36   | {
37   | 	if (urole.petnum != NON_PM)
38   | 	    return (urole.petnum);
39   | 	else if (preferred_pet == 'c')
40   | 	    return (PM_KITTEN);
41   | 	else if (preferred_pet == 'd')
42   | 	    return (PM_LITTLE_DOG);
43   | 	else
44   | 	    return (rn2(2) ? PM_KITTEN : PM_LITTLE_DOG);
45   | }
46   | 
47   | struct monst *
48   | make_familiar(otmp,x,y,quietly)
49   | register struct obj *otmp;
50   | xchar x, y;
51   | boolean quietly;
52   | {
53   | 	struct permonst *pm;
54   | 	struct monst *mtmp = 0;
55   | 	int chance, trycnt = 100;
56   | 
57   | 	do {
58   | 	    if (otmp) {
59   | 		pm = &mons[otmp->corpsenm]; /* Figurine; otherwise spell */
60   | 	    } else if (!rn2(3)) {
61   | 		pm = &mons[pet_type()];
62   | 	    } else {
63   | 		pm = rndmonst();
64   | 		if (!pm) {
65   | 		  if (!quietly)
66   | 		    There("seems to be nothing available for a familiar.");
67   | 		  break;
68   | 		}
69   | 	    }
70   | 
71   | 	    mtmp = makemon(pm, x, y, MM_EDOG);
72   | 	    if (otmp && !mtmp) { /* monster was genocided or square occupied */
73   | 	 	if (!quietly)
74   | 		   pline_The("figurine writhes and then shatters into pieces!");
75   | 		break;
76   | 	    }
77   | 	} while (!mtmp && --trycnt > 0);
78   | 
79   | 	if (!mtmp) return (struct monst *)0;
80   | 
81   | 	initedog(mtmp);
82   | 	mtmp->msleeping = 0;
83   | 	if (otmp) { /* figurine; resulting monster might not become a pet */
84   | 	    chance = rn2(10);	/* 0==tame, 1==peaceful, 2==hostile */
85   | 	    if (chance > 2) chance = otmp->blessed ? 0 : !otmp->cursed ? 1 : 2;
86   | 	    /* 0,1,2:  b=80%,10,10; nc=10%,80,10; c=10%,10,80 */
87   | 	    if (chance > 0) {
88   | 		mtmp->mtame = 0;	/* not tame after all */
89   | 		if (chance == 2) { /* hostile (cursed figurine) */
90   | 		    if (!quietly)
91   | 		       You("get a bad feeling about this.");
92   | 		    mtmp->mpeaceful = 0;
93   | 		}
94   | 	    }
95   | 	    /* if figurine has been named, give same name to the monster */
96   | 	    if (otmp->onamelth)
97   | 		mtmp = christen_monst(mtmp, ONAME(otmp));
98   | 	}
99   | 	set_malign(mtmp); /* more alignment changes */
100  | 	newsym(mtmp->mx, mtmp->my);
101  | 
102  | 	/* must wield weapon immediately since pets will otherwise drop it */
103  | 	if (mtmp->mtame && attacktype(mtmp->data, AT_WEAP)) {
104  | 		mtmp->weapon_check = NEED_HTH_WEAPON;
105  | 		(void) mon_wield_item(mtmp);
106  | 	}
107  | 	return mtmp;
108  | }
109  | 
110  | struct monst *
111  | makedog()
112  | {
113  | 	register struct monst *mtmp;
114  | #ifdef STEED
115  | 	register struct obj *otmp;
116  | #endif
117  | 	const char *petname;
118  | 	int   pettype;
119  | 	static int petname_used = 0;
120  | 
121  | 	pettype = pet_type();
122  | 	if (pettype == PM_LITTLE_DOG)
123  | 		petname = dogname;
124  | 	else if (pettype == PM_PONY)
125  | 		petname = horsename;
126  | 	else
127  | 		petname = catname;
128  | 
129  | 	/* default pet names */
130  | 	if (!*petname && pettype == PM_LITTLE_DOG) {
131  | 	    /* All of these names were for dogs. */
132  | 	    if(Role_if(PM_CAVEMAN)) petname = "Slasher";   /* The Warrior */
133  | 	    if(Role_if(PM_SAMURAI)) petname = "Hachi";     /* Shibuya Station */
134  | 	    if(Role_if(PM_BARBARIAN)) petname = "Idefix";  /* Obelix */
135  | 	    if(Role_if(PM_RANGER)) petname = "Sirius";     /* Orion's dog */
136  | 	}
137  | 
138  | 	mtmp = makemon(&mons[pettype], u.ux, u.uy, MM_EDOG);
139  | 
140  | 	if(!mtmp) return((struct monst *) 0); /* pets were genocided */
141  | 
142  | #ifdef STEED
143  | 	/* Horses already wear a saddle */
144  | 	if (pettype == PM_PONY && !!(otmp = mksobj(SADDLE, TRUE, FALSE))) {
145  | 	    if (mpickobj(mtmp, otmp))
146  | 		panic("merged saddle?");
147  | 	    mtmp->misc_worn_check |= W_SADDLE;
148  | 	    otmp->dknown = otmp->bknown = otmp->rknown = 1;
149  | 	    otmp->owornmask = W_SADDLE;
150  | 	    otmp->leashmon = mtmp->m_id;
151  | 	    update_mon_intrinsics(mtmp, otmp, TRUE);
152  | 	}
153  | #endif
154  | 
155  | 	if (!petname_used++ && *petname)
156  | 		mtmp = christen_monst(mtmp, petname);
157  | 
158  | 	initedog(mtmp);
159  | 	return(mtmp);
160  | }
161  | 
162  | /* record `last move time' for all monsters prior to level save so that
163  |    mon_arrive() can catch up for lost time when they're restored later */
164  | void
165  | update_mlstmv()
166  | {
167  | 	struct monst *mon;
168  | 
169  | 	/* monst->mlstmv used to be updated every time `monst' actually moved,
170  | 	   but that is no longer the case so we just do a blanket assignment */
171  | 	for (mon = fmon; mon; mon = mon->nmon)
172  | 	    if (!DEADMONSTER(mon)) mon->mlstmv = monstermoves;
173  | }
174  | 
175  | void
176  | losedogs()
177  | {
178  | 	register struct monst *mtmp, *mtmp0 = 0, *mtmp2;
179  | 
180  | 	while ((mtmp = mydogs) != 0) {
181  | 		mydogs = mtmp->nmon;
182  | 		mon_arrive(mtmp, TRUE);
183  | 	}
184  | 
185  | 	for(mtmp = migrating_mons; mtmp; mtmp = mtmp2) {
186  | 		mtmp2 = mtmp->nmon;
187  | 		if (mtmp->mux == u.uz.dnum && mtmp->muy == u.uz.dlevel) {
188  | 		    if(mtmp == migrating_mons)
189  | 			migrating_mons = mtmp->nmon;
190  | 		    else
191  | 			mtmp0->nmon = mtmp->nmon;
192  | 		    mon_arrive(mtmp, FALSE);
193  | 		} else
194  | 		    mtmp0 = mtmp;
195  | 	}
196  | }
197  | 
198  | /* called from resurrect() in addition to losedogs() */
199  | void
200  | mon_arrive(mtmp, with_you)
201  | struct monst *mtmp;
202  | boolean with_you;
203  | {
204  | 	struct trap *t;
205  | 	xchar xlocale, ylocale, xyloc, xyflags, wander;
206  | 	int num_segs;
207  | 
208  | 	mtmp->nmon = fmon;
209  | 	fmon = mtmp;
210  | 	if (mtmp->isshk)
211  | 	    set_residency(mtmp, FALSE);
212  | 
213  | 	num_segs = mtmp->wormno;
214  | 	/* baby long worms have no tail so don't use is_longworm() */
215  | 	if ((mtmp->data == &mons[PM_LONG_WORM]) &&
216  | #ifdef DCC30_BUG
217  | 	    (mtmp->wormno = get_wormno(), mtmp->wormno != 0))
218  | #else
219  | 	    (mtmp->wormno = get_wormno()) != 0)
220  | #endif
221  | 	{
222  | 	    initworm(mtmp, num_segs);
223  | 	    /* tail segs are not yet initialized or displayed */
224  | 	} else mtmp->wormno = 0;
225  | 
226  | 	/* some monsters might need to do something special upon arrival
227  | 	   _after_ the current level has been fully set up; see dochug() */
228  | 	mtmp->mstrategy |= STRAT_ARRIVE;
229  | 
230  | 	/* make sure mnexto(rloc_to(set_apparxy())) doesn't use stale data */
231  | 	mtmp->mux = u.ux,  mtmp->muy = u.uy;
232  | 	xyloc	= mtmp->mtrack[0].x;
233  | 	xyflags = mtmp->mtrack[0].y;
234  | 	xlocale = mtmp->mtrack[1].x;
235  | 	ylocale = mtmp->mtrack[1].y;
236  | 	mtmp->mtrack[0].x = mtmp->mtrack[0].y = 0;
237  | 	mtmp->mtrack[1].x = mtmp->mtrack[1].y = 0;
238  | 
239  | #ifdef STEED
240  | 	if (mtmp == u.usteed)
241  | 	    return;	/* don't place steed on the map */
242  | #endif
243  | 	if (with_you) {
244  | 	    /* When a monster accompanies you, sometimes it will arrive
245  | 	       at your intended destination and you'll end up next to
246  | 	       that spot.  This code doesn't control the final outcome;
247  | 	       goto_level(do.c) decides who ends up at your target spot
248  | 	       when there is a monster there too. */
249  | 	    if (!MON_AT(u.ux, u.uy) &&
250  | 		    !rn2(mtmp->mtame ? 10 : mtmp->mpeaceful ? 5 : 2))
251  | 		rloc_to(mtmp, u.ux, u.uy);
252  | 	    else
253  | 		mnexto(mtmp);
254  | 	    return;
255  | 	}
256  | 	/*
257  | 	 * The monster arrived on this level independently of the player.
258  | 	 * Its coordinate fields were overloaded for use as flags that
259  | 	 * specify its final destination.
260  | 	 */
261  | 
262  | 	if (mtmp->mlstmv < monstermoves - 1L) {
263  | 	    /* heal monster for time spent in limbo */
264  | 	    long nmv = monstermoves - 1L - mtmp->mlstmv;
265  | 
266  | 	    mon_catchup_elapsed_time(mtmp, nmv);
267  | 	    mtmp->mlstmv = monstermoves - 1L;
268  | 
269  | 	    /* let monster move a bit on new level (see placement code below) */
270  | 	    wander = (xchar) min(nmv, 8);
271  | 	} else
272  | 	    wander = 0;
273  | 
274  | 	switch (xyloc) {
275  | 	 case MIGR_APPROX_XY:	/* {x,y}locale set above */
276  | 		break;
277  | 	 case MIGR_EXACT_XY:	wander = 0;
278  | 		break;
279  | 	 case MIGR_NEAR_PLAYER:	xlocale = u.ux,  ylocale = u.uy;
280  | 		break;
281  | 	 case MIGR_STAIRS_UP:	xlocale = xupstair,  ylocale = yupstair;
282  | 		break;
283  | 	 case MIGR_STAIRS_DOWN:	xlocale = xdnstair,  ylocale = ydnstair;
284  | 		break;
285  | 	 case MIGR_LADDER_UP:	xlocale = xupladder,  ylocale = yupladder;
286  | 		break;
287  | 	 case MIGR_LADDER_DOWN:	xlocale = xdnladder,  ylocale = ydnladder;
288  | 		break;
289  | 	 case MIGR_SSTAIRS:	xlocale = sstairs.sx,  ylocale = sstairs.sy;
290  | 		break;
291  | 	 case MIGR_PORTAL:
292  | 		if (In_endgame(&u.uz)) {
293  | 		    /* there is no arrival portal for endgame levels */
294  | 		    /* BUG[?]: for simplicity, this code relies on the fact
295  | 		       that we know that the current endgame levels always
296  | 		       build upwards and never have any exclusion subregion
297  | 		       inside their TELEPORT_REGION settings. */
298  | 		    xlocale = rn1(updest.hx - updest.lx + 1, updest.lx);
299  | 		    ylocale = rn1(updest.hy - updest.ly + 1, updest.ly);
300  | 		    break;
301  | 		}
302  | 		/* find the arrival portal */
303  | 		for (t = ftrap; t; t = t->ntrap)
304  | 		    if (t->ttyp == MAGIC_PORTAL) break;
305  | 		if (t) {
306  | 		    xlocale = t->tx,  ylocale = t->ty;
307  | 		    break;
308  | 		} else {
309  | 		    impossible("mon_arrive: no corresponding portal?");
310  | 		} /*FALLTHRU*/
311  | 	 default:
312  | 	 case MIGR_RANDOM:	xlocale = ylocale = 0;
313  | 		    break;
314  | 	    }
315  | 
316  | 	if (xlocale && wander) {
317  | 	    /* monster moved a bit; pick a nearby location */
318  | 	    /* mnearto() deals w/stone, et al */
319  | 	    char *r = in_rooms(xlocale, ylocale, 0);
320  | 	    if (r && *r) {
321  | 		coord c;
322  | 		/* somexy() handles irregular rooms */
323  | 		if (somexy(&rooms[*r - ROOMOFFSET], &c))
324  | 		    xlocale = c.x,  ylocale = c.y;
325  | 		else
326  | 		    xlocale = ylocale = 0;
327  | 	    } else {		/* not in a room */
328  | 		int i, j;
329  | 		i = max(1, xlocale - wander);
330  | 		j = min(COLNO-1, xlocale + wander);
331  | 		xlocale = rn1(j - i, i);
332  | 		i = max(0, ylocale - wander);
333  | 		j = min(ROWNO-1, ylocale + wander);
334  | 		ylocale = rn1(j - i, i);
335  | 	    }
336  | 	}	/* moved a bit */
337  | 
338  | 	mtmp->mx = 0;	/*(already is 0)*/
339  | 	mtmp->my = xyflags;
340  | 	if (xlocale)
341  | 	    (void) mnearto(mtmp, xlocale, ylocale, FALSE);
342  | 	else
343  | 	    rloc(mtmp);
344  | }
345  | 
346  | /* heal monster for time spent elsewhere */
347  | void
348  | mon_catchup_elapsed_time(mtmp, nmv)
349  | struct monst *mtmp;
350  | long nmv;		/* number of moves */
351  | {
352  | 	int imv = 0;	/* avoid zillions of casts and lint warnings */
353  | 
354  | #if defined(DEBUG) || defined(BETA)
355  | 	if (nmv < 0L) {			/* crash likely... */
356  | 	    panic("catchup from future time?");
357  | 	    /*NOTREACHED*/
358  | 	    return;
359  | 	} else if (nmv == 0L) {		/* safe, but should'nt happen */
360  | 	    impossible("catchup from now?");
361  | 	} else
362  | #endif
363  | 	if (nmv >= LARGEST_INT)		/* paranoia */
364  | 	    imv = LARGEST_INT - 1;
365  | 	else
366  | 	    imv = (int)nmv;
367  | 
368  | 	/* might stop being afraid, blind or frozen */
369  | 	/* set to 1 and allow final decrement in movemon() */
370  | 	if (mtmp->mblinded) {
371  | 	    if (imv >= (int) mtmp->mblinded) mtmp->mblinded = 1;
372  | 	    else mtmp->mblinded -= imv;
373  | 	}
374  | 	if (mtmp->mfrozen) {
375  | 	    if (imv >= (int) mtmp->mfrozen) mtmp->mfrozen = 1;
376  | 	    else mtmp->mfrozen -= imv;
377  | 	}
378  | 	if (mtmp->mfleetim) {
379  | 	    if (imv >= (int) mtmp->mfleetim) mtmp->mfleetim = 1;
380  | 	    else mtmp->mfleetim -= imv;
381  | 	}
382  | 
383  | 	/* might recover from temporary trouble */
384  | 	if (mtmp->mtrapped && rn2(imv + 1) > 40/2) mtmp->mtrapped = 0;
385  | 	if (mtmp->mconf    && rn2(imv + 1) > 50/2) mtmp->mconf = 0;
386  | 	if (mtmp->mstun    && rn2(imv + 1) > 10/2) mtmp->mstun = 0;
387  | 
388  | 	/* might finish eating or be able to use special ability again */
389  | 	if (imv > mtmp->meating) mtmp->meating = 0;
390  | 	else mtmp->meating -= imv;
391  | 	if (imv > mtmp->mspec_used) mtmp->mspec_used = 0;
392  | 	else mtmp->mspec_used -= imv;
393  | 
394  | 	/* reduce tameness for every 150 moves you are separated */
395  | 	if (mtmp->mtame) {
396  | 	    int wilder = (imv + 75) / 150;
397  | 	    if (mtmp->mtame > wilder) mtmp->mtame -= wilder;	/* less tame */
398  | 	    else if (mtmp->mtame > rn2(wilder)) mtmp->mtame = 0;  /* untame */
399  | 	    else mtmp->mtame = mtmp->mpeaceful = 0;		/* hostile! */
400  | 	}
401  | 	/* check to see if it would have died as a pet; if so, go wild instead
402  | 	 * of dying the next time we call dog_move()
403  | 	 */
404  | 	if (mtmp->mtame && !mtmp->isminion &&
405  | 			(carnivorous(mtmp->data) || herbivorous(mtmp->data))) {
406  | 	    struct edog *edog = EDOG(mtmp);
407  | 
408  | 	    if ((monstermoves > edog->hungrytime + 500 && mtmp->mhp < 3) ||
409  | 		    (monstermoves > edog->hungrytime + 750))
410  | 		mtmp->mtame = mtmp->mpeaceful = 0;
411  | 	}
412  | 
413  | 	/* recover lost hit points */
414  | 	if (!regenerates(mtmp->data)) imv /= 20;
415  | 	if (mtmp->mhp + imv >= mtmp->mhpmax)
416  | 	    mtmp->mhp = mtmp->mhpmax;
417  | 	else mtmp->mhp += imv;
418  | }
419  | 
420  | #endif /* OVLB */
421  | #ifdef OVL2
422  | 
423  | /* called when you move to another level */
424  | void
425  | keepdogs(pets_only)
426  | boolean pets_only;	/* true for ascension or final escape */
427  | {
428  | 	register struct monst *mtmp, *mtmp2;
429  | 	register struct obj *obj;
430  | 	int num_segs;
431  | 	boolean stay_behind;
432  | 
433  | 	for (mtmp = fmon; mtmp; mtmp = mtmp2) {
434  | 	    mtmp2 = mtmp->nmon;
435  | 	    if (DEADMONSTER(mtmp)) continue;
436  | 	    if (pets_only && !mtmp->mtame) continue;
437  | 	    if (((monnear(mtmp, u.ux, u.uy) && levl_follower(mtmp)) ||
438  | #ifdef STEED
439  | 			(mtmp == u.usteed) ||
440  | #endif
441  | 		/* the wiz will level t-port from anywhere to chase
442  | 		   the amulet; if you don't have it, will chase you
443  | 		   only if in range. -3. */
444  | 			(u.uhave.amulet && mtmp->iswiz))
445  | 			&& !mtmp->msleeping && mtmp->mcanmove) {
446  | 		stay_behind = FALSE;
447  | 		if (mtmp->mtame && mtmp->meating) {
448  | 			if (canseemon(mtmp))
449  | 			    pline("%s is still eating.", Monnam(mtmp));
450  | 			stay_behind = TRUE;
451  | 		} else if (mon_has_amulet(mtmp)) {
452  | 			if (canseemon(mtmp))
453  | 			    pline("%s seems very disoriented for a moment.",
454  | 				Monnam(mtmp));
455  | 			stay_behind = TRUE;
456  | 		}
457  | 		if (stay_behind
458  | #ifdef STEED
459  | 				&& mtmp != u.usteed
460  | #endif
461  | 				) {
462  | 			if (mtmp->mleashed) {
463  | 				pline("%s leash suddenly comes loose.",
464  | 					humanoid(mtmp->data)
465  | 					    ? (mtmp->female ? "Her" : "His")
466  | 					    : "Its");
467  | 				m_unleash(mtmp);
468  | 			}
469  | 			continue;
470  | 		}
471  | 		if (mtmp->isshk)
472  | 			set_residency(mtmp, TRUE);
473  | 
474  | 		if (mtmp->wormno) {
475  | 		    register int cnt;
476  | 		    /* NOTE: worm is truncated to # segs = max wormno size */
477  | 		    cnt = count_wsegs(mtmp);
478  | 		    num_segs = min(cnt, MAX_NUM_WORMS - 1);
479  | 		    wormgone(mtmp);
480  | 		} else num_segs = 0;
481  | 
482  | 		/* set minvent's obj->no_charge to 0 */
483  | 		for(obj = mtmp->minvent; obj; obj = obj->nobj) {
484  | 		    if (Has_contents(obj))
485  | 			picked_container(obj);	/* does the right thing */
486  | 		    obj->no_charge = 0;
487  | 		}
488  | 
489  | 		relmon(mtmp);
490  | 		newsym(mtmp->mx,mtmp->my);
491  | 		mtmp->mx = mtmp->my = 0; /* avoid mnexto()/MON_AT() problem */
492  | 		mtmp->wormno = num_segs;
493  | 		mtmp->mlstmv = monstermoves;
494  | 		mtmp->nmon = mydogs;
495  | 		mydogs = mtmp;
496  | 	    } else if (mtmp->iswiz) {
497  | 		/* we want to be able to find him when his next resurrection
498  | 		   chance comes up, but have him resume his present location
499  | 		   if player returns to this level before that time */
500  | 		migrate_to_level(mtmp, ledger_no(&u.uz),
501  | 				 MIGR_EXACT_XY, (coord *)0);
502  | 	    } else if (mtmp->mleashed) {
503  | 		/* this can happen if your quest leader ejects you from the
504  | 		   "home" level while a leashed pet isn't next to you */
505  | 		pline("%s leash goes slack.", s_suffix(Monnam(mtmp)));
506  | 		m_unleash(mtmp);
507  | 	    }
508  | 	}
509  | }
510  | 
511  | #endif /* OVL2 */
512  | #ifdef OVLB
513  | 
514  | void
515  | migrate_to_level(mtmp, tolev, xyloc, cc)
516  | 	register struct monst *mtmp;
517  | 	xchar tolev;	/* destination level */
518  | 	xchar xyloc;	/* MIGR_xxx destination xy location: */
519  | 	coord *cc;	/* optional destination coordinates */
520  | {
521  | 	register struct obj *obj;
522  | 	d_level new_lev;
523  | 	xchar xyflags;
524  | 	int num_segs = 0;	/* count of worm segments */
525  | 
526  | 	if (mtmp->isshk)
527  | 	    set_residency(mtmp, TRUE);
528  | 
529  | 	if (mtmp->wormno) {
530  | 	    register int cnt;
531  | 	  /* **** NOTE: worm is truncated to # segs = max wormno size **** */
532  | 	    cnt = count_wsegs(mtmp);
533  | 	    num_segs = min(cnt, MAX_NUM_WORMS - 1);
534  | 	    wormgone(mtmp);
535  | 	}
536  | 
537  | 	/* set minvent's obj->no_charge to 0 */
538  | 	for(obj = mtmp->minvent; obj; obj = obj->nobj) {
539  | 	    if (Has_contents(obj))
540  | 		picked_container(obj);	/* does the right thing */
541  | 	    obj->no_charge = 0;
542  | 	}
543  | 
544  | 	relmon(mtmp);
545  | 	mtmp->nmon = migrating_mons;
546  | 	migrating_mons = mtmp;
547  | 	if (mtmp->mleashed)  {
548  | 		m_unleash(mtmp);
549  | 		mtmp->mtame--;
550  | 		pline_The("leash comes off!");
551  | 	}
552  | 	newsym(mtmp->mx,mtmp->my);
553  | 
554  | 	new_lev.dnum = ledger_to_dnum((xchar)tolev);
555  | 	new_lev.dlevel = ledger_to_dlev((xchar)tolev);
556  | 	/* overload mtmp->[mx,my], mtmp->[mux,muy], and mtmp->mtrack[] as */
557  | 	/* destination codes (setup flag bits before altering mx or my) */
558  | 	xyflags = (depth(&new_lev) < depth(&u.uz));	/* 1 => up */
559  | 	if (In_W_tower(mtmp->mx, mtmp->my, &u.uz)) xyflags |= 2;
560  | 	mtmp->wormno = num_segs;
561  | 	mtmp->mlstmv = monstermoves;
562  | 	mtmp->mtrack[1].x = cc ? cc->x : mtmp->mx;
563  | 	mtmp->mtrack[1].y = cc ? cc->y : mtmp->my;
564  | 	mtmp->mtrack[0].x = xyloc;
565  | 	mtmp->mtrack[0].y = xyflags;
566  | 	mtmp->mux = new_lev.dnum;
567  | 	mtmp->muy = new_lev.dlevel;
568  | 	mtmp->mx = mtmp->my = 0;	/* this implies migration */
569  | }
570  | 
571  | #endif /* OVLB */
572  | #ifdef OVL1
573  | 
574  | /* return quality of food; the lower the better */
575  | /* fungi will eat even tainted food */
576  | int
577  | dogfood(mon,obj)
578  | struct monst *mon;
579  | register struct obj *obj;
580  | {
581  | 	boolean carni = carnivorous(mon->data);
582  | 	boolean herbi = herbivorous(mon->data);
583  | 	struct permonst *fptr = &mons[obj->corpsenm];
584  | 
585  | 	if (is_quest_artifact(obj) || obj_resists(obj, 0, 95))
586  | 	    return (obj->cursed ? TABU : APPORT);
587  | 
588  | 	switch(obj->oclass) {
589  | 	case FOOD_CLASS:
590  | 	    if (obj->otyp == CORPSE &&
591  | 		((touch_petrifies(&mons[obj->corpsenm]) && !resists_ston(mon))
592  | 		 || is_rider(fptr)))
593  | 		    return TABU;
594  | 
595  | 	    /* Ghouls only eat old corpses... yum! */
596  | 	    if (mon->data == &mons[PM_GHOUL])
597  | 	    	return (obj->otyp == CORPSE && obj->age+50 <= monstermoves) ?
598  | 	       			DOGFOOD : TABU;
599  | 
600  | 	    if (!carni && !herbi)
601  | 		    return (obj->cursed ? UNDEF : APPORT);
602  | 
603  | 	    switch (obj->otyp) {
604  | 		case TRIPE_RATION:
605  | 		case MEATBALL:
606  | 		case MEAT_RING:
607  | 		case MEAT_STICK:
608  | 		case HUGE_CHUNK_OF_MEAT:
609  | 		    return (carni ? DOGFOOD : MANFOOD);
610  | 		case EGG:
611  | 		    if (touch_petrifies(&mons[obj->corpsenm]) && !resists_ston(mon))
612  | 			return POISON;
613  | 		    return (carni ? CADAVER : MANFOOD);
614  | 		case CORPSE:
615  | 		   if ((peek_at_iced_corpse_age(obj)+50 <= monstermoves
616  | 					    && obj->corpsenm != PM_LIZARD
617  | 					    && obj->corpsenm != PM_LICHEN
618  | 					    && mon->data->mlet != S_FUNGUS) ||
619  | 			(acidic(&mons[obj->corpsenm]) && !resists_acid(mon)) ||
620  | 			(poisonous(&mons[obj->corpsenm]) &&
621  | 						!resists_poison(mon)))
622  | 			return POISON;
623  | 		    else if (vegan(fptr))
624  | 			return (herbi ? CADAVER : MANFOOD);
625  | 		    else return (carni ? CADAVER : MANFOOD);
626  | 		case CLOVE_OF_GARLIC:
627  | 		    return (is_undead(mon->data) ? TABU :
628  | 			    (herbi ? ACCFOOD : MANFOOD));
629  | 		case TIN:
630  | 		    return (metallivorous(mon->data) ? ACCFOOD : MANFOOD);
631  | 		case APPLE:
632  | 		case CARROT:
633  | 		    return (herbi ? DOGFOOD : MANFOOD);
634  | 		case BANANA:
635  | 		    return ((mon->data->mlet == S_YETI) ? DOGFOOD :
636  | 			    (herbi ? ACCFOOD : MANFOOD));
637  | 		default:
638  | 		    return (obj->otyp > SLIME_MOLD ?
639  | 			    (carni ? ACCFOOD : MANFOOD) :
640  | 			    (herbi ? ACCFOOD : MANFOOD));
641  | 	    }
642  | 	default:
643  | 	    if (obj->otyp == AMULET_OF_STRANGULATION ||
644  | 	    		obj->otyp == RIN_SLOW_DIGESTION)
645  | 	    	return (TABU);
646  | 	    if (hates_silver(mon->data) &&
647  | 		objects[obj->otyp].oc_material == SILVER)
648  | 		return(TABU);
649  | 	    if (mon->data == &mons[PM_GELATINOUS_CUBE] && is_organic(obj))
650  | 		return(ACCFOOD);
651  | 	    if (metallivorous(mon->data) && is_metallic(obj))
652  | 		/* Non-rustproofed ferrous based metals are preferred. */
653  | 		return(objects[obj->otyp].oc_material == IRON &&
654  | 		       !obj->oerodeproof ? DOGFOOD : ACCFOOD);
655  | 	    if(!obj->cursed && obj->oclass != BALL_CLASS &&
656  | 						obj->oclass != CHAIN_CLASS)
657  | 		return(APPORT);
658  | 	    /* fall into next case */
659  | 	case ROCK_CLASS:
660  | 	    return(UNDEF);
661  | 	}
662  | }
663  | 
664  | #endif /* OVL1 */
665  | #ifdef OVLB
666  | 
667  | struct monst *
668  | tamedog(mtmp, obj)
669  | register struct monst *mtmp;
670  | register struct obj *obj;
671  | {
672  | 	register struct monst *mtmp2;
673  | 
674  | 	/* The Wiz, Medusa and the quest nemeses aren't even made peaceful. */
675  | 	if (mtmp->iswiz || mtmp->data == &mons[PM_MEDUSA]
676  | 				|| (mtmp->data->mflags3 & M3_WANTSARTI))
677  | 		return((struct monst *)0);
678  | 
679  | 	/* worst case, at least it'll be peaceful. */
680  | 	mtmp->mpeaceful = 1;
681  | 	set_malign(mtmp);
682  | 	if(flags.moonphase == FULL_MOON && night() && rn2(6) && obj
683  | 						&& mtmp->data->mlet == S_DOG)
684  | 		return((struct monst *)0);
685  | 
686  | 	/* If we cannot tame it, at least it's no longer afraid. */
687  | 	mtmp->mflee = 0;
688  | 	mtmp->mfleetim = 0;
689  | 
690  | 	/* make grabber let go now, whether it becomes tame or not */
691  | 	if (mtmp == u.ustuck) {
692  | 	    if (u.uswallow)
693  | 		expels(mtmp, mtmp->data, TRUE);
694  | 	    else if (!(Upolyd && sticks(youmonst.data)))
695  | 		unstuck(mtmp);
696  | 	}
697  | 
698  | 	/* feeding it treats makes it tamer */
699  | 	if (mtmp->mtame && obj) {
700  | 	    int tasty;
701  | 
702  | 	    if (mtmp->mcanmove && !mtmp->mconf && !mtmp->meating &&
703  | 		((tasty = dogfood(mtmp, obj)) == DOGFOOD ||
704  | 		 (tasty <= ACCFOOD && EDOG(mtmp)->hungrytime <= monstermoves))) {
705  | 		/* pet will "catch" and eat this thrown food */
706  | 		if (canseemon(mtmp)) {
707  | 		    boolean big_corpse = (obj->otyp == CORPSE &&
708  | 					  obj->corpsenm >= LOW_PM &&
709  | 				mons[obj->corpsenm].msize > mtmp->data->msize);
710  | 		    pline("%s catches %s%s",
711  | 			  Monnam(mtmp), the(xname(obj)),
712  | 			  !big_corpse ? "." : ", or vice versa!");
713  | 		} else if (cansee(mtmp->mx,mtmp->my))
714  | 		    pline("%s stops.", The(xname(obj)));
715  | 		/* dog_eat expects a floor object */
716  | 		place_object(obj, mtmp->mx, mtmp->my);
717  | 		(void) dog_eat(mtmp, obj, mtmp->mx, mtmp->my, FALSE);
718  | 		/* eating might have killed it, but that doesn't matter here;
719  | 		   a non-null result suppresses "miss" message for thrown
720  | 		   food and also implies that the object has been deleted */
721  | 		return mtmp;
722  | 	    } else
723  | 		return (struct monst *)0;
724  | 	}
725  | 
726  | 	if (mtmp->mtame || !mtmp->mcanmove ||
727  | 	    /* monsters with conflicting structures cannot be tamed */
728  | 	    mtmp->isshk || mtmp->isgd || mtmp->ispriest || mtmp->isminion ||
729  | 	    is_covetous(mtmp->data) || is_human(mtmp->data) ||
730  | 	    (is_demon(mtmp->data) && !is_demon(youmonst.data)) ||
731  | 	    (obj && dogfood(mtmp, obj) >= MANFOOD)) return (struct monst *)0;
732  | 
733  | 	/* make a new monster which has the pet extension */
734  | 	mtmp2 = newmonst(sizeof(struct edog) + mtmp->mnamelth);
735  | 	*mtmp2 = *mtmp;
736  | 	mtmp2->mxlth = sizeof(struct edog);
737  | 	if (mtmp->mnamelth) Strcpy(NAME(mtmp2), NAME(mtmp));
738  | 	initedog(mtmp2);
739  | 	replmon(mtmp, mtmp2);
740  | 	/* `mtmp' is now obsolete */
741  | 
742  | 	if (obj) {		/* thrown food */
743  | 	    /* defer eating until the edog extension has been set up */
744  | 	    place_object(obj, mtmp2->mx, mtmp2->my);	/* put on floor */
745  | 	    /* devour the food (might grow into larger, genocided monster) */
746  | 	    if (dog_eat(mtmp2, obj, mtmp2->mx, mtmp2->my, TRUE) == 2)
747  | 		return mtmp2;		/* oops, it died... */
748  | 	    /* `obj' is now obsolete */
749  | 	}
750  | 
751  | 	newsym(mtmp2->mx, mtmp2->my);
752  | 	if (attacktype(mtmp2->data, AT_WEAP)) {
753  | 		mtmp2->weapon_check = NEED_HTH_WEAPON;
754  | 		(void) mon_wield_item(mtmp2);
755  | 	}
756  | 	return(mtmp2);
757  | }
758  | 
759  | /*
760  |  * Called during pet revival or pet life-saving.
761  |  * If you killed the pet, it revives wild.
762  |  * If you abused the pet a lot while alive, it revives wild.
763  |  * If you abused the pet at all while alive, it revives untame.
764  |  * If the pet wasn't abused and was very tame, it might revive tame.
765  |  */
766  | void
767  | wary_dog(mtmp, quietly)
768  | struct monst *mtmp;
769  | boolean quietly;
770  | {
771  |     int has_edog;
772  | 
773  |     if (!mtmp->mtame) return;
774  |     has_edog = !mtmp->isminion;
775  |     if (has_edog &&
776  | 		((EDOG(mtmp)->killed_by_u == 1) || (EDOG(mtmp)->abuse > 2))) {
777  | 	mtmp->mpeaceful = mtmp->mtame = 0;
778  | 	if (EDOG(mtmp)->abuse >= 0 && EDOG(mtmp)->abuse < 10)
779  | 		if (!rn2(EDOG(mtmp)->abuse + 1)) mtmp->mpeaceful = 1;
780  | 	if(!quietly && cansee(mtmp->mx, mtmp->my)) {
781  | 	    if (haseyes(youmonst.data)) {
782  | 		if (haseyes(mtmp->data))
783  | 			pline("%s %s to look you in the %s.",
784  | 				Monnam(mtmp),
785  | 				mtmp->mpeaceful ? "seems unable" :
786  | 					    "refuses",
787  | 				body_part(EYE));
788  | 		else 
789  | 			pline("%s avoids your gaze.",
790  | 				Monnam(mtmp));
791  | 	    }
792  | 	}
793  |     } else {
794  |     	/* chance it goes wild anyway - Pet Semetary */
795  |     	if (!rn2(mtmp->mtame)) {
796  |     		mtmp->mpeaceful = mtmp->mtame = 0;
797  |     	}
798  |     }
799  |     /* if its still a pet, start a clean pet-slate now */
800  |     if (has_edog && mtmp->mtame) {
801  | 	EDOG(mtmp)->revivals++;
802  |     	EDOG(mtmp)->killed_by_u = 0;
803  |     	EDOG(mtmp)->abuse = 0;
804  |     }
805  | }
806  | 
807  | void
808  | abuse_dog(mtmp)
809  | struct monst *mtmp;
810  | {
811  | 	if (!mtmp->mtame) return;
812  | 
813  | 	if (Aggravate_monster || Conflict) mtmp->mtame /=2;
814  | 	else mtmp->mtame--;
815  | 
816  | 	if (mtmp->mtame && !mtmp->isminion)
817  | 		EDOG(mtmp)->abuse++;
818  | 
819  | 	if (mtmp->mtame && rn2(mtmp->mtame)) yelp(mtmp);
820  | 	else growl(mtmp);	/* give them a moment's worry */
821  | 	
822  | 	if (!mtmp->mtame) newsym(mtmp->mx, mtmp->my);
823  | }
824  | 
825  | #endif /* OVLB */
826  | 
827  | /*dog.c*/