1 | /* SCCS Id: @(#)monmove.c 3.3 2000/07/24 */ 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 "mfndpos.h" 7 | #include "artifact.h" 8 | 9 | extern boolean notonhead; 10 | 11 | #ifdef OVL0 12 | 13 | STATIC_DCL int FDECL(disturb,(struct monst *)); 14 | STATIC_DCL void FDECL(distfleeck,(struct monst *,int *,int *,int *)); 15 | STATIC_DCL int FDECL(m_arrival, (struct monst *)); 16 | STATIC_DCL void FDECL(watch_on_duty,(struct monst *)); 17 | 18 | #endif /* OVL0 */ 19 | #ifdef OVLB 20 | 21 | boolean /* TRUE : mtmp died */ 22 | mb_trapped(mtmp) 23 | register struct monst *mtmp; 24 | { 25 | if (flags.verbose) { 26 | if (cansee(mtmp->mx, mtmp->my)) 27 | pline("KABOOM!! You see a door explode."); 28 | else if (flags.soundok) 29 | You_hear("a distant explosion."); 30 | } 31 | wake_nearto(mtmp->mx, mtmp->my, 7*7); 32 | mtmp->mstun = 1; 33 | mtmp->mhp -= rnd(15); 34 | if(mtmp->mhp <= 0) { 35 | mondied(mtmp); 36 | if (mtmp->mhp > 0) /* lifesaved */ 37 | return(FALSE); 38 | else 39 | return(TRUE); 40 | } 41 | return(FALSE); 42 | } 43 | 44 | #endif /* OVLB */ 45 | #ifdef OVL0 46 | 47 | STATIC_OVL void 48 | watch_on_duty(mtmp) 49 | register struct monst *mtmp; 50 | { 51 | register s_level *slev = Is_special(&u.uz); 52 | int x, y; 53 | 54 | if(slev && slev->flags.town && mtmp->mpeaceful && 55 | mtmp->mcansee && m_canseeu(mtmp) && !rn2(3)) { 56 | 57 | if(picking_lock(&x, &y) && IS_DOOR(levl[x][y].typ) && 58 | (levl[x][y].doormask & D_LOCKED)) { 59 | 60 | if(couldsee(mtmp->mx, mtmp->my)) { 61 | 62 | pline("%s yells:", Amonnam(mtmp)); 63 | if(levl[x][y].looted & D_WARNED) { 64 | verbalize("Halt, thief! You're under arrest!"); 65 | (void) angry_guards(!(flags.soundok)); 66 | } else { 67 | verbalize("Hey, stop picking that lock!"); 68 | levl[x][y].looted |= D_WARNED; 69 | } 70 | stop_occupation(); 71 | } 72 | } 73 | } 74 | } 75 | 76 | #endif /* OVL0 */ 77 | #ifdef OVL1 78 | 79 | int 80 | dochugw(mtmp) 81 | register struct monst *mtmp; 82 | { 83 | register int x = mtmp->mx, y = mtmp->my; 84 | boolean already_saw_mon = !occupation ? 0 : canspotmon(mtmp); 85 | int rd = dochug(mtmp); 86 | #if 0 87 | /* part of the original warning code which was replaced in 3.3.1 */ 88 | int dd; 89 | 90 | if(Warning && !rd && !mtmp->mpeaceful && 91 | (dd = distu(mtmp->mx,mtmp->my)) < distu(x,y) && 92 | dd < 100 && !canseemon(mtmp)) { 93 | /* Note: this assumes we only want to warn against the monster to 94 | * which the weapon does extra damage, as there is no "monster 95 | * which the weapon warns against" field. 96 | */ 97 | if (spec_ability(uwep, SPFX_WARN) && spec_dbon(uwep, mtmp, 1)) 98 | warnlevel = 100; 99 | else if ((int) (mtmp->m_lev / 4) > warnlevel) 100 | warnlevel = (mtmp->m_lev / 4); 101 | } 102 | #endif /* 0 */ 103 | 104 | /* a similar check is in monster_nearby() in hack.c */ 105 | /* check whether hero notices monster and stops current activity */ 106 | if (occupation && !rd && !Confusion && 107 | (!mtmp->mpeaceful || Hallucination) && 108 | /* it's close enough to be a threat */ 109 | distu(mtmp->mx,mtmp->my) <= (BOLT_LIM+1)*(BOLT_LIM+1) && 110 | /* and either couldn't see it before, or it was too far away */ 111 | (!already_saw_mon || !couldsee(x,y) || 112 | distu(x,y) > (BOLT_LIM+1)*(BOLT_LIM+1)) && 113 | /* can see it now, or sense it and would normally see it */ 114 | (canseemon(mtmp) || 115 | (sensemon(mtmp) && couldsee(mtmp->mx,mtmp->my))) && 116 | !noattacks(mtmp->data) && !onscary(u.ux, u.uy, mtmp)) 117 | stop_occupation(); 118 | 119 | return(rd); 120 | } 121 | 122 | #endif /* OVL1 */ 123 | #ifdef OVL2 124 | 125 | boolean 126 | onscary(x, y, mtmp) 127 | int x, y; 128 | struct monst *mtmp; 129 | { 130 | if (mtmp->isshk || mtmp->isgd || mtmp->iswiz || !mtmp->mcansee || 131 | mtmp->mpeaceful || mtmp->data->mlet == S_HUMAN || 132 | is_lminion(mtmp->data) || is_rider(mtmp->data) || 133 | mtmp->data == &mons[PM_MINOTAUR]) 134 | return(FALSE); 135 | 136 | return (boolean)(sobj_at(SCR_SCARE_MONSTER, x, y) 137 | #ifdef ELBERETH 138 | || sengr_at("Elbereth", x, y) 139 | #endif 140 | || (mtmp->data->mlet == S_VAMPIRE 141 | && IS_ALTAR(levl[x][y].typ))); 142 | } 143 | 144 | #endif /* OVL2 */ 145 | #ifdef OVL0 146 | 147 | /* regenerate lost hit points */ 148 | void 149 | mon_regen(mon, digest_meal) 150 | struct monst *mon; 151 | boolean digest_meal; 152 | { 153 | if (mon->mhp < mon->mhpmax && 154 | (moves % 20 == 0 || regenerates(mon->data))) mon->mhp++; 155 | if (mon->mspec_used) mon->mspec_used--; 156 | if (digest_meal) { 157 | if (mon->meating) mon->meating--; 158 | } 159 | } 160 | 161 | /* 162 | * Possibly awaken the given monster. Return a 1 if the monster has been 163 | * jolted awake. 164 | */ 165 | STATIC_OVL int 166 | disturb(mtmp) 167 | register struct monst *mtmp; 168 | { 169 | /* 170 | * + Ettins are hard to surprise. 171 | * + Nymphs, jabberwocks, and leprechauns do not easily wake up. 172 | * 173 | * Wake up if: 174 | * in direct LOS AND 175 | * within 10 squares AND 176 | * not stealthy or (mon is an ettin and 9/10) AND 177 | * (mon is not a nymph, jabberwock, or leprechaun) or 1/50 AND 178 | * Aggravate or mon is (dog or human) or 179 | * (1/7 and mon is not mimicing furniture or object) 180 | */ 181 | if(couldsee(mtmp->mx,mtmp->my) && 182 | distu(mtmp->mx,mtmp->my) <= 100 && 183 | (!Stealth || (mtmp->data == &mons[PM_ETTIN] && rn2(10))) && 184 | (!(mtmp->data->mlet == S_NYMPH 185 | || mtmp->data == &mons[PM_JABBERWOCK] 186 | #if 0 /* DEFERRED */ 187 | || mtmp->data == &mons[PM_VORPAL_JABBERWOCK] 188 | #endif 189 | || mtmp->data->mlet == S_LEPRECHAUN) || !rn2(50)) && 190 | (Aggravate_monster 191 | || (mtmp->data->mlet == S_DOG || 192 | mtmp->data->mlet == S_HUMAN) 193 | || (!rn2(7) && mtmp->m_ap_type != M_AP_FURNITURE && 194 | mtmp->m_ap_type != M_AP_OBJECT) )) { 195 | mtmp->msleeping = 0; 196 | return(1); 197 | } 198 | return(0); 199 | } 200 | 201 | STATIC_OVL void 202 | distfleeck(mtmp,inrange,nearby,scared) 203 | register struct monst *mtmp; 204 | int *inrange, *nearby, *scared; 205 | { 206 | int seescaryx, seescaryy; 207 | 208 | *inrange = (dist2(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy) <= 209 | (BOLT_LIM * BOLT_LIM)); 210 | *nearby = *inrange && monnear(mtmp, mtmp->mux, mtmp->muy); 211 | 212 | /* Note: if your image is displaced, the monster sees the Elbereth 213 | * at your displaced position, thus never attacking your displaced 214 | * position, but possibly attacking you by accident. If you are 215 | * invisible, it sees the Elbereth at your real position, thus never 216 | * running into you by accident but possibly attacking the spot 217 | * where it guesses you are. 218 | */ 219 | if (!mtmp->mcansee || (Invis && !perceives(mtmp->data))) { 220 | seescaryx = mtmp->mux; 221 | seescaryy = mtmp->muy; 222 | } else { 223 | seescaryx = u.ux; 224 | seescaryy = u.uy; 225 | } 226 | *scared = (*nearby && (onscary(seescaryx, seescaryy, mtmp) || 227 | (!mtmp->mpeaceful && 228 | in_your_sanctuary(mtmp, 0, 0)))); 229 | 230 | if(*scared && !mtmp->mflee) { 231 | if (!sticks(youmonst.data)) 232 | unstuck(mtmp); /* monster lets go when fleeing */ 233 | mtmp->mflee = 1; 234 | #ifdef STUPID 235 | if (rn2(7)) 236 | mtmp->mfleetim = rnd(10); 237 | else 238 | mtmp->mfleetim = rnd(100); 239 | #else 240 | mtmp->mfleetim = rnd(rn2(7) ? 10 : 100); 241 | #endif 242 | } 243 | 244 | } 245 | 246 | /* perform a special one-time action for a monster; returns -1 if nothing 247 | special happened, 0 if monster uses up its turn, 1 if monster is killed */ 248 | STATIC_OVL int 249 | m_arrival(mon) 250 | struct monst *mon; 251 | { 252 | mon->mstrategy &= ~STRAT_ARRIVE; /* always reset */ 253 | 254 | return -1; 255 | } 256 | 257 | /* returns 1 if monster died moving, 0 otherwise */ 258 | /* The whole dochugw/m_move/distfleeck/mfndpos section is serious spaghetti 259 | * code. --KAA 260 | */ 261 | int 262 | dochug(mtmp) 263 | register struct monst *mtmp; 264 | { 265 | register struct permonst *mdat; 266 | register int tmp=0; 267 | int inrange, nearby, scared; 268 | 269 | /* Pre-movement adjustments */ 270 | 271 | mdat = mtmp->data; 272 | 273 | if (mtmp->mstrategy & STRAT_ARRIVE) { 274 | int res = m_arrival(mtmp); 275 | if (res >= 0) return res; 276 | } 277 | 278 | /* check for waitmask status change */ 279 | if ((mtmp->mstrategy & STRAT_WAITFORU) && 280 | (m_canseeu(mtmp) || mtmp->mhp < mtmp->mhpmax)) 281 | mtmp->mstrategy &= ~STRAT_WAITFORU; 282 | 283 | /* update quest status flags */ 284 | quest_stat_check(mtmp); 285 | 286 | if (!mtmp->mcanmove || (mtmp->mstrategy & STRAT_WAITMASK)) { 287 | if (Hallucination) newsym(mtmp->mx,mtmp->my); 288 | if (mtmp->mcanmove && (mtmp->mstrategy & STRAT_CLOSE) && 289 | !mtmp->msleeping && monnear(mtmp, u.ux, u.uy)) 290 | quest_talk(mtmp); /* give the leaders a chance to speak */ 291 | return(0); /* other frozen monsters can't do anything */ 292 | } 293 | 294 | /* there is a chance we will wake it */ 295 | if (mtmp->msleeping && !disturb(mtmp)) { 296 | if (Hallucination) newsym(mtmp->mx,mtmp->my); 297 | return(0); 298 | } 299 | 300 | /* not frozen or sleeping: wipe out texts written in the dust */ 301 | wipe_engr_at(mtmp->mx, mtmp->my, 1); 302 | 303 | /* confused monsters get unconfused with small probability */ 304 | if (mtmp->mconf && !rn2(50)) mtmp->mconf = 0; 305 | 306 | /* stunned monsters get un-stunned with larger probability */ 307 | if (mtmp->mstun && !rn2(10)) mtmp->mstun = 0; 308 | 309 | /* some monsters teleport */ 310 | if (mtmp->mflee && !rn2(40) && can_teleport(mdat) && !mtmp->iswiz && 311 | !level.flags.noteleport) { 312 | rloc(mtmp); 313 | return(0); 314 | } 315 | if (mdat->msound == MS_SHRIEK && !um_dist(mtmp->mx, mtmp->my, 1)) 316 | m_respond(mtmp); 317 | if (mdat == &mons[PM_MEDUSA] && cansee(mtmp->mx, mtmp->my)) 318 | m_respond(mtmp); 319 | if (mtmp->mhp <= 0) return(1); /* m_respond gaze can kill medusa */ 320 | 321 | /* fleeing monsters might regain courage */ 322 | if (mtmp->mflee && !mtmp->mfleetim 323 | && mtmp->mhp == mtmp->mhpmax && !rn2(25)) mtmp->mflee = 0; 324 | 325 | set_apparxy(mtmp); 326 | /* Must be done after you move and before the monster does. The 327 | * set_apparxy() call in m_move() doesn't suffice since the variables 328 | * inrange, etc. all depend on stuff set by set_apparxy(). 329 | */ 330 | 331 | /* Monsters that want to acquire things */ 332 | /* may teleport, so do it before inrange is set */ 333 | if(is_covetous(mdat)) (void) tactics(mtmp); 334 | 335 | /* check distance and scariness of attacks */ 336 | distfleeck(mtmp,&inrange,&nearby,&scared); 337 | 338 | if(find_defensive(mtmp)) { 339 | if (use_defensive(mtmp) != 0) 340 | return 1; 341 | } else if(find_misc(mtmp)) { 342 | if (use_misc(mtmp) != 0) 343 | return 1; 344 | } 345 | 346 | /* Demonic Blackmail! */ 347 | if(nearby && mdat->msound == MS_BRIBE && 348 | mtmp->mpeaceful && !mtmp->mtame && !u.uswallow) { 349 | if (mtmp->mux != u.ux || mtmp->muy != u.uy) { 350 | pline("%s whispers at thin air.", 351 | cansee(mtmp->mux, mtmp->muy) ? Monnam(mtmp) : "It"); 352 | 353 | if (is_demon(youmonst.data)) { 354 | /* "Good hunting, brother" */ 355 | if (!tele_restrict(mtmp)) rloc(mtmp); 356 | } else { 357 | mtmp->minvis = mtmp->perminvis = 0; 358 | /* Why? For the same reason in real demon talk */ 359 | pline("%s gets angry!", Amonnam(mtmp)); 360 | mtmp->mpeaceful = 0; 361 | /* since no way is an image going to pay it off */ 362 | } 363 | } else if(demon_talk(mtmp)) return(1); /* you paid it off */ 364 | } 365 | 366 | /* the watch will look around and see if you are up to no good :-) */ 367 | if (mdat == &mons[PM_WATCHMAN] || mdat == &mons[PM_WATCH_CAPTAIN]) 368 | watch_on_duty(mtmp); 369 | 370 | else if (is_mind_flayer(mdat) && !rn2(20)) { 371 | struct monst *m2, *nmon = (struct monst *)0; 372 | 373 | if (canseemon(mtmp)) 374 | pline("%s concentrates.", Monnam(mtmp)); 375 | if (distu(mtmp->mx, mtmp->my) > BOLT_LIM * BOLT_LIM) { 376 | You("sense a faint wave of psychic energy."); 377 | goto toofar; 378 | } 379 | pline("A wave of psychic energy pours over you!"); 380 | if (mtmp->mpeaceful && 381 | (!Conflict || resist(mtmp, RING_CLASS, 0, 0))) 382 | pline("It feels quite soothing."); 383 | else { 384 | register boolean m_sen = sensemon(mtmp); 385 | 386 | if (m_sen || (Blind_telepat && rn2(2)) || !rn2(10)) { 387 | int dmg; 388 | pline("It locks on to your %s!", 389 | m_sen ? "telepathy" : 390 | Blind_telepat ? "latent telepathy" : "mind"); 391 | dmg = rnd(15); 392 | if (Half_spell_damage) dmg = (dmg+1) / 2; 393 | losehp(dmg, "psychic blast", KILLED_BY_AN); 394 | } 395 | } 396 | for(m2=fmon; m2; m2 = nmon) { 397 | nmon = m2->nmon; 398 | if (DEADMONSTER(m2)) continue; 399 | if (m2->mpeaceful == mtmp->mpeaceful) continue; 400 | if (mindless(m2->data)) continue; 401 | if (m2 == mtmp) continue; 402 | if ((telepathic(m2->data) && 403 | (rn2(2) || m2->mblinded)) || !rn2(10)) { 404 | if (cansee(m2->mx, m2->my)) 405 | pline("It locks on to %s.", mon_nam(m2)); 406 | m2->mhp -= rnd(15); 407 | if (m2->mhp <= 0) 408 | monkilled(m2, "", AD_DRIN); 409 | } 410 | } 411 | } 412 | toofar: 413 | /* If monster is nearby you, and has to wield a weapon, do so. This 414 | * costs the monster a move, of course. 415 | */ 416 | if((!mtmp->mpeaceful || Conflict) && inrange && 417 | dist2(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy) <= 8 418 | && attacktype(mdat, AT_WEAP)) { 419 | struct obj *mw_tmp; 420 | 421 | /* The scared check is necessary. Otherwise a monster that is 422 | * one square near the player but fleeing into a wall would keep 423 | * switching between pick-axe and weapon. 424 | */ 425 | mw_tmp = MON_WEP(mtmp); 426 | if (!(scared && mw_tmp && is_pick(mw_tmp)) && 427 | mtmp->weapon_check == NEED_WEAPON) { 428 | mtmp->weapon_check = NEED_HTH_WEAPON; 429 | if (mon_wield_item(mtmp) != 0) return(0); 430 | } 431 | } 432 | 433 | /* Now the actual movement phase */ 434 | 435 | if(!nearby || mtmp->mflee || scared || 436 | mtmp->mconf || mtmp->mstun || (mtmp->minvis && !rn2(3)) || 437 | (mdat->mlet == S_LEPRECHAUN && !u.ugold && (mtmp->mgold || rn2(2))) || 438 | (is_wanderer(mdat) && !rn2(4)) || (Conflict && !mtmp->iswiz) || 439 | (!mtmp->mcansee && !rn2(4)) || mtmp->mpeaceful) { 440 | 441 | tmp = m_move(mtmp, 0); 442 | distfleeck(mtmp,&inrange,&nearby,&scared); /* recalc */ 443 | 444 | switch (tmp) { 445 | case 0: /* no movement, but it can still attack you */ 446 | case 3: /* absolutely no movement */ 447 | /* for pets, case 0 and 3 are equivalent */ 448 | /* During hallucination, monster appearance should 449 | * still change - even if it doesn't move. 450 | */ 451 | if(Hallucination) newsym(mtmp->mx,mtmp->my); 452 | break; 453 | case 1: /* monster moved */ 454 | /* Maybe it stepped on a trap and fell asleep... */ 455 | if (mtmp->msleeping || !mtmp->mcanmove) return(0); 456 | if(!nearby && 457 | (ranged_attk(mdat) || find_offensive(mtmp))) 458 | break; 459 | else if(u.uswallow && mtmp == u.ustuck) { 460 | /* a monster that's digesting you can move at the 461 | * same time -dlc 462 | */ 463 | return(mattacku(mtmp)); 464 | } else 465 | return(0); 466 | /*NOTREACHED*/ 467 | break; 468 | case 2: /* monster died */ 469 | return(1); 470 | } 471 | } 472 | 473 | /* Now, attack the player if possible - one attack set per monst */ 474 | 475 | if (!mtmp->mpeaceful || 476 | (Conflict && !resist(mtmp, RING_CLASS, 0, 0))) { 477 | if(inrange && !noattacks(mdat) && u.uhp > 0 && !scared && tmp != 3) 478 | if(mattacku(mtmp)) return(1); /* monster died (e.g. exploded) */ 479 | 480 | if(mtmp->wormno) wormhitu(mtmp); 481 | } 482 | /* special speeches for quest monsters */ 483 | if (!mtmp->msleeping && mtmp->mcanmove && nearby) 484 | quest_talk(mtmp); 485 | /* extra emotional attack for vile monsters */ 486 | if (inrange && mtmp->data->msound == MS_CUSS && !mtmp->mpeaceful && 487 | couldsee(mtmp->mx, mtmp->my) && !mtmp->minvis && !rn2(5)) 488 | cuss(mtmp); 489 | 490 | return(tmp == 2); 491 | } 492 | 493 | static NEARDATA const char practical[] = { WEAPON_CLASS, ARMOR_CLASS, GEM_CLASS, FOOD_CLASS, 0 }; 494 | static NEARDATA const char magical[] = { 495 | AMULET_CLASS, POTION_CLASS, SCROLL_CLASS, WAND_CLASS, RING_CLASS, 496 | SPBOOK_CLASS, 0 }; 497 | static NEARDATA const char indigestion[] = { BALL_CLASS, ROCK_CLASS, 0 }; 498 | static NEARDATA const char boulder_class[] = { ROCK_CLASS, 0 }; 499 | static NEARDATA const char gem_class[] = { GEM_CLASS, 0 }; 500 | 501 | boolean 502 | itsstuck(mtmp) 503 | register struct monst *mtmp; 504 | { 505 | if (sticks(youmonst.data) && mtmp==u.ustuck && !u.uswallow) { 506 | pline("%s cannot escape from you!", Monnam(mtmp)); 507 | return(TRUE); 508 | } 509 | return(FALSE); 510 | } 511 | 512 | /* Return values: 513 | * 0: did not move, but can still attack and do other stuff. 514 | * 1: moved, possibly can attack. 515 | * 2: monster died. 516 | * 3: did not move, and can't do anything else either. 517 | */ 518 | int 519 | m_move(mtmp, after) 520 | register struct monst *mtmp; 521 | register int after; 522 | { 523 | register int appr; 524 | xchar gx,gy,nix,niy,chcnt; 525 | int chi; /* could be schar except for stupid Sun-2 compiler */ 526 | boolean likegold=0, likegems=0, likeobjs=0, likemagic=0, conceals=0; 527 | boolean likerock=0, can_tunnel=0; 528 | boolean can_open=0, can_unlock=0, doorbuster=0; 529 | boolean uses_items=0; 530 | struct permonst *ptr; 531 | struct monst *mtoo; 532 | schar mmoved = 0; /* not strictly nec.: chi >= 0 will do */ 533 | long info[9]; 534 | long flag; 535 | int omx = mtmp->mx, omy = mtmp->my; 536 | struct obj *mw_tmp; 537 | 538 | if(mtmp->mtrapped) { 539 | int i = mintrap(mtmp); 540 | if(i >= 2) { newsym(mtmp->mx,mtmp->my); return(2); }/* it died */ 541 | if(i == 1) return(0); /* still in trap, so didn't move */ 542 | } 543 | ptr = mtmp->data; /* mintrap() can change mtmp->data -dlc */ 544 | 545 | if (mtmp->meating) { 546 | mtmp->meating--; 547 | return 3; /* still eating */ 548 | } 549 | if (hides_under(ptr) && OBJ_AT(mtmp->mx, mtmp->my) && rn2(10)) 550 | return 0; /* do not leave hiding place */ 551 | 552 | set_apparxy(mtmp); 553 | /* where does mtmp think you are? */ 554 | /* Not necessary if m_move called from this file, but necessary in 555 | * other calls of m_move (ex. leprechauns dodging) 556 | */ 557 | can_tunnel = tunnels(ptr) && 558 | #ifdef REINCARNATION 559 | !Is_rogue_level(&u.uz) && 560 | #endif 561 | (!needspick(ptr) || m_carrying(mtmp, PICK_AXE) || 562 | (m_carrying(mtmp, DWARVISH_MATTOCK) && !which_armor(mtmp, W_ARMS))); 563 | can_open = !(nohands(ptr) || verysmall(ptr)); 564 | can_unlock = ((can_open && m_carrying(mtmp, SKELETON_KEY)) || 565 | mtmp->iswiz || is_rider(ptr)); 566 | doorbuster = is_giant(ptr); 567 | if(mtmp->wormno) goto not_special; 568 | /* my dog gets special treatment */ 569 | if(mtmp->mtame) { 570 | mmoved = dog_move(mtmp, after); 571 | goto postmov; 572 | } 573 | 574 | /* likewise for shopkeeper */ 575 | if(mtmp->isshk) { 576 | mmoved = shk_move(mtmp); 577 | if(mmoved == -2) return(2); 578 | if(mmoved >= 0) goto postmov; 579 | mmoved = 0; /* follow player outside shop */ 580 | } 581 | 582 | /* and for the guard */ 583 | if(mtmp->isgd) { 584 | mmoved = gd_move(mtmp); 585 | if(mmoved == -2) return(2); 586 | if(mmoved >= 0) goto postmov; 587 | mmoved = 0; 588 | } 589 | 590 | /* and the acquisitive monsters get special treatment */ 591 | if(is_covetous(ptr)) { 592 | xchar tx = STRAT_GOALX(mtmp->mstrategy), 593 | ty = STRAT_GOALY(mtmp->mstrategy); 594 | struct monst *intruder = m_at(tx, ty); 595 | /* 596 | * if there's a monster on the object or in possesion of it, 597 | * attack it. 598 | */ 599 | if((dist2(mtmp->mx, mtmp->my, tx, ty) < 2) && 600 | intruder && (intruder != mtmp)) { 601 | 602 | notonhead = (intruder->mx != tx || intruder->my != ty); 603 | if(mattackm(mtmp, intruder) == 2) return(2); 604 | mmoved = 1; 605 | } else mmoved = 0; 606 | goto postmov; 607 | } 608 | 609 | /* and for the priest */ 610 | if(mtmp->ispriest) { 611 | mmoved = pri_move(mtmp); 612 | if(mmoved == -2) return(2); 613 | if(mmoved >= 0) goto postmov; 614 | mmoved = 0; 615 | } 616 | 617 | #ifdef MAIL 618 | if(ptr == &mons[PM_MAIL_DAEMON]) { 619 | if(flags.soundok && canseemon(mtmp)) 620 | verbalize("I'm late!"); 621 | mongone(mtmp); 622 | return(2); 623 | } 624 | #endif 625 | 626 | /* teleport if that lies in our nature */ 627 | if(ptr == &mons[PM_TENGU] && !rn2(5) && !mtmp->mcan && 628 | !tele_restrict(mtmp)) { 629 | if(mtmp->mhp < 7 || mtmp->mpeaceful || rn2(2)) 630 | rloc(mtmp); 631 | else 632 | mnexto(mtmp); 633 | mmoved = 1; 634 | goto postmov; 635 | } 636 | not_special: 637 | if(u.uswallow && !mtmp->mflee && u.ustuck != mtmp) return(1); 638 | omx = mtmp->mx; 639 | omy = mtmp->my; 640 | gx = mtmp->mux; 641 | gy = mtmp->muy; 642 | appr = mtmp->mflee ? -1 : 1; 643 | if (mtmp->mconf || (u.uswallow && mtmp == u.ustuck)) 644 | appr = 0; 645 | else { 646 | boolean should_see = (couldsee(omx, omy) && 647 | (levl[gx][gy].lit || 648 | !levl[omx][omy].lit) && 649 | (dist2(omx, omy, gx, gy) <= 36)); 650 | 651 | if (!mtmp->mcansee || 652 | (should_see && Invis && !perceives(ptr) && rn2(11)) || 653 | (youmonst.m_ap_type == M_AP_OBJECT && youmonst.mappearance == STRANGE_OBJECT) || u.uundetected || 654 | (youmonst.m_ap_type == M_AP_OBJECT && youmonst.mappearance == GOLD_PIECE && !likes_gold(ptr)) || 655 | (mtmp->mpeaceful && !mtmp->isshk) || /* allow shks to follow */ 656 | ((monsndx(ptr) == PM_STALKER || ptr->mlet == S_BAT || 657 | ptr->mlet == S_LIGHT) && !rn2(3))) 658 | appr = 0; 659 | 660 | if(monsndx(ptr) == PM_LEPRECHAUN && (appr == 1) && 661 | (mtmp->mgold > u.ugold)) 662 | appr = -1; 663 | 664 | if (!should_see && can_track(ptr)) { 665 | register coord *cp; 666 | 667 | cp = gettrack(omx,omy); 668 | if (cp) { 669 | gx = cp->x; 670 | gy = cp->y; 671 | } 672 | } 673 | } 674 | 675 | if ((!mtmp->mpeaceful || !rn2(10)) 676 | #ifdef REINCARNATION 677 | && (!Is_rogue_level(&u.uz)) 678 | #endif 679 | ) { 680 | boolean in_line = lined_up(mtmp) && 681 | (distmin(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy) <= 682 | (throws_rocks(youmonst.data) ? 20 : ACURRSTR/2+1) 683 | ); 684 | 685 | if (appr != 1 || !in_line) { 686 | /* Monsters in combat won't pick stuff up, avoiding the 687 | * situation where you toss arrows at it and it has nothing 688 | * better to do than pick the arrows up. 689 | */ 690 | register int pctload = (curr_mon_load(mtmp) * 100) / 691 | max_mon_load(mtmp); 692 | 693 | /* look for gold or jewels nearby */ 694 | likegold = (likes_gold(ptr) && pctload < 95); 695 | likegems = (likes_gems(ptr) && pctload < 85); 696 | uses_items = (!mindless(ptr) && !is_animal(ptr) 697 | && pctload < 75); 698 | likeobjs = (likes_objs(ptr) && pctload < 75); 699 | likemagic = (likes_magic(ptr) && pctload < 85); 700 | likerock = (throws_rocks(ptr) && pctload < 50 && !In_sokoban(&u.uz)); 701 | conceals = hides_under(ptr); 702 | } 703 | } 704 | 705 | #define SQSRCHRADIUS 5 706 | 707 | { register int minr = SQSRCHRADIUS; /* not too far away */ 708 | register struct obj *otmp; 709 | register int xx, yy; 710 | int oomx, oomy, lmx, lmy; 711 | 712 | /* cut down the search radius if it thinks character is closer. */ 713 | if(distmin(mtmp->mux, mtmp->muy, omx, omy) < SQSRCHRADIUS && 714 | !mtmp->mpeaceful) minr--; 715 | /* guards shouldn't get too distracted */ 716 | if(!mtmp->mpeaceful && is_mercenary(ptr)) minr = 1; 717 | 718 | if((likegold || likegems || likeobjs || likemagic || likerock || conceals) 719 | && (!*in_rooms(omx, omy, SHOPBASE) || (!rn2(25) && !mtmp->isshk))) { 720 | look_for_obj: 721 | oomx = min(COLNO-1, omx+minr); 722 | oomy = min(ROWNO-1, omy+minr); 723 | lmx = max(1, omx-minr); 724 | lmy = max(0, omy-minr); 725 | for(otmp = fobj; otmp; otmp = otmp->nobj) { 726 | /* monsters may pick rocks up, but won't go out of their way 727 | to grab them; this might hamper sling wielders, but it cuts 728 | down on move overhead by filtering out most common item */ 729 | if (otmp->otyp == ROCK) continue; 730 | xx = otmp->ox; 731 | yy = otmp->oy; 732 | /* Nymphs take everything. Most other creatures should not 733 | * pick up corpses except as a special case like in 734 | * searches_for_item(). We need to do this check in 735 | * mpickstuff() as well. 736 | */ 737 | if(xx >= lmx && xx <= oomx && yy >= lmy && yy <= oomy) { 738 | /* don't get stuck circling around an object that's underneath 739 | an immobile or hidden monster; paralysis victims excluded */ 740 | if ((mtoo = m_at(xx,yy)) != 0 && 741 | (mtoo->msleeping || mtoo->mundetected || 742 | (mtoo->mappearance && !mtoo->iswiz) || 743 | !mtoo->data->mmove)) continue; 744 | 745 | if(((likegold && otmp->oclass == GOLD_CLASS) || 746 | (likeobjs && index(practical, otmp->oclass) && 747 | (otmp->otyp != CORPSE || (ptr->mlet == S_NYMPH 748 | && !is_rider(&mons[otmp->corpsenm])))) || 749 | (likemagic && index(magical, otmp->oclass)) || 750 | (uses_items && searches_for_item(mtmp, otmp)) || 751 | (likerock && otmp->otyp == BOULDER) || 752 | (likegems && otmp->oclass == GEM_CLASS && 753 | objects[otmp->otyp].oc_material != MINERAL) || 754 | (conceals && !cansee(otmp->ox,otmp->oy)) || 755 | (ptr == &mons[PM_GELATINOUS_CUBE] && 756 | !index(indigestion, otmp->oclass) && 757 | !(otmp->otyp == CORPSE && 758 | touch_petrifies(&mons[otmp->corpsenm]))) 759 | ) && touch_artifact(otmp,mtmp)) { 760 | if(can_carry(mtmp,otmp) && 761 | (throws_rocks(ptr) || 762 | !sobj_at(BOULDER,xx,yy)) && 763 | (!is_unicorn(ptr) || 764 | objects[otmp->otyp].oc_material == GEMSTONE) && 765 | /* Don't get stuck circling an Elbereth */ 766 | !(onscary(xx, yy, mtmp))) { 767 | minr = distmin(omx,omy,xx,yy); 768 | oomx = min(COLNO-1, omx+minr); 769 | oomy = min(ROWNO-1, omy+minr); 770 | lmx = max(1, omx-minr); 771 | lmy = max(0, omy-minr); 772 | gx = otmp->ox; 773 | gy = otmp->oy; 774 | if (gx == omx && gy == omy) { 775 | mmoved = 3; /* actually unnecessary */ 776 | goto postmov; 777 | } 778 | } 779 | } 780 | } 781 | } 782 | } else if(likegold) { 783 | /* don't try to pick up anything else, but use the same loop */ 784 | uses_items = 0; 785 | likegems = likeobjs = likemagic = likerock = conceals = 0; 786 | goto look_for_obj; 787 | } 788 | 789 | if(minr < SQSRCHRADIUS && appr == -1) { 790 | if(distmin(omx,omy,mtmp->mux,mtmp->muy) <= 3) { 791 | gx = mtmp->mux; 792 | gy = mtmp->muy; 793 | } else 794 | appr = 1; 795 | } 796 | } 797 | 798 | if (can_tunnel && needspick(ptr) && 799 | (mw_tmp = MON_WEP(mtmp)) != 0 && !is_pick(mw_tmp) && 800 | mw_tmp->cursed && mtmp->weapon_check == NO_WEAPON_WANTED) 801 | can_tunnel = FALSE; 802 | 803 | nix = omx; 804 | niy = omy; 805 | flag = 0L; 806 | if (mtmp->mpeaceful && (!Conflict || resist(mtmp, RING_CLASS, 0, 0))) 807 | flag |= (ALLOW_SANCT | ALLOW_SSM); 808 | else flag |= ALLOW_U; 809 | if (is_minion(ptr) || is_rider(ptr)) flag |= ALLOW_SANCT; 810 | if (is_unicorn(ptr)) flag |= NOTONL; 811 | if (passes_walls(ptr)) flag |= (ALLOW_WALL | ALLOW_ROCK); 812 | if (can_tunnel) flag |= ALLOW_DIG; 813 | if (is_human(ptr) || ptr == &mons[PM_MINOTAUR]) flag |= ALLOW_SSM; 814 | if (is_undead(ptr) && ptr->mlet != S_GHOST) flag |= NOGARLIC; 815 | if (throws_rocks(ptr)) flag |= ALLOW_ROCK; 816 | if (can_open) flag |= OPENDOOR; 817 | if (can_unlock) flag |= UNLOCKDOOR; 818 | if (doorbuster) flag |= BUSTDOOR; 819 | { 820 | register int i, j, nx, ny, nearer; 821 | int jcnt, cnt; 822 | int ndist, nidist; 823 | register coord *mtrk; 824 | coord poss[9]; 825 | 826 | cnt = mfndpos(mtmp, poss, info, flag); 827 | chcnt = 0; 828 | jcnt = min(MTSZ, cnt-1); 829 | chi = -1; 830 | nidist = dist2(nix,niy,gx,gy); 831 | /* allow monsters be shortsighted on some levels for balance */ 832 | if(!mtmp->mpeaceful && level.flags.shortsighted && 833 | nidist > (couldsee(nix,niy) ? 144 : 36) && appr == 1) appr = 0; 834 | 835 | for(i=0; i < cnt; i++) { 836 | nx = poss[i].x; 837 | ny = poss[i].y; 838 | 839 | if (appr != 0) { 840 | mtrk = &mtmp->mtrack[0]; 841 | for(j=0; j < jcnt; mtrk++, j++) 842 | if(nx == mtrk->x && ny == mtrk->y) 843 | if(rn2(4*(cnt-j))) 844 | goto nxti; 845 | } 846 | 847 | nearer = ((ndist = dist2(nx,ny,gx,gy)) < nidist); 848 | 849 | if((appr == 1 && nearer) || (appr == -1 && !nearer) || 850 | (!appr && !rn2(++chcnt)) || !mmoved) { 851 | nix = nx; 852 | niy = ny; 853 | nidist = ndist; 854 | chi = i; 855 | mmoved = 1; 856 | } 857 | nxti: ; 858 | } 859 | } 860 | 861 | if(mmoved) { 862 | register int j; 863 | 864 | if (mmoved==1 && (u.ux != nix || u.uy != niy) && itsstuck(mtmp)) 865 | return(3); 866 | 867 | if(IS_ROCK(levl[nix][niy].typ) && may_dig(nix,niy) && 868 | mmoved==1 && can_tunnel && needspick(ptr) && 869 | (!(mw_tmp = MON_WEP(mtmp)) || !is_pick(mw_tmp))) { 870 | mtmp->weapon_check = NEED_PICK_AXE; 871 | if (mon_wield_item(mtmp)) 872 | return(3); 873 | } 874 | /* If ALLOW_U is set, either it's trying to attack you, or it 875 | * thinks it is. In either case, attack this spot in preference to 876 | * all others. 877 | */ 878 | /* Actually, this whole section of code doesn't work as you'd expect. 879 | * Most attacks are handled in dochug(). It calls distfleeck(), which 880 | * among other things sets nearby if the monster is near you--and if 881 | * nearby is set, we never call m_move unless it is a special case 882 | * (confused, stun, etc.) The effect is that this ALLOW_U (and 883 | * mfndpos) has no effect for normal attacks, though it lets a confused 884 | * monster attack you by accident. 885 | */ 886 | if(info[chi] & ALLOW_U) { 887 | nix = mtmp->mux; 888 | niy = mtmp->muy; 889 | } 890 | if (nix == u.ux && niy == u.uy) { 891 | mtmp->mux = u.ux; 892 | mtmp->muy = u.uy; 893 | return(0); 894 | } 895 | /* The monster may attack another based on 1 of 2 conditions: 896 | * 1 - It may be confused. 897 | * 2 - It may mistake the monster for your (displaced) image. 898 | * Pets get taken care of above and shouldn't reach this code. 899 | * Conflict gets handled even farther away (movemon()). 900 | */ 901 | if((info[chi] & ALLOW_M) || 902 | (nix == mtmp->mux && niy == mtmp->muy)) { 903 | struct monst *mtmp2; 904 | int mstatus; 905 | mtmp2 = m_at(nix,niy); 906 | 907 | notonhead = mtmp2 && (nix != mtmp2->mx || niy != mtmp2->my); 908 | /* note: mstatus returns 0 if mtmp2 is nonexistent */ 909 | mstatus = mattackm(mtmp, mtmp2); 910 | 911 | if (mstatus & MM_AGR_DIED) /* aggressor died */ 912 | return 2; 913 | 914 | if ((mstatus & MM_HIT) && !(mstatus & MM_DEF_DIED) && 915 | rn2(4) && mtmp2->movement >= NORMAL_SPEED) { 916 | mtmp2->movement -= NORMAL_SPEED; 917 | notonhead = 0; 918 | mstatus = mattackm(mtmp2, mtmp); /* return attack */ 919 | if (mstatus & MM_DEF_DIED) 920 | return 2; 921 | } 922 | return 3; 923 | } 924 | 925 | if (!m_in_out_region(mtmp,nix,niy)) 926 | return 3; 927 | remove_monster(omx, omy); 928 | place_monster(mtmp, nix, niy); 929 | for(j = MTSZ-1; j > 0; j--) 930 | mtmp->mtrack[j] = mtmp->mtrack[j-1]; 931 | mtmp->mtrack[0].x = omx; 932 | mtmp->mtrack[0].y = omy; 933 | /* Place a segment at the old position. */ 934 | if (mtmp->wormno) worm_move(mtmp); 935 | } else { 936 | if(is_unicorn(ptr) && rn2(2) && !tele_restrict(mtmp)) { 937 | rloc(mtmp); 938 | return(1); 939 | } 940 | if(mtmp->wormno) worm_nomove(mtmp); 941 | } 942 | postmov: 943 | if(mmoved == 1 || mmoved == 3) { 944 | boolean canseeit = cansee(mtmp->mx, mtmp->my); 945 | 946 | if(mmoved == 1) { 947 | newsym(omx,omy); /* update the old position */ 948 | if (mintrap(mtmp) >= 2) { 949 | if(mtmp->mx) newsym(mtmp->mx,mtmp->my); 950 | return(2); /* it died */ 951 | } 952 | ptr = mtmp->data; 953 | 954 | /* open a door, or crash through it, if you can */ 955 | if(IS_DOOR(levl[mtmp->mx][mtmp->my].typ) 956 | && !passes_walls(ptr) /* doesn't need to open doors */ 957 | && !can_tunnel /* taken care of below */ 958 | ) { 959 | struct rm *here = &levl[mtmp->mx][mtmp->my]; 960 | boolean btrapped = (here->doormask & D_TRAPPED); 961 | 962 | if(here->doormask & (D_LOCKED|D_CLOSED) && amorphous(ptr)) { 963 | if (flags.verbose && canseemon(mtmp)) 964 | pline("%s %ss under the door.", Monnam(mtmp), 965 | (ptr == &mons[PM_FOG_CLOUD] || 966 | ptr == &mons[PM_YELLOW_LIGHT]) 967 | ? "flow" : "ooze"); 968 | } else if(here->doormask & D_LOCKED && can_unlock) { 969 | if(btrapped) { 970 | here->doormask = D_NODOOR; 971 | newsym(mtmp->mx, mtmp->my); 972 | unblock_point(mtmp->mx,mtmp->my); /* vision */ 973 | if(mb_trapped(mtmp)) return(2); 974 | } else { 975 | if (flags.verbose) { 976 | if (canseeit) 977 | You("see a door unlock and open."); 978 | else if (flags.soundok) 979 | You_hear("a door unlock and open."); 980 | } 981 | here->doormask = D_ISOPEN; 982 | /* newsym(mtmp->mx, mtmp->my); */ 983 | unblock_point(mtmp->mx,mtmp->my); /* vision */ 984 | } 985 | } else if (here->doormask == D_CLOSED && can_open) { 986 | if(btrapped) { 987 | here->doormask = D_NODOOR; 988 | newsym(mtmp->mx, mtmp->my); 989 | unblock_point(mtmp->mx,mtmp->my); /* vision */ 990 | if(mb_trapped(mtmp)) return(2); 991 | } else { 992 | if (flags.verbose) { 993 | if (canseeit) 994 | You("see a door open."); 995 | else if (flags.soundok) 996 | You_hear("a door open."); 997 | } 998 | here->doormask = D_ISOPEN; 999 | /* newsym(mtmp->mx, mtmp->my); */ /* done below */ 1000 | unblock_point(mtmp->mx,mtmp->my); /* vision */ 1001 | } 1002 | } else if (here->doormask & (D_LOCKED|D_CLOSED)) { 1003 | /* mfndpos guarantees this must be a doorbuster */ 1004 | if(btrapped) { 1005 | here->doormask = D_NODOOR; 1006 | newsym(mtmp->mx, mtmp->my); 1007 | unblock_point(mtmp->mx,mtmp->my); /* vision */ 1008 | if(mb_trapped(mtmp)) return(2); 1009 | } else { 1010 | if (flags.verbose) { 1011 | if (canseeit) 1012 | You("see a door crash open."); 1013 | else if (flags.soundok) 1014 | You_hear("a door crash open."); 1015 | } 1016 | if (here->doormask & D_LOCKED && !rn2(2)) 1017 | here->doormask = D_NODOOR; 1018 | else here->doormask = D_BROKEN; 1019 | /* newsym(mtmp->mx, mtmp->my); */ /* done below */ 1020 | unblock_point(mtmp->mx,mtmp->my); /* vision */ 1021 | } 1022 | /* if it's a shop door, schedule repair */ 1023 | if (*in_rooms(mtmp->mx, mtmp->my, SHOPBASE)) 1024 | add_damage(mtmp->mx, mtmp->my, 0L); 1025 | } 1026 | } 1027 | 1028 | /* possibly dig */ 1029 | if (can_tunnel && mdig_tunnel(mtmp)) 1030 | return(2); /* mon died (position already updated) */ 1031 | 1032 | /* set also in domove(), hack.c */ 1033 | if (u.uswallow && mtmp == u.ustuck && 1034 | (mtmp->mx != omx || mtmp->my != omy)) { 1035 | /* If the monster moved, then update */ 1036 | u.ux0 = u.ux; 1037 | u.uy0 = u.uy; 1038 | u.ux = mtmp->mx; 1039 | u.uy = mtmp->my; 1040 | swallowed(0); 1041 | } else 1042 | newsym(mtmp->mx,mtmp->my); 1043 | } 1044 | if(OBJ_AT(mtmp->mx, mtmp->my) && mtmp->mcanmove) { 1045 | /* Maybe a rock mole just ate some metal object */ 1046 | if (metallivorous(ptr)) { 1047 | if (meatgold(mtmp) == 2) return 2; /* it died */ 1048 | } 1049 | 1050 | if(g_at(mtmp->mx,mtmp->my) && likegold) mpickgold(mtmp); 1051 | 1052 | /* Maybe a cube ate just about anything */ 1053 | if (ptr == &mons[PM_GELATINOUS_CUBE]) { 1054 | if (meatobj(mtmp) == 2) return 2; /* it died */ 1055 | } 1056 | 1057 | if(!*in_rooms(mtmp->mx, mtmp->my, SHOPBASE) || !rn2(25)) { 1058 | boolean picked = FALSE; 1059 | 1060 | if(likeobjs) picked |= mpickstuff(mtmp, practical); 1061 | if(likemagic) picked |= mpickstuff(mtmp, magical); 1062 | if(likerock) picked |= mpickstuff(mtmp, boulder_class); 1063 | if(likegems) picked |= mpickstuff(mtmp, gem_class); 1064 | if(uses_items) picked |= mpickstuff(mtmp, (char *)0); 1065 | if(picked) mmoved = 3; 1066 | } 1067 | 1068 | if(mtmp->minvis) { 1069 | newsym(mtmp->mx, mtmp->my); 1070 | if (mtmp->wormno) see_wsegs(mtmp); 1071 | } 1072 | } 1073 | 1074 | if(hides_under(ptr) || ptr->mlet == S_EEL) { 1075 | /* Always set--or reset--mundetected if it's already hidden 1076 | (just in case the object it was hiding under went away); 1077 | usually set mundetected unless monster can't move. */ 1078 | if (mtmp->mundetected || 1079 | (mtmp->mcanmove && !mtmp->msleeping && rn2(5))) 1080 | mtmp->mundetected = (ptr->mlet != S_EEL) ? 1081 | OBJ_AT(mtmp->mx, mtmp->my) : 1082 | (is_pool(mtmp->mx, mtmp->my) && !Is_waterlevel(&u.uz)); 1083 | newsym(mtmp->mx, mtmp->my); 1084 | } 1085 | } 1086 | return(mmoved); 1087 | } 1088 | 1089 | #endif /* OVL0 */ 1090 | #ifdef OVL2 1091 | 1092 | boolean 1093 | closed_door(x, y) 1094 | register int x, y; 1095 | { 1096 | return((boolean)(IS_DOOR(levl[x][y].typ) && 1097 | (levl[x][y].doormask & (D_LOCKED | D_CLOSED)))); 1098 | } 1099 | 1100 | boolean 1101 | accessible(x, y) 1102 | register int x, y; 1103 | { 1104 | return((boolean)(ACCESSIBLE(levl[x][y].typ) && !closed_door(x, y))); 1105 | } 1106 | 1107 | #endif /* OVL2 */ 1108 | #ifdef OVL0 1109 | 1110 | /* decide where the monster thinks you are standing */ 1111 | void 1112 | set_apparxy(mtmp) 1113 | register struct monst *mtmp; 1114 | { 1115 | boolean notseen, gotu; 1116 | register int disp, mx = mtmp->mux, my = mtmp->muy; 1117 | 1118 | /* 1119 | * do cheapest and/or most likely tests first 1120 | */ 1121 | 1122 | /* pet knows your smell; grabber still has hold of you */ 1123 | if (mtmp->mtame || mtmp == u.ustuck) goto found_you; 1124 | 1125 | /* monsters which know where you are don't suddenly forget, 1126 | if you haven't moved away */ 1127 | if (mx == u.ux && my == u.uy) goto found_you; 1128 | 1129 | notseen = (!mtmp->mcansee || (Invis && !perceives(mtmp->data))); 1130 | /* add cases as required. eg. Displacement ... */ 1131 | disp = ((notseen || Underwater) ? 1 : 1132 | Displaced ? (couldsee(mx, my) ? 2 : 1) : 0); 1133 | if (!disp) goto found_you; 1134 | 1135 | /* without something like the following, invis. and displ. 1136 | are too powerful */ 1137 | gotu = notseen ? !rn2(3) : Displaced ? !rn2(4) : FALSE; 1138 | 1139 | #if 0 /* this never worked as intended & isn't needed anyway */ 1140 | /* If invis but not displaced, staying around gets you 'discovered' */ 1141 | gotu |= (!Displaced && u.dx == 0 && u.dy == 0); 1142 | #endif 1143 | 1144 | if (!gotu) { 1145 | register int try_cnt = 0; 1146 | do { 1147 | if (++try_cnt > 200) goto found_you; /* punt */ 1148 | mx = u.ux - disp + rn2(2*disp+1); 1149 | my = u.uy - disp + rn2(2*disp+1); 1150 | } while (!isok(mx,my) 1151 | || (disp != 2 && mx == mtmp->mx && my == mtmp->my) 1152 | || ((mx != u.ux || my != u.uy) && 1153 | !passes_walls(mtmp->data) && 1154 | (!ACCESSIBLE(levl[mx][my].typ) || 1155 | (closed_door(mx, my) && !can_ooze(mtmp))))); 1156 | } else { 1157 | found_you: 1158 | mx = u.ux; 1159 | my = u.uy; 1160 | } 1161 | 1162 | mtmp->mux = mx; 1163 | mtmp->muy = my; 1164 | } 1165 | 1166 | boolean 1167 | can_ooze(mtmp) 1168 | struct monst *mtmp; 1169 | { 1170 | struct obj *chain, *obj; 1171 | 1172 | if (!amorphous(mtmp->data)) return FALSE; 1173 | if (mtmp == &youmonst) { 1174 | if (u.ugold > 100L) return FALSE; 1175 | chain = invent; 1176 | } else { 1177 | if (mtmp->mgold > 100L) return FALSE; 1178 | chain = mtmp->minvent; 1179 | } 1180 | for (obj = chain; obj; obj = obj->nobj) { 1181 | int typ = obj->otyp; 1182 | 1183 | if (obj->oclass != GEM_CLASS && 1184 | !(typ >= ARROW && typ <= BOOMERANG) && 1185 | !(typ >= DAGGER && typ <= CRYSKNIFE) && 1186 | typ != SLING && 1187 | !is_cloak(obj) && typ != FEDORA && 1188 | !is_gloves(obj) && typ != LEATHER_JACKET && 1189 | #ifdef TOURIST 1190 | typ != CREDIT_CARD && !is_shirt(obj) && 1191 | #endif 1192 | !(typ == CORPSE && verysmall(&mons[obj->corpsenm])) && 1193 | typ != FORTUNE_COOKIE && typ != CANDY_BAR && 1194 | typ != PANCAKE && typ != LEMBAS_WAFER && 1195 | typ != LUMP_OF_ROYAL_JELLY && 1196 | obj->oclass != AMULET_CLASS && 1197 | obj->oclass != RING_CLASS && 1198 | #ifdef WIZARD 1199 | obj->oclass != VENOM_CLASS && 1200 | #endif 1201 | typ != SACK && typ != BAG_OF_HOLDING && 1202 | typ != BAG_OF_TRICKS && !Is_candle(obj) && 1203 | typ != OILSKIN_SACK && typ != LEASH && 1204 | typ != STETHOSCOPE && typ != BLINDFOLD && typ != TOWEL && 1205 | typ != TIN_WHISTLE && typ != MAGIC_WHISTLE && 1206 | typ != MAGIC_MARKER && typ != TIN_OPENER && 1207 | typ != SKELETON_KEY && typ != LOCK_PICK 1208 | ) return FALSE; 1209 | if (Is_container(obj) && obj->cobj) return FALSE; 1210 | 1211 | } 1212 | return TRUE; 1213 | } 1214 | 1215 | #endif /* OVL0 */ 1216 | 1217 | /*monmove.c*/