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