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