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