dumplog message history groundwork
[aNetHack.git] / src / dogmove.c
blob321deb0afd91b255dd7c27c4d9170ab7c47577b9
1 /* NetHack 3.6 dogmove.c $NHDT-Date: 1463704424 2016/05/20 00:33:44 $ $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.60 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
5 #include "hack.h"
7 #include "mfndpos.h"
9 extern boolean notonhead;
11 STATIC_DCL boolean FDECL(dog_hunger, (struct monst *, struct edog *));
12 STATIC_DCL int FDECL(dog_invent, (struct monst *, struct edog *, int));
13 STATIC_DCL int FDECL(dog_goal, (struct monst *, struct edog *, int, int, int));
14 STATIC_DCL struct monst *FDECL(find_targ, (struct monst *, int, int, int));
15 STATIC_OVL int FDECL(find_friends, (struct monst *, struct monst *, int));
16 STATIC_DCL struct monst *FDECL(best_target, (struct monst *));
17 STATIC_DCL long FDECL(score_targ, (struct monst *, struct monst *));
18 STATIC_DCL boolean FDECL(can_reach_location, (struct monst *, XCHAR_P,
19 XCHAR_P, XCHAR_P, XCHAR_P));
20 STATIC_DCL boolean FDECL(could_reach_item, (struct monst *, XCHAR_P, XCHAR_P));
21 STATIC_DCL void FDECL(quickmimic, (struct monst *));
23 /* pick a carried item for pet to drop */
24 struct obj *
25 droppables(mon)
26 struct monst *mon;
28 struct obj *obj, *wep, dummy, *pickaxe, *unihorn, *key;
30 dummy = zeroobj;
31 dummy.otyp = GOLD_PIECE; /* not STRANGE_OBJECT or tools of interest */
32 dummy.oartifact = 1; /* so real artifact won't override "don't keep it" */
33 pickaxe = unihorn = key = (struct obj *) 0;
34 wep = MON_WEP(mon);
36 if (is_animal(mon->data) || mindless(mon->data)) {
37 /* won't hang on to any objects of these types */
38 pickaxe = unihorn = key = &dummy; /* act as if already have them */
39 } else {
40 /* don't hang on to pick-axe if can't use one or don't need one */
41 if (!tunnels(mon->data) || !needspick(mon->data))
42 pickaxe = &dummy;
43 /* don't hang on to key if can't open doors */
44 if (nohands(mon->data) || verysmall(mon->data))
45 key = &dummy;
47 if (wep) {
48 if (is_pick(wep))
49 pickaxe = wep;
50 if (wep->otyp == UNICORN_HORN)
51 unihorn = wep;
52 /* don't need any wielded check for keys... */
55 for (obj = mon->minvent; obj; obj = obj->nobj) {
56 switch (obj->otyp) {
57 case DWARVISH_MATTOCK:
58 /* reject mattock if couldn't wield it */
59 if (which_armor(mon, W_ARMS))
60 break;
61 /* keep mattock in preference to pick unless pick is already
62 wielded or is an artifact and mattock isn't */
63 if (pickaxe && pickaxe->otyp == PICK_AXE && pickaxe != wep
64 && (!pickaxe->oartifact || obj->oartifact))
65 return pickaxe; /* drop the one we earlier decided to keep */
66 /*FALLTHRU*/
67 case PICK_AXE:
68 if (!pickaxe || (obj->oartifact && !pickaxe->oartifact)) {
69 if (pickaxe)
70 return pickaxe;
71 pickaxe = obj; /* keep this digging tool */
72 continue;
74 break;
76 case UNICORN_HORN:
77 /* reject cursed unicorn horns */
78 if (obj->cursed)
79 break;
80 /* keep artifact unihorn in preference to ordinary one */
81 if (!unihorn || (obj->oartifact && !unihorn->oartifact)) {
82 if (unihorn)
83 return unihorn;
84 unihorn = obj; /* keep this unicorn horn */
85 continue;
87 break;
89 case SKELETON_KEY:
90 /* keep key in preference to lock-pick */
91 if (key && key->otyp == LOCK_PICK
92 && (!key->oartifact || obj->oartifact))
93 return key; /* drop the one we earlier decided to keep */
94 /*FALLTHRU*/
95 case LOCK_PICK:
96 /* keep lock-pick in preference to credit card */
97 if (key && key->otyp == CREDIT_CARD
98 && (!key->oartifact || obj->oartifact))
99 return key;
100 /*FALLTHRU*/
101 case CREDIT_CARD:
102 if (!key || (obj->oartifact && !key->oartifact)) {
103 if (key)
104 return key;
105 key = obj; /* keep this unlocking tool */
106 continue;
108 break;
110 default:
111 break;
114 if (!obj->owornmask && obj != wep)
115 return obj;
118 return (struct obj *) 0; /* don't drop anything */
121 static NEARDATA const char nofetch[] = { BALL_CLASS, CHAIN_CLASS, ROCK_CLASS,
122 0 };
124 STATIC_VAR xchar gtyp, gx, gy; /* type and position of dog's current goal */
126 STATIC_PTR void FDECL(wantdoor, (int, int, genericptr_t));
128 boolean
129 cursed_object_at(x, y)
130 int x, y;
132 struct obj *otmp;
134 for (otmp = level.objects[x][y]; otmp; otmp = otmp->nexthere)
135 if (otmp->cursed)
136 return TRUE;
137 return FALSE;
141 dog_nutrition(mtmp, obj)
142 struct monst *mtmp;
143 struct obj *obj;
145 int nutrit;
148 * It is arbitrary that the pet takes the same length of time to eat
149 * as a human, but gets more nutritional value.
151 if (obj->oclass == FOOD_CLASS) {
152 if (obj->otyp == CORPSE) {
153 mtmp->meating = 3 + (mons[obj->corpsenm].cwt >> 6);
154 nutrit = mons[obj->corpsenm].cnutrit;
155 } else {
156 mtmp->meating = objects[obj->otyp].oc_delay;
157 nutrit = objects[obj->otyp].oc_nutrition;
159 switch (mtmp->data->msize) {
160 case MZ_TINY:
161 nutrit *= 8;
162 break;
163 case MZ_SMALL:
164 nutrit *= 6;
165 break;
166 default:
167 case MZ_MEDIUM:
168 nutrit *= 5;
169 break;
170 case MZ_LARGE:
171 nutrit *= 4;
172 break;
173 case MZ_HUGE:
174 nutrit *= 3;
175 break;
176 case MZ_GIGANTIC:
177 nutrit *= 2;
178 break;
180 if (obj->oeaten) {
181 mtmp->meating = eaten_stat(mtmp->meating, obj);
182 nutrit = eaten_stat(nutrit, obj);
184 } else if (obj->oclass == COIN_CLASS) {
185 mtmp->meating = (int) (obj->quan / 2000) + 1;
186 if (mtmp->meating < 0)
187 mtmp->meating = 1;
188 nutrit = (int) (obj->quan / 20);
189 if (nutrit < 0)
190 nutrit = 0;
191 } else {
192 /* Unusual pet such as gelatinous cube eating odd stuff.
193 * meating made consistent with wild monsters in mon.c.
194 * nutrit made consistent with polymorphed player nutrit in
195 * eat.c. (This also applies to pets eating gold.)
197 mtmp->meating = obj->owt / 20 + 1;
198 nutrit = 5 * objects[obj->otyp].oc_nutrition;
200 return nutrit;
203 /* returns 2 if pet dies, otherwise 1 */
205 dog_eat(mtmp, obj, x, y, devour)
206 register struct monst *mtmp;
207 register struct obj *obj; /* if unpaid, then thrown or kicked by hero */
208 int x, y; /* dog's starting location, might be different from current */
209 boolean devour;
211 register struct edog *edog = EDOG(mtmp);
212 boolean poly, grow, heal, slimer, deadmimic;
213 int nutrit;
214 long oprice;
215 char objnambuf[BUFSZ];
217 objnambuf[0] = '\0';
218 if (edog->hungrytime < monstermoves)
219 edog->hungrytime = monstermoves;
220 nutrit = dog_nutrition(mtmp, obj);
222 deadmimic = (obj->otyp == CORPSE && (obj->corpsenm == PM_SMALL_MIMIC
223 || obj->corpsenm == PM_LARGE_MIMIC
224 || obj->corpsenm == PM_GIANT_MIMIC));
225 slimer = (obj->otyp == CORPSE && obj->corpsenm == PM_GREEN_SLIME);
226 poly = polyfodder(obj);
227 grow = mlevelgain(obj);
228 heal = mhealup(obj);
230 if (devour) {
231 if (mtmp->meating > 1)
232 mtmp->meating /= 2;
233 if (nutrit > 1)
234 nutrit = (nutrit * 3) / 4;
236 edog->hungrytime += nutrit;
237 mtmp->mconf = 0;
238 if (edog->mhpmax_penalty) {
239 /* no longer starving */
240 mtmp->mhpmax += edog->mhpmax_penalty;
241 edog->mhpmax_penalty = 0;
243 if (mtmp->mflee && mtmp->mfleetim > 1)
244 mtmp->mfleetim /= 2;
245 if (mtmp->mtame < 20)
246 mtmp->mtame++;
247 if (x != mtmp->mx || y != mtmp->my) { /* moved & ate on same turn */
248 newsym(x, y);
249 newsym(mtmp->mx, mtmp->my);
252 /* food items are eaten one at a time; entire stack for other stuff */
253 if (obj->quan > 1L && obj->oclass == FOOD_CLASS)
254 obj = splitobj(obj, 1L);
255 if (obj->unpaid)
256 iflags.suppress_price++;
257 if (is_pool(x, y) && !Underwater) {
258 /* Don't print obj */
259 /* TODO: Reveal presence of sea monster (especially sharks) */
260 } else {
261 /* food is at monster's current location, <mx,my>;
262 <x,y> was monster's location at start of this turn;
263 they might be the same but will be different when
264 the monster is moving+eating on same turn */
265 boolean seeobj = cansee(mtmp->mx, mtmp->my),
266 sawpet = cansee(x, y) && mon_visible(mtmp);
268 /* Observe the action if either the food location or the pet
269 itself is in view. When pet which was in view moves to an
270 unseen spot to eat the food there, avoid referring to that
271 pet as "it". However, we want "it" if invisible/unsensed
272 pet eats visible food. */
273 if (sawpet || (seeobj && canspotmon(mtmp))) {
274 if (tunnels(mtmp->data))
275 pline("%s digs in.", noit_Monnam(mtmp));
276 else
277 pline("%s %s %s.", noit_Monnam(mtmp),
278 devour ? "devours" : "eats", distant_name(obj, doname));
279 } else if (seeobj)
280 pline("It %s %s.", devour ? "devours" : "eats",
281 distant_name(obj, doname));
283 if (obj->unpaid) {
284 Strcpy(objnambuf, xname(obj));
285 iflags.suppress_price--;
287 /* It's a reward if it's DOGFOOD and the player dropped/threw it.
288 We know the player had it if invlet is set. -dlc */
289 if (dogfood(mtmp, obj) == DOGFOOD && obj->invlet)
290 #ifdef LINT
291 edog->apport = 0;
292 #else
293 edog->apport += (int) (200L / ((long) edog->dropdist + monstermoves
294 - edog->droptime));
295 #endif
296 if (mtmp->data == &mons[PM_RUST_MONSTER] && obj->oerodeproof) {
297 /* The object's rustproofing is gone now */
298 if (obj->unpaid)
299 costly_alteration(obj, COST_DEGRD);
300 obj->oerodeproof = 0;
301 mtmp->mstun = 1;
302 if (canseemon(mtmp) && flags.verbose) {
303 pline("%s spits %s out in disgust!", Monnam(mtmp),
304 distant_name(obj, doname));
306 } else if (obj == uball) {
307 unpunish();
308 delobj(obj); /* we assume this can't be unpaid */
309 } else if (obj == uchain) {
310 unpunish();
311 } else {
312 if (obj->unpaid) {
313 /* edible item owned by shop has been thrown or kicked
314 by hero and caught by tame or food-tameable monst */
315 oprice = unpaid_cost(obj, TRUE);
316 pline("That %s will cost you %ld %s.", objnambuf, oprice,
317 currency(oprice));
318 /* delobj->obfree will handle actual shop billing update */
320 delobj(obj);
323 #if 0 /* pet is eating, so slime recovery is not feasible... */
324 /* turning into slime might be cureable */
325 if (slimer && munslime(mtmp, FALSE)) {
326 /* but the cure (fire directed at self) might be fatal */
327 if (mtmp->mhp < 1)
328 return 2;
329 slimer = FALSE; /* sliming is avoided, skip polymorph */
331 #endif
333 if (poly || slimer) {
334 struct permonst *ptr = slimer ? &mons[PM_GREEN_SLIME] : 0;
336 (void) newcham(mtmp, ptr, FALSE, cansee(mtmp->mx, mtmp->my));
339 /* limit "instant" growth to prevent potential abuse */
340 if (grow && (int) mtmp->m_lev < (int) mtmp->data->mlevel + 15) {
341 if (!grow_up(mtmp, (struct monst *) 0))
342 return 2;
344 if (heal)
345 mtmp->mhp = mtmp->mhpmax;
346 if (deadmimic)
347 quickmimic(mtmp);
348 return 1;
351 /* hunger effects -- returns TRUE on starvation */
352 STATIC_OVL boolean
353 dog_hunger(mtmp, edog)
354 struct monst *mtmp;
355 struct edog *edog;
357 if (monstermoves > edog->hungrytime + 500) {
358 if (!carnivorous(mtmp->data) && !herbivorous(mtmp->data)) {
359 edog->hungrytime = monstermoves + 500;
360 /* but not too high; it might polymorph */
361 } else if (!edog->mhpmax_penalty) {
362 /* starving pets are limited in healing */
363 int newmhpmax = mtmp->mhpmax / 3;
364 mtmp->mconf = 1;
365 edog->mhpmax_penalty = mtmp->mhpmax - newmhpmax;
366 mtmp->mhpmax = newmhpmax;
367 if (mtmp->mhp > mtmp->mhpmax)
368 mtmp->mhp = mtmp->mhpmax;
369 if (mtmp->mhp < 1)
370 goto dog_died;
371 if (cansee(mtmp->mx, mtmp->my))
372 pline("%s is confused from hunger.", Monnam(mtmp));
373 else if (couldsee(mtmp->mx, mtmp->my))
374 beg(mtmp);
375 else
376 You_feel("worried about %s.", y_monnam(mtmp));
377 stop_occupation();
378 } else if (monstermoves > edog->hungrytime + 750 || mtmp->mhp < 1) {
379 dog_died:
380 if (mtmp->mleashed && mtmp != u.usteed)
381 Your("leash goes slack.");
382 else if (cansee(mtmp->mx, mtmp->my))
383 pline("%s starves.", Monnam(mtmp));
384 else
385 You_feel("%s for a moment.",
386 Hallucination ? "bummed" : "sad");
387 mondied(mtmp);
388 return TRUE;
391 return FALSE;
394 /* do something with object (drop, pick up, eat) at current position
395 * returns 1 if object eaten (since that counts as dog's move), 2 if died
397 STATIC_OVL int
398 dog_invent(mtmp, edog, udist)
399 register struct monst *mtmp;
400 register struct edog *edog;
401 int udist;
403 register int omx, omy, carryamt = 0;
404 struct obj *obj, *otmp;
406 if (mtmp->msleeping || !mtmp->mcanmove)
407 return 0;
409 omx = mtmp->mx;
410 omy = mtmp->my;
412 /* If we are carrying something then we drop it (perhaps near @).
413 * Note: if apport == 1 then our behaviour is independent of udist.
414 * Use udist+1 so steed won't cause divide by zero.
416 if (droppables(mtmp)) {
417 if (!rn2(udist + 1) || !rn2(edog->apport))
418 if (rn2(10) < edog->apport) {
419 relobj(mtmp, (int) mtmp->minvis, TRUE);
420 if (edog->apport > 1)
421 edog->apport--;
422 edog->dropdist = udist; /* hpscdi!jon */
423 edog->droptime = monstermoves;
425 } else {
426 if ((obj = level.objects[omx][omy]) != 0
427 && !index(nofetch, obj->oclass)
428 #ifdef MAIL
429 && obj->otyp != SCR_MAIL
430 #endif
432 int edible = dogfood(mtmp, obj);
434 if ((edible <= CADAVER
435 /* starving pet is more aggressive about eating */
436 || (edog->mhpmax_penalty && edible == ACCFOOD))
437 && could_reach_item(mtmp, obj->ox, obj->oy))
438 return dog_eat(mtmp, obj, omx, omy, FALSE);
440 carryamt = can_carry(mtmp, obj);
441 if (carryamt > 0 && !obj->cursed
442 && could_reach_item(mtmp, obj->ox, obj->oy)) {
443 if (rn2(20) < edog->apport + 3) {
444 if (rn2(udist) || !rn2(edog->apport)) {
445 otmp = obj;
446 if (carryamt != obj->quan)
447 otmp = splitobj(obj, carryamt);
448 if (cansee(omx, omy) && flags.verbose)
449 pline("%s picks up %s.", Monnam(mtmp),
450 distant_name(otmp, doname));
451 obj_extract_self(otmp);
452 newsym(omx, omy);
453 (void) mpickobj(mtmp, otmp);
454 if (attacktype(mtmp->data, AT_WEAP)
455 && mtmp->weapon_check == NEED_WEAPON) {
456 mtmp->weapon_check = NEED_HTH_WEAPON;
457 (void) mon_wield_item(mtmp);
459 m_dowear(mtmp, FALSE);
465 return 0;
468 /* set dog's goal -- gtyp, gx, gy;
469 returns -1/0/1 (dog's desire to approach player) or -2 (abort move) */
470 STATIC_OVL int
471 dog_goal(mtmp, edog, after, udist, whappr)
472 register struct monst *mtmp;
473 struct edog *edog;
474 int after, udist, whappr;
476 register int omx, omy;
477 boolean in_masters_sight, dog_has_minvent;
478 register struct obj *obj;
479 xchar otyp;
480 int appr;
482 /* Steeds don't move on their own will */
483 if (mtmp == u.usteed)
484 return -2;
486 omx = mtmp->mx;
487 omy = mtmp->my;
489 in_masters_sight = couldsee(omx, omy);
490 dog_has_minvent = (droppables(mtmp) != 0);
492 if (!edog || mtmp->mleashed) { /* he's not going anywhere... */
493 gtyp = APPORT;
494 gx = u.ux;
495 gy = u.uy;
496 } else {
497 #define DDIST(x, y) (dist2(x, y, omx, omy))
498 #define SQSRCHRADIUS 5
499 int min_x, max_x, min_y, max_y;
500 register int nx, ny;
502 gtyp = UNDEF; /* no goal as yet */
503 gx = gy = 0; /* suppress 'used before set' message */
505 if ((min_x = omx - SQSRCHRADIUS) < 1)
506 min_x = 1;
507 if ((max_x = omx + SQSRCHRADIUS) >= COLNO)
508 max_x = COLNO - 1;
509 if ((min_y = omy - SQSRCHRADIUS) < 0)
510 min_y = 0;
511 if ((max_y = omy + SQSRCHRADIUS) >= ROWNO)
512 max_y = ROWNO - 1;
514 /* nearby food is the first choice, then other objects */
515 for (obj = fobj; obj; obj = obj->nobj) {
516 nx = obj->ox;
517 ny = obj->oy;
518 if (nx >= min_x && nx <= max_x && ny >= min_y && ny <= max_y) {
519 otyp = dogfood(mtmp, obj);
520 /* skip inferior goals */
521 if (otyp > gtyp || otyp == UNDEF)
522 continue;
523 /* avoid cursed items unless starving */
524 if (cursed_object_at(nx, ny)
525 && !(edog->mhpmax_penalty && otyp < MANFOOD))
526 continue;
527 /* skip completely unreachable goals */
528 if (!could_reach_item(mtmp, nx, ny)
529 || !can_reach_location(mtmp, mtmp->mx, mtmp->my, nx, ny))
530 continue;
531 if (otyp < MANFOOD) {
532 if (otyp < gtyp || DDIST(nx, ny) < DDIST(gx, gy)) {
533 gx = nx;
534 gy = ny;
535 gtyp = otyp;
537 } else if (gtyp == UNDEF && in_masters_sight
538 && !dog_has_minvent
539 && (!levl[omx][omy].lit || levl[u.ux][u.uy].lit)
540 && (otyp == MANFOOD || m_cansee(mtmp, nx, ny))
541 && edog->apport > rn2(8)
542 && can_carry(mtmp, obj) > 0) {
543 gx = nx;
544 gy = ny;
545 gtyp = APPORT;
551 /* follow player if appropriate */
552 if (gtyp == UNDEF || (gtyp != DOGFOOD && gtyp != APPORT
553 && monstermoves < edog->hungrytime)) {
554 gx = u.ux;
555 gy = u.uy;
556 if (after && udist <= 4 && gx == u.ux && gy == u.uy)
557 return -2;
558 appr = (udist >= 9) ? 1 : (mtmp->mflee) ? -1 : 0;
559 if (udist > 1) {
560 if (!IS_ROOM(levl[u.ux][u.uy].typ) || !rn2(4) || whappr
561 || (dog_has_minvent && rn2(edog->apport)))
562 appr = 1;
564 /* if you have dog food it'll follow you more closely */
565 if (appr == 0)
566 for (obj = invent; obj; obj = obj->nobj)
567 if (dogfood(mtmp, obj) == DOGFOOD) {
568 appr = 1;
569 break;
571 } else
572 appr = 1; /* gtyp != UNDEF */
573 if (mtmp->mconf)
574 appr = 0;
576 #define FARAWAY (COLNO + 2) /* position outside screen */
577 if (gx == u.ux && gy == u.uy && !in_masters_sight) {
578 register coord *cp;
580 cp = gettrack(omx, omy);
581 if (cp) {
582 gx = cp->x;
583 gy = cp->y;
584 if (edog)
585 edog->ogoal.x = 0;
586 } else {
587 /* assume master hasn't moved far, and reuse previous goal */
588 if (edog && edog->ogoal.x
589 && (edog->ogoal.x != omx || edog->ogoal.y != omy)) {
590 gx = edog->ogoal.x;
591 gy = edog->ogoal.y;
592 edog->ogoal.x = 0;
593 } else {
594 int fardist = FARAWAY * FARAWAY;
595 gx = gy = FARAWAY; /* random */
596 do_clear_area(omx, omy, 9, wantdoor, (genericptr_t) &fardist);
598 /* here gx == FARAWAY e.g. when dog is in a vault */
599 if (gx == FARAWAY || (gx == omx && gy == omy)) {
600 gx = u.ux;
601 gy = u.uy;
602 } else if (edog) {
603 edog->ogoal.x = gx;
604 edog->ogoal.y = gy;
608 } else if (edog) {
609 edog->ogoal.x = 0;
611 return appr;
615 STATIC_OVL struct monst *
616 find_targ(mtmp, dx, dy, maxdist)
617 register struct monst *mtmp;
618 int dx, dy;
619 int maxdist;
621 struct monst *targ = 0;
622 int curx = mtmp->mx, cury = mtmp->my;
623 int dist = 0;
625 /* Walk outwards */
626 for ( ; dist < maxdist; ++dist) {
627 curx += dx;
628 cury += dy;
629 if (!isok(curx, cury))
630 break;
632 /* FIXME: Check if we hit a wall/door/boulder to
633 * short-circuit unnecessary subsequent checks
636 /* If we can't see up to here, forget it - will this
637 * mean pets in corridors don't breathe at monsters
638 * in rooms? If so, is that necessarily bad?
640 if (!m_cansee(mtmp, curx, cury))
641 break;
643 targ = m_at(curx, cury);
645 if (curx == mtmp->mux && cury == mtmp->muy)
646 return &youmonst;
648 if (targ) {
649 /* Is the monster visible to the pet? */
650 if ((!targ->minvis || perceives(mtmp->data)) &&
651 !targ->mundetected)
652 break;
654 /* If the pet can't see it, it assumes it aint there */
655 targ = 0;
658 return targ;
661 STATIC_OVL int
662 find_friends(mtmp, mtarg, maxdist)
663 struct monst *mtmp, *mtarg;
664 int maxdist;
666 struct monst *pal;
667 int dx = sgn(mtarg->mx - mtmp->mx),
668 dy = sgn(mtarg->my - mtmp->my);
669 int curx = mtarg->mx, cury = mtarg->my;
670 int dist = distmin(mtarg->mx, mtarg->my, mtmp->mx, mtmp->my);
672 for ( ; dist <= maxdist; ++dist) {
673 curx += dx;
674 cury += dy;
676 if (!isok(curx, cury))
677 return 0;
679 /* If the pet can't see beyond this point, don't
680 * check any farther
682 if (!m_cansee(mtmp, curx, cury))
683 return 0;
685 /* Does pet think you're here? */
686 if (mtmp->mux == curx && mtmp->muy == cury)
687 return 1;
689 pal = m_at(curx, cury);
691 if (pal) {
692 if (pal->mtame) {
693 /* Pet won't notice invisible pets */
694 if (!pal->minvis || perceives(mtmp->data))
695 return 1;
696 } else {
697 /* Quest leaders and guardians are always seen */
698 if (pal->data->msound == MS_LEADER
699 || pal->data->msound == MS_GUARDIAN)
700 return 1;
704 return 0;
707 STATIC_OVL long
708 score_targ(mtmp, mtarg)
709 struct monst *mtmp, *mtarg;
711 long score = 0L;
713 /* If the monster is confused, normal scoring is disrupted -
714 * anything may happen
717 /* Give 1 in 3 chance of safe breathing even if pet is confused or
718 * if you're on the quest start level */
719 if (!mtmp->mconf || !rn2(3) || Is_qstart(&u.uz)) {
720 aligntyp align1 = A_NONE, align2 = A_NONE; /* For priests, minions */
721 boolean faith1 = TRUE, faith2 = TRUE;
723 if (mtmp->isminion)
724 align1 = EMIN(mtmp)->min_align;
725 else if (mtmp->ispriest)
726 align1 = EPRI(mtmp)->shralign;
727 else
728 faith1 = FALSE;
729 if (mtarg->isminion)
730 align2 = EMIN(mtarg)->min_align; /* MAR */
731 else if (mtarg->ispriest)
732 align2 = EPRI(mtarg)->shralign; /* MAR */
733 else
734 faith2 = FALSE;
736 /* Never target quest friendlies */
737 if (mtarg->data->msound == MS_LEADER
738 || mtarg->data->msound == MS_GUARDIAN)
739 return -5000L;
740 /* D: Fixed angelic beings using gaze attacks on coaligned priests */
741 if (faith1 && faith2 && align1 == align2 && mtarg->mpeaceful) {
742 score -= 5000L;
743 return score;
745 /* Is monster adjacent? */
746 if (distmin(mtmp->mx, mtmp->my, mtarg->mx, mtarg->my) <= 1) {
747 score -= 3000L;
748 return score;
750 /* Is the monster peaceful or tame? */
751 if (/*mtarg->mpeaceful ||*/ mtarg->mtame || mtarg == &youmonst) {
752 /* Pets will never be targeted */
753 score -= 3000L;
754 return score;
756 /* Is master/pet behind monster? Check up to 15 squares beyond pet. */
757 if (find_friends(mtmp, mtarg, 15)) {
758 score -= 3000L;
759 return score;
761 /* Target hostile monsters in preference to peaceful ones */
762 if (!mtarg->mpeaceful)
763 score += 10;
764 /* Is the monster passive? Don't waste energy on it, if so */
765 if (mtarg->data->mattk[0].aatyp == AT_NONE)
766 score -= 1000;
767 /* Even weak pets with breath attacks shouldn't take on very
768 low-level monsters. Wasting breath on lichens is ridiculous. */
769 if ((mtarg->m_lev < 2 && mtmp->m_lev > 5)
770 || (mtmp->m_lev > 12 && mtarg->m_lev < mtmp->m_lev - 9
771 && u.ulevel > 8 && mtarg->m_lev < u.ulevel - 7))
772 score -= 25;
773 /* And pets will hesitate to attack vastly stronger foes.
774 This penalty will be discarded if master's in trouble. */
775 if (mtarg->m_lev > mtmp->m_lev + 4L)
776 score -= (mtarg->m_lev - mtmp->m_lev) * 20L;
777 /* All things being the same, go for the beefiest monster. This
778 bonus should not be large enough to override the pet's aversion
779 to attacking much stronger monsters. */
780 score += mtarg->m_lev * 2 + mtarg->mhp / 3;
782 /* Fuzz factor to make things less predictable when very
783 similar targets are abundant. */
784 score += rnd(5);
785 /* Pet may decide not to use ranged attack when confused */
786 if (mtmp->mconf && !rn2(3))
787 score -= 1000;
788 return score;
792 STATIC_OVL struct monst *
793 best_target(mtmp)
794 struct monst *mtmp; /* Pet */
796 int dx, dy;
797 long bestscore = -40000L, currscore;
798 struct monst *best_targ = 0, *temp_targ = 0;
800 /* Help! */
801 if (!mtmp)
802 return 0;
804 /* If the pet is blind, it's not going to see any target */
805 if (!mtmp->mcansee)
806 return 0;
808 /* Search for any monsters lined up with the pet, within an arbitrary
809 * distance from the pet (7 squares, even along diagonals). Monsters
810 * are assigned scores and the best score is chosen.
812 for (dy = -1; dy < 2; ++dy) {
813 for (dx = -1; dx < 2; ++dx) {
814 if (!dx && !dy)
815 continue;
816 /* Traverse the line to find the first monster within 7
817 * squares. Invisible monsters are skipped (if the
818 * pet doesn't have see invisible).
820 temp_targ = find_targ(mtmp, dx, dy, 7);
822 /* Nothing in this line? */
823 if (!temp_targ)
824 continue;
826 /* Decide how attractive the target is */
827 currscore = score_targ(mtmp, temp_targ);
829 if (currscore > bestscore) {
830 bestscore = currscore;
831 best_targ = temp_targ;
836 /* Filter out targets the pet doesn't like */
837 if (bestscore < 0L)
838 best_targ = 0;
840 return best_targ;
844 /* return 0 (no move), 1 (move) or 2 (dead) */
846 dog_move(mtmp, after)
847 register struct monst *mtmp;
848 int after; /* this is extra fast monster movement */
850 int omx, omy; /* original mtmp position */
851 int appr, whappr, udist;
852 int i, j, k;
853 register struct edog *edog = EDOG(mtmp);
854 struct obj *obj = (struct obj *) 0;
855 xchar otyp;
856 boolean has_edog, cursemsg[9], do_eat = FALSE;
857 boolean better_with_displacing = FALSE;
858 xchar nix, niy; /* position mtmp is (considering) moving to */
859 register int nx, ny; /* temporary coordinates */
860 xchar cnt, uncursedcnt, chcnt;
861 int chi = -1, nidist, ndist;
862 coord poss[9];
863 long info[9], allowflags;
864 #define GDIST(x, y) (dist2(x, y, gx, gy))
867 * Tame Angels have isminion set and an ispriest structure instead of
868 * an edog structure. Fortunately, guardian Angels need not worry
869 * about mundane things like eating and fetching objects, and can
870 * spend all their energy defending the player. (They are the only
871 * monsters with other structures that can be tame.)
873 has_edog = !mtmp->isminion;
875 omx = mtmp->mx;
876 omy = mtmp->my;
877 if (has_edog && dog_hunger(mtmp, edog))
878 return 2; /* starved */
880 udist = distu(omx, omy);
881 /* Let steeds eat and maybe throw rider during Conflict */
882 if (mtmp == u.usteed) {
883 if (Conflict && !resist(mtmp, RING_CLASS, 0, 0)) {
884 dismount_steed(DISMOUNT_THROWN);
885 return 1;
887 udist = 1;
888 } else if (!udist)
889 /* maybe we tamed him while being swallowed --jgm */
890 return 0;
892 nix = omx; /* set before newdogpos */
893 niy = omy;
894 cursemsg[0] = FALSE; /* lint suppression */
895 info[0] = 0; /* ditto */
897 if (has_edog) {
898 j = dog_invent(mtmp, edog, udist);
899 if (j == 2)
900 return 2; /* died */
901 else if (j == 1)
902 goto newdogpos; /* eating something */
904 whappr = (monstermoves - edog->whistletime < 5);
905 } else
906 whappr = 0;
908 appr = dog_goal(mtmp, has_edog ? edog : (struct edog *) 0, after, udist,
909 whappr);
910 if (appr == -2)
911 return 0;
913 allowflags = ALLOW_M | ALLOW_TRAPS | ALLOW_SSM | ALLOW_SANCT;
914 if (passes_walls(mtmp->data))
915 allowflags |= (ALLOW_ROCK | ALLOW_WALL);
916 if (passes_bars(mtmp->data))
917 allowflags |= ALLOW_BARS;
918 if (throws_rocks(mtmp->data))
919 allowflags |= ALLOW_ROCK;
920 if (is_displacer(mtmp->data))
921 allowflags |= ALLOW_MDISP;
922 if (Conflict && !resist(mtmp, RING_CLASS, 0, 0)) {
923 allowflags |= ALLOW_U;
924 if (!has_edog) {
925 /* Guardian angel refuses to be conflicted; rather,
926 * it disappears, angrily, and sends in some nasties
928 lose_guardian_angel(mtmp);
929 return 2; /* current monster is gone */
932 #if 0 /* [this is now handled in dochug()] */
933 if (!Conflict && !mtmp->mconf
934 && mtmp == u.ustuck && !sticks(youmonst.data)) {
935 unstuck(mtmp); /* swallowed case handled above */
936 You("get released!");
938 #endif
939 if (!nohands(mtmp->data) && !verysmall(mtmp->data)) {
940 allowflags |= OPENDOOR;
941 if (monhaskey(mtmp, TRUE))
942 allowflags |= UNLOCKDOOR;
943 /* note: the Wizard and Riders can unlock doors without a key;
944 they won't use that ability if someone manages to tame them */
946 if (is_giant(mtmp->data))
947 allowflags |= BUSTDOOR;
948 if (tunnels(mtmp->data)
949 && !Is_rogue_level(&u.uz)) /* same restriction as m_move() */
950 allowflags |= ALLOW_DIG;
951 cnt = mfndpos(mtmp, poss, info, allowflags);
953 /* Normally dogs don't step on cursed items, but if they have no
954 * other choice they will. This requires checking ahead of time
955 * to see how many uncursed item squares are around.
957 uncursedcnt = 0;
958 for (i = 0; i < cnt; i++) {
959 nx = poss[i].x;
960 ny = poss[i].y;
961 if (MON_AT(nx, ny) && !((info[i] & ALLOW_M) || info[i] & ALLOW_MDISP))
962 continue;
963 if (cursed_object_at(nx, ny))
964 continue;
965 uncursedcnt++;
968 better_with_displacing = should_displace(mtmp, poss, info, cnt, gx, gy);
970 chcnt = 0;
971 chi = -1;
972 nidist = GDIST(nix, niy);
974 for (i = 0; i < cnt; i++) {
975 nx = poss[i].x;
976 ny = poss[i].y;
977 cursemsg[i] = FALSE;
979 /* if leashed, we drag him along. */
980 if (mtmp->mleashed && distu(nx, ny) > 4)
981 continue;
983 /* if a guardian, try to stay close by choice */
984 if (!has_edog && (j = distu(nx, ny)) > 16 && j >= udist)
985 continue;
987 if ((info[i] & ALLOW_M) && MON_AT(nx, ny)) {
988 int mstatus;
989 register struct monst *mtmp2 = m_at(nx, ny);
991 if ((int) mtmp2->m_lev >= (int) mtmp->m_lev + 2
992 || (mtmp2->data == &mons[PM_FLOATING_EYE] && rn2(10)
993 && mtmp->mcansee && haseyes(mtmp->data) && mtmp2->mcansee
994 && (perceives(mtmp->data) || !mtmp2->minvis))
995 || (mtmp2->data == &mons[PM_GELATINOUS_CUBE] && rn2(10))
996 || (max_passive_dmg(mtmp2, mtmp) >= mtmp->mhp)
997 || ((mtmp->mhp * 4 < mtmp->mhpmax
998 || mtmp2->data->msound == MS_GUARDIAN
999 || mtmp2->data->msound == MS_LEADER) && mtmp2->mpeaceful
1000 && !Conflict)
1001 || (touch_petrifies(mtmp2->data) && !resists_ston(mtmp)))
1002 continue;
1004 if (after)
1005 return 0; /* hit only once each move */
1007 notonhead = 0;
1008 mstatus = mattackm(mtmp, mtmp2);
1010 /* aggressor (pet) died */
1011 if (mstatus & MM_AGR_DIED)
1012 return 2;
1014 if ((mstatus & MM_HIT) && !(mstatus & MM_DEF_DIED) && rn2(4)
1015 && mtmp2->mlstmv != monstermoves
1016 && !onscary(mtmp->mx, mtmp->my, mtmp2)
1017 /* monnear check needed: long worms hit on tail */
1018 && monnear(mtmp2, mtmp->mx, mtmp->my)) {
1019 mstatus = mattackm(mtmp2, mtmp); /* return attack */
1020 if (mstatus & MM_DEF_DIED)
1021 return 2;
1023 return 0;
1025 if ((info[i] & ALLOW_MDISP) && MON_AT(nx, ny)
1026 && better_with_displacing && !undesirable_disp(mtmp, nx, ny)) {
1027 int mstatus;
1028 register struct monst *mtmp2 = m_at(nx, ny);
1030 mstatus = mdisplacem(mtmp, mtmp2, FALSE); /* displace monster */
1031 if (mstatus & MM_DEF_DIED)
1032 return 2;
1033 return 0;
1037 /* Dog avoids harmful traps, but perhaps it has to pass one
1038 * in order to follow player. (Non-harmful traps do not
1039 * have ALLOW_TRAPS in info[].) The dog only avoids the
1040 * trap if you've seen it, unlike enemies who avoid traps
1041 * if they've seen some trap of that type sometime in the
1042 * past. (Neither behavior is really realistic.)
1044 struct trap *trap;
1046 if ((info[i] & ALLOW_TRAPS) && (trap = t_at(nx, ny))) {
1047 if (mtmp->mleashed) {
1048 if (!Deaf)
1049 whimper(mtmp);
1050 } else {
1051 /* 1/40 chance of stepping on it anyway, in case
1052 * it has to pass one to follow the player...
1054 if (trap->tseen && rn2(40))
1055 continue;
1060 /* dog eschews cursed objects, but likes dog food */
1061 /* (minion isn't interested; `cursemsg' stays FALSE) */
1062 if (has_edog)
1063 for (obj = level.objects[nx][ny]; obj; obj = obj->nexthere) {
1064 if (obj->cursed) {
1065 cursemsg[i] = TRUE;
1066 } else if ((otyp = dogfood(mtmp, obj)) < MANFOOD
1067 && (otyp < ACCFOOD
1068 || edog->hungrytime <= monstermoves)) {
1069 /* Note: our dog likes the food so much that he
1070 * might eat it even when it conceals a cursed object */
1071 nix = nx;
1072 niy = ny;
1073 chi = i;
1074 do_eat = TRUE;
1075 cursemsg[i] = FALSE; /* not reluctant */
1076 goto newdogpos;
1079 /* didn't find something to eat; if we saw a cursed item and
1080 aren't being forced to walk on it, usually keep looking */
1081 if (cursemsg[i] && !mtmp->mleashed && uncursedcnt > 0
1082 && rn2(13 * uncursedcnt))
1083 continue;
1085 /* lessen the chance of backtracking to previous position(s) */
1086 k = has_edog ? uncursedcnt : cnt;
1087 for (j = 0; j < MTSZ && j < k - 1; j++)
1088 if (nx == mtmp->mtrack[j].x && ny == mtmp->mtrack[j].y)
1089 if (rn2(MTSZ * (k - j)))
1090 goto nxti;
1092 j = ((ndist = GDIST(nx, ny)) - nidist) * appr;
1093 if ((j == 0 && !rn2(++chcnt)) || j < 0
1094 || (j > 0 && !whappr
1095 && ((omx == nix && omy == niy && !rn2(3)) || !rn2(12)))) {
1096 nix = nx;
1097 niy = ny;
1098 nidist = ndist;
1099 if (j < 0)
1100 chcnt = 0;
1101 chi = i;
1103 nxti:
1107 /* Pet hasn't attacked anything but is considering moving -
1108 * now's the time for ranged attacks. Note that the pet can move
1109 * after it performs its ranged attack. Should this be changed?
1112 struct monst *mtarg;
1113 int hungry = 0;
1115 /* How hungry is the pet? */
1116 if (!mtmp->isminion) {
1117 struct edog *dog = EDOG(mtmp);
1118 hungry = (monstermoves > (dog->hungrytime + 300));
1121 /* Identify the best target in a straight line from the pet;
1122 * if there is such a target, we'll let the pet attempt an
1123 * attack.
1125 mtarg = best_target(mtmp);
1127 /* Hungry pets are unlikely to use breath/spit attacks */
1128 if (mtarg && (!hungry || !rn2(5))) {
1129 int mstatus;
1131 if (mtarg == &youmonst) {
1132 if (mattacku(mtmp))
1133 return 2;
1134 } else {
1135 mstatus = mattackm(mtmp, mtarg);
1137 /* Shouldn't happen, really */
1138 if (mstatus & MM_AGR_DIED)
1139 return 2;
1141 /* Allow the targeted nasty to strike back - if
1142 * the targeted beast doesn't have a ranged attack,
1143 * nothing will happen.
1145 if ((mstatus & MM_HIT) && !(mstatus & MM_DEF_DIED)
1146 && rn2(4) && mtarg != &youmonst) {
1148 /* Can monster see? If it can, it can retaliate
1149 * even if the pet is invisible, since it'll see
1150 * the direction from which the ranged attack came;
1151 * if it's blind or unseeing, it can't retaliate
1153 if (mtarg->mcansee && haseyes(mtarg->data)) {
1154 mstatus = mattackm(mtarg, mtmp);
1155 if (mstatus & MM_DEF_DIED)
1156 return 2;
1163 newdogpos:
1164 if (nix != omx || niy != omy) {
1165 boolean wasseen;
1167 if (info[chi] & ALLOW_U) {
1168 if (mtmp->mleashed) { /* play it safe */
1169 pline("%s breaks loose of %s leash!", Monnam(mtmp),
1170 mhis(mtmp));
1171 m_unleash(mtmp, FALSE);
1173 (void) mattacku(mtmp);
1174 return 0;
1176 if (!m_in_out_region(mtmp, nix, niy))
1177 return 1;
1178 if (m_digweapon_check(mtmp, nix,niy))
1179 return 0;
1181 /* insert a worm_move() if worms ever begin to eat things */
1182 wasseen = canseemon(mtmp);
1183 remove_monster(omx, omy);
1184 place_monster(mtmp, nix, niy);
1185 if (cursemsg[chi] && (wasseen || canseemon(mtmp))) {
1186 /* describe top item of pile, not necessarily cursed item itself;
1187 don't use glyph_at() here--it would return the pet but we want
1188 to know whether an object is remembered at this map location */
1189 struct obj *o = (!Hallucination && level.flags.hero_memory
1190 && glyph_is_object(levl[nix][niy].glyph))
1191 ? vobj_at(nix, niy) : 0;
1192 const char *what = o ? distant_name(o, doname) : something;
1194 pline("%s %s reluctantly over %s.", noit_Monnam(mtmp),
1195 vtense((char *) 0, locomotion(mtmp->data, "step")), what);
1197 for (j = MTSZ - 1; j > 0; j--)
1198 mtmp->mtrack[j] = mtmp->mtrack[j - 1];
1199 mtmp->mtrack[0].x = omx;
1200 mtmp->mtrack[0].y = omy;
1201 /* We have to know if the pet's going to do a combined eat and
1202 * move before moving it, but it can't eat until after being
1203 * moved. Thus the do_eat flag.
1205 if (do_eat) {
1206 if (dog_eat(mtmp, obj, omx, omy, FALSE) == 2)
1207 return 2;
1209 } else if (mtmp->mleashed && distu(omx, omy) > 4) {
1210 /* an incredible kludge, but the only way to keep pooch near
1211 * after it spends time eating or in a trap, etc.
1213 coord cc;
1215 nx = sgn(omx - u.ux);
1216 ny = sgn(omy - u.uy);
1217 cc.x = u.ux + nx;
1218 cc.y = u.uy + ny;
1219 if (goodpos(cc.x, cc.y, mtmp, 0))
1220 goto dognext;
1222 i = xytod(nx, ny);
1223 for (j = (i + 7) % 8; j < (i + 1) % 8; j++) {
1224 dtoxy(&cc, j);
1225 if (goodpos(cc.x, cc.y, mtmp, 0))
1226 goto dognext;
1228 for (j = (i + 6) % 8; j < (i + 2) % 8; j++) {
1229 dtoxy(&cc, j);
1230 if (goodpos(cc.x, cc.y, mtmp, 0))
1231 goto dognext;
1233 cc.x = mtmp->mx;
1234 cc.y = mtmp->my;
1235 dognext:
1236 if (!m_in_out_region(mtmp, nix, niy))
1237 return 1;
1238 remove_monster(mtmp->mx, mtmp->my);
1239 place_monster(mtmp, cc.x, cc.y);
1240 newsym(cc.x, cc.y);
1241 set_apparxy(mtmp);
1243 return 1;
1246 /* check if a monster could pick up objects from a location */
1247 STATIC_OVL boolean
1248 could_reach_item(mon, nx, ny)
1249 struct monst *mon;
1250 xchar nx, ny;
1252 if ((!is_pool(nx, ny) || is_swimmer(mon->data))
1253 && (!is_lava(nx, ny) || likes_lava(mon->data))
1254 && (!sobj_at(BOULDER, nx, ny) || throws_rocks(mon->data)))
1255 return TRUE;
1256 return FALSE;
1259 /* Hack to prevent a dog from being endlessly stuck near an object that
1260 * it can't reach, such as caught in a teleport scroll niche. It recursively
1261 * checks to see if the squares in between are good. The checking could be
1262 * a little smarter; a full check would probably be useful in m_move() too.
1263 * Since the maximum food distance is 5, this should never be more than 5
1264 * calls deep.
1266 STATIC_OVL boolean
1267 can_reach_location(mon, mx, my, fx, fy)
1268 struct monst *mon;
1269 xchar mx, my, fx, fy;
1271 int i, j;
1272 int dist;
1274 if (mx == fx && my == fy)
1275 return TRUE;
1276 if (!isok(mx, my))
1277 return FALSE; /* should not happen */
1279 dist = dist2(mx, my, fx, fy);
1280 for (i = mx - 1; i <= mx + 1; i++) {
1281 for (j = my - 1; j <= my + 1; j++) {
1282 if (!isok(i, j))
1283 continue;
1284 if (dist2(i, j, fx, fy) >= dist)
1285 continue;
1286 if (IS_ROCK(levl[i][j].typ) && !passes_walls(mon->data)
1287 && (!may_dig(i, j) || !tunnels(mon->data)))
1288 continue;
1289 if (IS_DOOR(levl[i][j].typ)
1290 && (levl[i][j].doormask & (D_CLOSED | D_LOCKED)))
1291 continue;
1292 if (!could_reach_item(mon, i, j))
1293 continue;
1294 if (can_reach_location(mon, i, j, fx, fy))
1295 return TRUE;
1298 return FALSE;
1301 /* do_clear_area client */
1302 STATIC_PTR void
1303 wantdoor(x, y, distance)
1304 int x, y;
1305 genericptr_t distance;
1307 int ndist, *dist_ptr = (int *) distance;
1309 if (*dist_ptr > (ndist = distu(x, y))) {
1310 gx = x;
1311 gy = y;
1312 *dist_ptr = ndist;
1316 static struct qmchoices {
1317 int mndx; /* type of pet, 0 means any */
1318 char mlet; /* symbol of pet, 0 means any */
1319 unsigned mappearance; /* mimic this */
1320 uchar m_ap_type; /* what is the thing it is mimicing? */
1321 } qm[] = {
1322 /* Things that some pets might be thinking about at the time */
1323 { PM_LITTLE_DOG, 0, PM_KITTEN, M_AP_MONSTER },
1324 { PM_DOG, 0, PM_HOUSECAT, M_AP_MONSTER },
1325 { PM_LARGE_DOG, 0, PM_LARGE_CAT, M_AP_MONSTER },
1326 { PM_KITTEN, 0, PM_LITTLE_DOG, M_AP_MONSTER },
1327 { PM_HOUSECAT, 0, PM_DOG, M_AP_MONSTER },
1328 { PM_LARGE_CAT, 0, PM_LARGE_DOG, M_AP_MONSTER },
1329 { PM_HOUSECAT, 0, PM_GIANT_RAT, M_AP_MONSTER },
1330 { 0, S_DOG, SINK,
1331 M_AP_FURNITURE }, /* sorry, no fire hydrants in NetHack */
1332 { 0, 0, TRIPE_RATION, M_AP_OBJECT }, /* leave this at end */
1335 void
1336 finish_meating(mtmp)
1337 struct monst *mtmp;
1339 mtmp->meating = 0;
1340 if (mtmp->m_ap_type && mtmp->mappearance && mtmp->cham == NON_PM) {
1341 /* was eating a mimic and now appearance needs resetting */
1342 mtmp->m_ap_type = 0;
1343 mtmp->mappearance = 0;
1344 newsym(mtmp->mx, mtmp->my);
1348 STATIC_OVL void
1349 quickmimic(mtmp)
1350 struct monst *mtmp;
1352 int idx = 0, trycnt = 5, spotted;
1353 char buf[BUFSZ];
1355 if (Protection_from_shape_changers || !mtmp->meating)
1356 return;
1358 do {
1359 idx = rn2(SIZE(qm));
1360 if (qm[idx].mndx != 0 && monsndx(mtmp->data) == qm[idx].mndx)
1361 break;
1362 if (qm[idx].mlet != 0 && mtmp->data->mlet == qm[idx].mlet)
1363 break;
1364 if (qm[idx].mndx == 0 && qm[idx].mlet == 0)
1365 break;
1366 } while (--trycnt > 0);
1367 if (trycnt == 0)
1368 idx = SIZE(qm) - 1;
1370 Strcpy(buf, mon_nam(mtmp));
1371 spotted = canspotmon(mtmp);
1373 mtmp->m_ap_type = qm[idx].m_ap_type;
1374 mtmp->mappearance = qm[idx].mappearance;
1376 if (spotted || cansee(mtmp->mx, mtmp->my) || canspotmon(mtmp)) {
1377 /* this isn't quite right; if sensing a monster without being
1378 able to see its location, you really shouldn't be told you
1379 sense it becoming furniture or an object that you can't see
1380 (on the other hand, perhaps you're sensing a brief glimpse
1381 of its mind as it changes form) */
1382 newsym(mtmp->mx, mtmp->my);
1383 You("%s %s %sappear%s where %s was!",
1384 cansee(mtmp->mx, mtmp->my) ? "see" : "sense that",
1385 (mtmp->m_ap_type == M_AP_FURNITURE)
1386 ? an(defsyms[mtmp->mappearance].explanation)
1387 : (mtmp->m_ap_type == M_AP_OBJECT
1388 && OBJ_DESCR(objects[mtmp->mappearance]))
1389 ? an(OBJ_DESCR(objects[mtmp->mappearance]))
1390 : (mtmp->m_ap_type == M_AP_OBJECT
1391 && OBJ_NAME(objects[mtmp->mappearance]))
1392 ? an(OBJ_NAME(objects[mtmp->mappearance]))
1393 : (mtmp->m_ap_type == M_AP_MONSTER)
1394 ? an(mons[mtmp->mappearance].mname)
1395 : something,
1396 cansee(mtmp->mx, mtmp->my) ? "" : "has ",
1397 cansee(mtmp->mx, mtmp->my) ? "" : "ed",
1398 buf);
1399 display_nhwindow(WIN_MAP, TRUE);
1403 /*dogmove.c*/