1/* SCCS Id: @(#)dogmove.c 3.4 2002/09/10 */ 2/* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */ 3/* NetHack may be freely redistributed. See license for details. */ 4 5#include "hack.h" 6 7#include "mfndpos.h" 8#include "edog.h" 9 10extern boolean notonhead; 11 12#ifdef OVL0 13 14STATIC_DCL boolean FDECL(dog_hunger,(struct monst *,struct edog *)); 15STATIC_DCL int FDECL(dog_invent,(struct monst *,struct edog *,int)); 16STATIC_DCL int FDECL(dog_goal,(struct monst *,struct edog *,int,int,int)); 17 18STATIC_DCL struct obj *FDECL(DROPPABLES, (struct monst *)); 19STATIC_DCL boolean FDECL(can_reach_location,(struct monst *,XCHAR_P,XCHAR_P, 20 XCHAR_P,XCHAR_P)); 21STATIC_DCL boolean FDECL(could_reach_item,(struct monst *, XCHAR_P,XCHAR_P)); 22 23STATIC_OVL struct obj * 24DROPPABLES(mon) 25register struct monst *mon; 26{ 27 register struct obj *obj; 28 struct obj *wep = MON_WEP(mon); 29 boolean item1 = FALSE, item2 = FALSE; 30 31 if (is_animal(mon->data) || mindless(mon->data)) 32 item1 = item2 = TRUE; 33 if (!tunnels(mon->data) || !needspick(mon->data)) 34 item1 = TRUE; 35 for(obj = mon->minvent; obj; obj = obj->nobj) { 36 if (!item1 && is_pick(obj) && (obj->otyp != DWARVISH_MATTOCK 37 || !which_armor(mon, W_ARMS))) { 38 item1 = TRUE; 39 continue; 40 } 41 if (!item2 && obj->otyp == UNICORN_HORN && !obj->cursed) { 42 item2 = TRUE; 43 continue; 44 } 45 if (!obj->owornmask && obj != wep) return obj; 46 } 47 return (struct obj *)0; 48} 49 50static NEARDATA const char nofetch[] = { BALL_CLASS, CHAIN_CLASS, ROCK_CLASS, 0 }; 51 52#endif /* OVL0 */ 53 54STATIC_OVL boolean FDECL(cursed_object_at, (int, int)); 55 56STATIC_VAR xchar gtyp, gx, gy; /* type and position of dog's current goal */ 57 58STATIC_PTR void FDECL(wantdoor, (int, int, genericptr_t)); 59 60#ifdef OVLB 61STATIC_OVL boolean 62cursed_object_at(x, y) 63int x, y; 64{ 65 struct obj *otmp; 66 67 for(otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere) 68 if (otmp->cursed) return TRUE; 69 return FALSE; 70} 71 72int 73dog_nutrition(mtmp, obj) 74struct monst *mtmp; 75struct obj *obj; 76{ 77 int nutrit; 78 79 /* 80 * It is arbitrary that the pet takes the same length of time to eat 81 * as a human, but gets more nutritional value. 82 */ 83 if (obj->oclass == FOOD_CLASS) { 84 if(obj->otyp == CORPSE) { 85 mtmp->meating = 3 + (mons[obj->corpsenm].cwt >> 6); 86 nutrit = mons[obj->corpsenm].cnutrit; 87 } else { 88 mtmp->meating = objects[obj->otyp].oc_delay; 89 nutrit = objects[obj->otyp].oc_nutrition; 90 } 91 switch(mtmp->data->msize) { 92 case MZ_TINY: nutrit *= 8; break; 93 case MZ_SMALL: nutrit *= 6; break; 94 default: 95 case MZ_MEDIUM: nutrit *= 5; break; 96 case MZ_LARGE: nutrit *= 4; break; 97 case MZ_HUGE: nutrit *= 3; break; 98 case MZ_GIGANTIC: nutrit *= 2; break; 99 } 100 if(obj->oeaten) { 101 mtmp->meating = eaten_stat(mtmp->meating, obj); 102 nutrit = eaten_stat(nutrit, obj); 103 } 104 } else if (obj->oclass == COIN_CLASS) { 105 mtmp->meating = (int)(obj->quan/2000) + 1; 106 if (mtmp->meating < 0) mtmp->meating = 1; 107 nutrit = (int)(obj->quan/20); 108 if (nutrit < 0) nutrit = 0; 109 } else { 110 /* Unusual pet such as gelatinous cube eating odd stuff. 111 * meating made consistent with wild monsters in mon.c. 112 * nutrit made consistent with polymorphed player nutrit in 113 * eat.c. (This also applies to pets eating gold.) 114 */ 115 mtmp->meating = obj->owt/20 + 1; 116 nutrit = 5*objects[obj->otyp].oc_nutrition; 117 } 118 return nutrit; 119} 120 121/* returns 2 if pet dies, otherwise 1 */ 122int 123dog_eat(mtmp, obj, x, y, devour) 124register struct monst *mtmp; 125register struct obj * obj; 126int x, y; 127boolean devour; 128{ 129 register struct edog *edog = EDOG(mtmp); 130 boolean poly = FALSE, grow = FALSE, heal = FALSE; 131 int nutrit; 132 133 if(edog->hungrytime < monstermoves) 134 edog->hungrytime = monstermoves; 135 nutrit = dog_nutrition(mtmp, obj); 136 poly = polyfodder(obj); 137 grow = mlevelgain(obj); 138 heal = mhealup(obj); 139 if (devour) { 140 if (mtmp->meating > 1) mtmp->meating /= 2; 141 if (nutrit > 1) nutrit = (nutrit * 3) / 4; 142 } 143 edog->hungrytime += nutrit; 144 mtmp->mconf = 0; 145 if (edog->mhpmax_penalty) { 146 /* no longer starving */ 147 mtmp->mhpmax += edog->mhpmax_penalty; 148 edog->mhpmax_penalty = 0; 149 } 150 if (mtmp->mflee && mtmp->mfleetim > 1) mtmp->mfleetim /= 2; 151 if (mtmp->mtame < 20) mtmp->mtame++; 152 if (x != mtmp->mx || y != mtmp->my) { /* moved & ate on same turn */ 153 newsym(x, y); 154 newsym(mtmp->mx, mtmp->my); 155 } 156 if (is_pool(x, y) && !Underwater) { 157 /* Don't print obj */ 158 /* TODO: Reveal presence of sea monster (especially sharks) */ 159 } else 160 /* hack: observe the action if either new or old location is in view */ 161 /* However, invisible monsters should still be "it" even though out of 162 sight locations should not. */ 163 if (cansee(x, y) || cansee(mtmp->mx, mtmp->my)) 164 pline("%s %s %s.", mon_visible(mtmp) ? noit_Monnam(mtmp) : "It", 165 devour ? "devours" : "eats", 166 (obj->oclass == FOOD_CLASS) ? 167 singular(obj, doname) : doname(obj)); 168 /* It's a reward if it's DOGFOOD and the player dropped/threw it. */ 169 /* We know the player had it if invlet is set -dlc */ 170 if(dogfood(mtmp,obj) == DOGFOOD && obj->invlet) 171#ifdef LINT 172 edog->apport = 0; 173#else 174 edog->apport += (int)(200L/ 175 ((long)edog->dropdist + monstermoves - edog->droptime)); 176#endif 177 if (mtmp->data == &mons[PM_RUST_MONSTER] && obj->oerodeproof) { 178 /* The object's rustproofing is gone now */ 179 obj->oerodeproof = 0; 180 mtmp->mstun = 1; 181 if (canseemon(mtmp) && flags.verbose) { 182 pline("%s spits %s out in disgust!", 183 Monnam(mtmp), distant_name(obj,doname)); 184 } 185 } else if (obj == uball) { 186 unpunish(); 187 delobj(obj); 188 } else if (obj == uchain) 189 unpunish(); 190 else if (obj->quan > 1L && obj->oclass == FOOD_CLASS) { 191 obj->quan--; 192 obj->owt = weight(obj); 193 } else 194 delobj(obj); 195 196 if (poly) { 197 (void) newcham(mtmp, (struct permonst *)0, FALSE, 198 cansee(mtmp->mx, mtmp->my)); 199 } 200 /* limit "instant" growth to prevent potential abuse */ 201 if (grow && (int) mtmp->m_lev < (int)mtmp->data->mlevel + 15) { 202 if (!grow_up(mtmp, (struct monst *)0)) return 2; 203 } 204 if (heal) mtmp->mhp = mtmp->mhpmax; 205 return 1; 206} 207 208#endif /* OVLB */ 209#ifdef OVL0 210 211/* hunger effects -- returns TRUE on starvation */ 212STATIC_OVL boolean 213dog_hunger(mtmp, edog) 214register struct monst *mtmp; 215register struct edog *edog; 216{ 217 if (monstermoves > edog->hungrytime + 500) { 218 if (!carnivorous(mtmp->data) && !herbivorous(mtmp->data)) { 219 edog->hungrytime = monstermoves + 500; 220 /* but not too high; it might polymorph */ 221 } else if (!edog->mhpmax_penalty) { 222 /* starving pets are limited in healing */ 223 int newmhpmax = mtmp->mhpmax / 3; 224 mtmp->mconf = 1; 225 edog->mhpmax_penalty = mtmp->mhpmax - newmhpmax; 226 mtmp->mhpmax = newmhpmax; 227 if (mtmp->mhp > mtmp->mhpmax) 228 mtmp->mhp = mtmp->mhpmax; 229 if (mtmp->mhp < 1) goto dog_died; 230 if (cansee(mtmp->mx, mtmp->my)) 231 pline("%s is confused from hunger.", Monnam(mtmp)); 232 else if (couldsee(mtmp->mx, mtmp->my)) 233 beg(mtmp); 234 else 235 You_feel("worried about %s.", y_monnam(mtmp)); 236 stop_occupation(); 237 } else if (monstermoves > edog->hungrytime + 750 || mtmp->mhp < 1) { 238 dog_died: 239 if (mtmp->mleashed 240#ifdef STEED 241 && mtmp != u.usteed 242#endif 243 ) 244 Your("leash goes slack."); 245 else if (cansee(mtmp->mx, mtmp->my)) 246 pline("%s starves.", Monnam(mtmp)); 247 else 248 You_feel("%s for a moment.", 249 Hallucination ? "bummed" : "sad"); 250 mondied(mtmp); 251 return(TRUE); 252 } 253 } 254 return(FALSE); 255} 256 257/* do something with object (drop, pick up, eat) at current position 258 * returns 1 if object eaten (since that counts as dog's move), 2 if died 259 */ 260STATIC_OVL int 261dog_invent(mtmp, edog, udist) 262register struct monst *mtmp; 263register struct edog *edog; 264int udist; 265{ 266 register int omx, omy; 267 struct obj *obj; 268 269 if (mtmp->msleeping || !mtmp->mcanmove) return(0); 270 271 omx = mtmp->mx; 272 omy = mtmp->my; 273 274 /* if we are carrying sth then we drop it (perhaps near @) */ 275 /* Note: if apport == 1 then our behaviour is independent of udist */ 276 /* Use udist+1 so steed won't cause divide by zero */ 277#ifndef GOLDOBJ 278 if(DROPPABLES(mtmp) || mtmp->mgold) { 279#else 280 if(DROPPABLES(mtmp)) { 281#endif 282 if (!rn2(udist+1) || !rn2(edog->apport)) 283 if(rn2(10) < edog->apport){ 284 relobj(mtmp, (int)mtmp->minvis, TRUE); 285 if(edog->apport > 1) edog->apport--; 286 edog->dropdist = udist; /* hpscdi!jon */ 287 edog->droptime = monstermoves; 288 } 289 } else { 290 if((obj=level.objects[omx][omy]) && !index(nofetch,obj->oclass) 291#ifdef MAIL 292 && obj->otyp != SCR_MAIL 293#endif 294 ){ 295 int edible = dogfood(mtmp, obj); 296 297 if ((edible <= CADAVER || 298 /* starving pet is more aggressive about eating */ 299 (edog->mhpmax_penalty && edible == ACCFOOD)) && 300 could_reach_item(mtmp, obj->ox, obj->oy)) 301 return dog_eat(mtmp, obj, omx, omy, FALSE); 302 303 if(can_carry(mtmp, obj) && !obj->cursed && 304 could_reach_item(mtmp, obj->ox, obj->oy)) { 305 if(rn2(20) < edog->apport+3) { 306 if (rn2(udist) || !rn2(edog->apport)) { 307 if (cansee(omx, omy) && flags.verbose) 308 pline("%s picks up %s.", Monnam(mtmp), 309 distant_name(obj, doname)); 310 obj_extract_self(obj); 311 newsym(omx,omy); 312 (void) mpickobj(mtmp,obj); 313 if (attacktype(mtmp->data, AT_WEAP) && 314 mtmp->weapon_check == NEED_WEAPON) { 315 mtmp->weapon_check = NEED_HTH_WEAPON; 316 (void) mon_wield_item(mtmp); 317 } 318 m_dowear(mtmp, FALSE); 319 } 320 } 321 } 322 } 323 } 324 return 0; 325} 326 327/* set dog's goal -- gtyp, gx, gy 328 * returns -1/0/1 (dog's desire to approach player) or -2 (abort move) 329 */ 330STATIC_OVL int 331dog_goal(mtmp, edog, after, udist, whappr) 332register struct monst *mtmp; 333struct edog *edog; 334int after, udist, whappr; 335{ 336 register int omx, omy; 337 boolean in_masters_sight, dog_has_minvent; 338 register struct obj *obj; 339 xchar otyp; 340 int appr; 341 342#ifdef STEED 343 /* Steeds don't move on their own will */ 344 if (mtmp == u.usteed) 345 return (-2); 346#endif 347 348 omx = mtmp->mx; 349 omy = mtmp->my; 350 351 in_masters_sight = couldsee(omx, omy); 352 dog_has_minvent = (DROPPABLES(mtmp) != 0); 353 354 if (!edog || mtmp->mleashed) { /* he's not going anywhere... */ 355 gtyp = APPORT; 356 gx = u.ux; 357 gy = u.uy; 358 } else { 359#define DDIST(x,y) (dist2(x,y,omx,omy)) 360#define SQSRCHRADIUS 5 361 int min_x, max_x, min_y, max_y; 362 register int nx, ny; 363 364 gtyp = UNDEF; /* no goal as yet */ 365 gx = gy = 0; /* suppress 'used before set' message */ 366 367 if ((min_x = omx - SQSRCHRADIUS) < 1) min_x = 1; 368 if ((max_x = omx + SQSRCHRADIUS) >= COLNO) max_x = COLNO - 1; 369 if ((min_y = omy - SQSRCHRADIUS) < 0) min_y = 0; 370 if ((max_y = omy + SQSRCHRADIUS) >= ROWNO) max_y = ROWNO - 1; 371 372 /* nearby food is the first choice, then other objects */ 373 for (obj = fobj; obj; obj = obj->nobj) { 374 nx = obj->ox; 375 ny = obj->oy; 376 if (nx >= min_x && nx <= max_x && ny >= min_y && ny <= max_y) { 377 otyp = dogfood(mtmp, obj); 378 /* skip inferior goals */ 379 if (otyp > gtyp || otyp == UNDEF) 380 continue; 381 /* avoid cursed items unless starving */ 382 if (cursed_object_at(nx, ny) && 383 !(edog->mhpmax_penalty && otyp < MANFOOD)) 384 continue; 385 /* skip completely unreacheable goals */ 386 if (!could_reach_item(mtmp, nx, ny) || 387 !can_reach_location(mtmp, mtmp->mx, mtmp->my, nx, ny)) 388 continue; 389 if (otyp < MANFOOD) { 390 if (otyp < gtyp || DDIST(nx,ny) < DDIST(gx,gy)) { 391 gx = nx; 392 gy = ny; 393 gtyp = otyp; 394 } 395 } else if(gtyp == UNDEF && in_masters_sight && 396 !dog_has_minvent && 397 (!levl[omx][omy].lit || levl[u.ux][u.uy].lit) && 398 (otyp == MANFOOD || m_cansee(mtmp, nx, ny)) && 399 edog->apport > rn2(8) && 400 can_carry(mtmp,obj)) { 401 gx = nx; 402 gy = ny; 403 gtyp = APPORT; 404 } 405 } 406 } 407 } 408 409 /* follow player if appropriate */ 410 if (gtyp == UNDEF || 411 (gtyp != DOGFOOD && gtyp != APPORT && monstermoves < edog->hungrytime)) { 412 gx = u.ux; 413 gy = u.uy; 414 if (after && udist <= 4 && gx == u.ux && gy == u.uy) 415 return(-2); 416 appr = (udist >= 9) ? 1 : (mtmp->mflee) ? -1 : 0; 417 if (udist > 1) { 418 if (!IS_ROOM(levl[u.ux][u.uy].typ) || !rn2(4) || 419 whappr || 420 (dog_has_minvent && rn2(edog->apport))) 421 appr = 1; 422 } 423 /* if you have dog food it'll follow you more closely */ 424 if (appr == 0) { 425 obj = invent; 426 while (obj) { 427 if(dogfood(mtmp, obj) == DOGFOOD) { 428 appr = 1; 429 break; 430 } 431 obj = obj->nobj; 432 } 433 } 434 } else 435 appr = 1; /* gtyp != UNDEF */ 436 if(mtmp->mconf) 437 appr = 0; 438 439#define FARAWAY (COLNO + 2) /* position outside screen */ 440 if (gx == u.ux && gy == u.uy && !in_masters_sight) { 441 register coord *cp; 442 443 cp = gettrack(omx,omy); 444 if (cp) { 445 gx = cp->x; 446 gy = cp->y; 447 if(edog) edog->ogoal.x = 0; 448 } else { 449 /* assume master hasn't moved far, and reuse previous goal */ 450 if(edog && edog->ogoal.x && 451 ((edog->ogoal.x != omx) || (edog->ogoal.y != omy))) { 452 gx = edog->ogoal.x; 453 gy = edog->ogoal.y; 454 edog->ogoal.x = 0; 455 } else { 456 int fardist = FARAWAY * FARAWAY; 457 gx = gy = FARAWAY; /* random */ 458 do_clear_area(omx, omy, 9, wantdoor, 459 (genericptr_t)&fardist); 460 461 /* here gx == FARAWAY e.g. when dog is in a vault */ 462 if (gx == FARAWAY || (gx == omx && gy == omy)) { 463 gx = u.ux; 464 gy = u.uy; 465 } else if(edog) { 466 edog->ogoal.x = gx; 467 edog->ogoal.y = gy; 468 } 469 } 470 } 471 } else if(edog) { 472 edog->ogoal.x = 0; 473 } 474 return appr; 475} 476 477/* return 0 (no move), 1 (move) or 2 (dead) */ 478int 479dog_move(mtmp, after) 480register struct monst *mtmp; 481register int after; /* this is extra fast monster movement */ 482{ 483 int omx, omy; /* original mtmp position */ 484 int appr, whappr, udist; 485 int i, j, k; 486 register struct edog *edog = EDOG(mtmp); 487 struct obj *obj = (struct obj *) 0; 488 xchar otyp; 489 boolean has_edog, cursemsg[9], do_eat = FALSE; 490 xchar nix, niy; /* position mtmp is (considering) moving to */ 491 register int nx, ny; /* temporary coordinates */ 492 xchar cnt, uncursedcnt, chcnt; 493 int chi = -1, nidist, ndist; 494 coord poss[9]; 495 long info[9], allowflags; 496#define GDIST(x,y) (dist2(x,y,gx,gy)) 497 498 /* 499 * Tame Angels have isminion set and an ispriest structure instead of 500 * an edog structure. Fortunately, guardian Angels need not worry 501 * about mundane things like eating and fetching objects, and can 502 * spend all their energy defending the player. (They are the only 503 * monsters with other structures that can be tame.) 504 */ 505 has_edog = !mtmp->isminion; 506 507 omx = mtmp->mx; 508 omy = mtmp->my; 509 if (has_edog && dog_hunger(mtmp, edog)) return(2); /* starved */ 510 511 udist = distu(omx,omy); 512#ifdef STEED 513 /* Let steeds eat and maybe throw rider during Conflict */ 514 if (mtmp == u.usteed) { 515 if (Conflict && !resist(mtmp, RING_CLASS, 0, 0)) { 516 dismount_steed(DISMOUNT_THROWN); 517 return (1); 518 } 519 udist = 1; 520 } else 521#endif 522 /* maybe we tamed him while being swallowed --jgm */ 523 if (!udist) return(0); 524 525 nix = omx; /* set before newdogpos */ 526 niy = omy; 527 cursemsg[0] = FALSE; /* lint suppression */ 528 info[0] = 0; /* ditto */ 529 530 if (has_edog) { 531 j = dog_invent(mtmp, edog, udist); 532 if (j == 2) return 2; /* died */ 533 else if (j == 1) goto newdogpos; /* eating something */ 534 535 whappr = (monstermoves - edog->whistletime < 5); 536 } else 537 whappr = 0; 538 539 appr = dog_goal(mtmp, has_edog ? edog : (struct edog *)0, 540 after, udist, whappr); 541 if (appr == -2) return(0); 542 543 allowflags = ALLOW_M | ALLOW_TRAPS | ALLOW_SSM | ALLOW_SANCT; 544 if (passes_walls(mtmp->data)) allowflags |= (ALLOW_ROCK | ALLOW_WALL); 545 if (passes_bars(mtmp->data)) allowflags |= ALLOW_BARS; 546 if (throws_rocks(mtmp->data)) allowflags |= ALLOW_ROCK; 547 if (Conflict && !resist(mtmp, RING_CLASS, 0, 0)) { 548 allowflags |= ALLOW_U; 549 if (!has_edog) { 550 coord mm; 551 /* Guardian angel refuses to be conflicted; rather, 552 * it disappears, angrily, and sends in some nasties 553 */ 554 if (canspotmon(mtmp)) { 555 pline("%s rebukes you, saying:", Monnam(mtmp)); 556 verbalize("Since you desire conflict, have some more!"); 557 } 558 mongone(mtmp); 559 i = rnd(4); 560 while(i--) { 561 mm.x = u.ux; 562 mm.y = u.uy; 563 if(enexto(&mm, mm.x, mm.y, &mons[PM_ANGEL])) 564 (void) mk_roamer(&mons[PM_ANGEL], u.ualign.type, 565 mm.x, mm.y, FALSE); 566 } 567 return(2); 568 569 } 570 } 571 if (!Conflict && !mtmp->mconf && 572 mtmp == u.ustuck && !sticks(youmonst.data)) { 573 unstuck(mtmp); /* swallowed case handled above */ 574 You("get released!"); 575 } 576 if (!nohands(mtmp->data) && !verysmall(mtmp->data)) { 577 allowflags |= OPENDOOR; 578 if (m_carrying(mtmp, SKELETON_KEY)) allowflags |= BUSTDOOR; 579 } 580 if (is_giant(mtmp->data)) allowflags |= BUSTDOOR; 581 if (tunnels(mtmp->data)) allowflags |= ALLOW_DIG; 582 cnt = mfndpos(mtmp, poss, info, allowflags); 583 584 /* Normally dogs don't step on cursed items, but if they have no 585 * other choice they will. This requires checking ahead of time 586 * to see how many uncursed item squares are around. 587 */ 588 uncursedcnt = 0; 589 for (i = 0; i < cnt; i++) { 590 nx = poss[i].x; ny = poss[i].y; 591 if (MON_AT(nx,ny) && !(info[i] & ALLOW_M)) continue; 592 if (cursed_object_at(nx, ny)) continue; 593 uncursedcnt++; 594 } 595 596 chcnt = 0; 597 chi = -1; 598 nidist = GDIST(nix,niy); 599 600 for (i = 0; i < cnt; i++) { 601 nx = poss[i].x; 602 ny = poss[i].y; 603 cursemsg[i] = FALSE; 604 605 /* if leashed, we drag him along. */ 606 if (mtmp->mleashed && distu(nx, ny) > 4) continue; 607 608 /* if a guardian, try to stay close by choice */ 609 if (!has_edog && 610 (j = distu(nx, ny)) > 16 && j >= udist) continue; 611 612 if ((info[i] & ALLOW_M) && MON_AT(nx, ny)) { 613 int mstatus; 614 register struct monst *mtmp2 = m_at(nx,ny); 615 616 if ((int)mtmp2->m_lev >= (int)mtmp->m_lev+2 || 617 (mtmp2->data == &mons[PM_FLOATING_EYE] && rn2(10) && 618 mtmp->mcansee && haseyes(mtmp->data) && mtmp2->mcansee 619 && (perceives(mtmp->data) || !mtmp2->minvis)) || 620 (mtmp2->data==&mons[PM_GELATINOUS_CUBE] && rn2(10)) || 621 (max_passive_dmg(mtmp2, mtmp) >= mtmp->mhp) || 622 ((mtmp->mhp*4 < mtmp->mhpmax 623 || mtmp2->data->msound == MS_GUARDIAN 624 || mtmp2->data->msound == MS_LEADER) && 625 mtmp2->mpeaceful && !Conflict) || 626 (touch_petrifies(mtmp2->data) && 627 !resists_ston(mtmp))) 628 continue; 629 630 if (after) return(0); /* hit only once each move */ 631 632 notonhead = 0; 633 mstatus = mattackm(mtmp, mtmp2); 634 635 /* aggressor (pet) died */ 636 if (mstatus & MM_AGR_DIED) return 2; 637 638 if ((mstatus & MM_HIT) && !(mstatus & MM_DEF_DIED) && 639 rn2(4) && mtmp2->mlstmv != monstermoves && 640 !onscary(mtmp->mx, mtmp->my, mtmp2) && 641 /* monnear check needed: long worms hit on tail */ 642 monnear(mtmp2, mtmp->mx, mtmp->my)) { 643 mstatus = mattackm(mtmp2, mtmp); /* return attack */ 644 if (mstatus & MM_DEF_DIED) return 2; 645 } 646 647 return 0; 648 } 649 650 { /* Dog avoids harmful traps, but perhaps it has to pass one 651 * in order to follow player. (Non-harmful traps do not 652 * have ALLOW_TRAPS in info[].) The dog only avoids the 653 * trap if you've seen it, unlike enemies who avoid traps 654 * if they've seen some trap of that type sometime in the 655 * past. (Neither behavior is really realistic.) 656 */ 657 struct trap *trap; 658 659 if ((info[i] & ALLOW_TRAPS) && (trap = t_at(nx,ny))) { 660 if (mtmp->mleashed) { 661 if (flags.soundok) whimper(mtmp); 662 } else 663 /* 1/40 chance of stepping on it anyway, in case 664 * it has to pass one to follow the player... 665 */ 666 if (trap->tseen && rn2(40)) continue; 667 } 668 } 669 670 /* dog eschews cursed objects, but likes dog food */ 671 /* (minion isn't interested; `cursemsg' stays FALSE) */ 672 if (has_edog) 673 for (obj = level.objects[nx][ny]; obj; obj = obj->nexthere) { 674 if (obj->cursed) cursemsg[i] = TRUE; 675 else if ((otyp = dogfood(mtmp, obj)) < MANFOOD && 676 (otyp < ACCFOOD || edog->hungrytime <= monstermoves)) { 677 /* Note: our dog likes the food so much that he 678 * might eat it even when it conceals a cursed object */ 679 nix = nx; 680 niy = ny; 681 chi = i; 682 do_eat = TRUE; 683 cursemsg[i] = FALSE; /* not reluctant */ 684 goto newdogpos; 685 } 686 } 687 /* didn't find something to eat; if we saw a cursed item and 688 aren't being forced to walk on it, usually keep looking */ 689 if (cursemsg[i] && !mtmp->mleashed && uncursedcnt > 0 && 690 rn2(13 * uncursedcnt)) continue; 691 692 /* lessen the chance of backtracking to previous position(s) */ 693 k = has_edog ? uncursedcnt : cnt; 694 for (j = 0; j < MTSZ && j < k - 1; j++) 695 if (nx == mtmp->mtrack[j].x && ny == mtmp->mtrack[j].y) 696 if (rn2(MTSZ * (k - j))) goto nxti; 697 698 j = ((ndist = GDIST(nx,ny)) - nidist) * appr; 699 if ((j == 0 && !rn2(++chcnt)) || j < 0 || 700 (j > 0 && !whappr && 701 ((omx == nix && omy == niy && !rn2(3)) 702 || !rn2(12)) 703 )) { 704 nix = nx; 705 niy = ny; 706 nidist = ndist; 707 if(j < 0) chcnt = 0; 708 chi = i; 709 } 710 nxti: ; 711 } 712newdogpos: 713 if (nix != omx || niy != omy) { 714 struct obj *mw_tmp; 715 716 if (info[chi] & ALLOW_U) { 717 if (mtmp->mleashed) { /* play it safe */ 718 pline("%s breaks loose of %s leash!", 719 Monnam(mtmp), mhis(mtmp)); 720 m_unleash(mtmp, FALSE); 721 } 722 (void) mattacku(mtmp); 723 return(0); 724 } 725 if (!m_in_out_region(mtmp, nix, niy)) 726 return 1; 727 if (((IS_ROCK(levl[nix][niy].typ) && may_dig(nix,niy)) || 728 closed_door(nix, niy)) && 729 mtmp->weapon_check != NO_WEAPON_WANTED && 730 tunnels(mtmp->data) && needspick(mtmp->data)) { 731 if (closed_door(nix, niy)) { 732 if (!(mw_tmp = MON_WEP(mtmp)) || 733 !is_pick(mw_tmp) || !is_axe(mw_tmp)) 734 mtmp->weapon_check = NEED_PICK_OR_AXE; 735 } else if (IS_TREE(levl[nix][niy].typ)) { 736 if (!(mw_tmp = MON_WEP(mtmp)) || !is_axe(mw_tmp)) 737 mtmp->weapon_check = NEED_AXE; 738 } else if (!(mw_tmp = MON_WEP(mtmp)) || !is_pick(mw_tmp)) { 739 mtmp->weapon_check = NEED_PICK_AXE; 740 } 741 if (mtmp->weapon_check >= NEED_PICK_AXE && 742 mon_wield_item(mtmp)) 743 return 0; 744 } 745 /* insert a worm_move() if worms ever begin to eat things */ 746 remove_monster(omx, omy); 747 place_monster(mtmp, nix, niy); 748 if (cursemsg[chi] && (cansee(omx,omy) || cansee(nix,niy))) 749 pline("%s moves only reluctantly.", Monnam(mtmp)); 750 for (j=MTSZ-1; j>0; j--) mtmp->mtrack[j] = mtmp->mtrack[j-1]; 751 mtmp->mtrack[0].x = omx; 752 mtmp->mtrack[0].y = omy; 753 /* We have to know if the pet's gonna do a combined eat and 754 * move before moving it, but it can't eat until after being 755 * moved. Thus the do_eat flag. 756 */ 757 if (do_eat) { 758 if (dog_eat(mtmp, obj, omx, omy, FALSE) == 2) return 2; 759 } 760 } else if (mtmp->mleashed && distu(omx, omy) > 4) { 761 /* an incredible kludge, but the only way to keep pooch near 762 * after it spends time eating or in a trap, etc. 763 */ 764 coord cc; 765 766 nx = sgn(omx - u.ux); 767 ny = sgn(omy - u.uy); 768 cc.x = u.ux + nx; 769 cc.y = u.uy + ny; 770 if (goodpos(cc.x, cc.y, mtmp, 0)) goto dognext; 771 772 i = xytod(nx, ny); 773 for (j = (i + 7)%8; j < (i + 1)%8; j++) { 774 dtoxy(&cc, j); 775 if (goodpos(cc.x, cc.y, mtmp, 0)) goto dognext; 776 } 777 for (j = (i + 6)%8; j < (i + 2)%8; j++) { 778 dtoxy(&cc, j); 779 if (goodpos(cc.x, cc.y, mtmp, 0)) goto dognext; 780 } 781 cc.x = mtmp->mx; 782 cc.y = mtmp->my; 783dognext: 784 if (!m_in_out_region(mtmp, nix, niy)) 785 return 1; 786 remove_monster(mtmp->mx, mtmp->my); 787 place_monster(mtmp, cc.x, cc.y); 788 newsym(cc.x,cc.y); 789 set_apparxy(mtmp); 790 } 791 return(1); 792} 793 794/* check if a monster could pick up objects from a location */ 795STATIC_OVL boolean 796could_reach_item(mon, nx, ny) 797struct monst *mon; 798xchar nx, ny; 799{ 800 if ((!is_pool(nx,ny) || is_swimmer(mon->data)) && 801 (!is_lava(nx,ny) || likes_lava(mon->data)) && 802 (!sobj_at(BOULDER,nx,ny) || throws_rocks(mon->data))) 803 return TRUE; 804 return FALSE; 805} 806 807/* Hack to prevent a dog from being endlessly stuck near an object that 808 * it can't reach, such as caught in a teleport scroll niche. It recursively 809 * checks to see if the squares in between are good. The checking could be a 810 * little smarter; a full check would probably be useful in m_move() too. 811 * Since the maximum food distance is 5, this should never be more than 5 calls 812 * deep. 813 */ 814STATIC_OVL boolean 815can_reach_location(mon, mx, my, fx, fy) 816struct monst *mon; 817xchar mx, my, fx, fy; 818{ 819 int i, j; 820 int dist; 821 822 if (mx == fx && my == fy) return TRUE; 823 if (!isok(mx, my)) return FALSE; /* should not happen */ 824 825 dist = dist2(mx, my, fx, fy); 826 for(i=mx-1; i<=mx+1; i++) { 827 for(j=my-1; j<=my+1; j++) { 828 if (!isok(i, j)) 829 continue; 830 if (dist2(i, j, fx, fy) >= dist) 831 continue; 832 if (IS_ROCK(levl[i][j].typ) && !passes_walls(mon->data) && 833 (!may_dig(i,j) || !tunnels(mon->data))) 834 continue; 835 if (IS_DOOR(levl[i][j].typ) && 836 (levl[i][j].doormask & (D_CLOSED | D_LOCKED))) 837 continue; 838 if (!could_reach_item(mon, i, j)) 839 continue; 840 if (can_reach_location(mon, i, j, fx, fy)) 841 return TRUE; 842 } 843 } 844 return FALSE; 845} 846 847#endif /* OVL0 */ 848#ifdef OVLB 849 850/*ARGSUSED*/ /* do_clear_area client */ 851STATIC_PTR void 852wantdoor(x, y, distance) 853int x, y; 854genericptr_t distance; 855{ 856 int ndist; 857 858 if (*(int*)distance > (ndist = distu(x, y))) { 859 gx = x; 860 gy = y; 861 *(int*)distance = ndist; 862 } 863} 864 865#endif /* OVLB */ 866 867/*dogmove.c*/ 868