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*/