OSDN Git Service

import nethack-3.6.0
[jnethack/source.git] / src / mhitm.c
1 /* NetHack 3.6  mhitm.c $NHDT-Date: 1446854229 2015/11/06 23:57:09 $  $NHDT-Branch: master $:$NHDT-Revision: 1.83 $ */
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 "artifact.h"
7
8 extern boolean notonhead;
9
10 static NEARDATA boolean vis, far_noise;
11 static NEARDATA long noisetime;
12 static NEARDATA struct obj *otmp;
13
14 static const char brief_feeling[] =
15     "have a %s feeling for a moment, then it passes.";
16
17 STATIC_DCL char *FDECL(mon_nam_too, (char *, struct monst *, struct monst *));
18 STATIC_DCL int FDECL(hitmm, (struct monst *, struct monst *,
19                              struct attack *));
20 STATIC_DCL int FDECL(gazemm, (struct monst *, struct monst *,
21                               struct attack *));
22 STATIC_DCL int FDECL(gulpmm, (struct monst *, struct monst *,
23                               struct attack *));
24 STATIC_DCL int FDECL(explmm, (struct monst *, struct monst *,
25                               struct attack *));
26 STATIC_DCL int FDECL(mdamagem, (struct monst *, struct monst *,
27                                 struct attack *));
28 STATIC_DCL void FDECL(mswingsm, (struct monst *, struct monst *,
29                                  struct obj *));
30 STATIC_DCL void FDECL(noises, (struct monst *, struct attack *));
31 STATIC_DCL void FDECL(missmm, (struct monst *, struct monst *,
32                                struct attack *));
33 STATIC_DCL int FDECL(passivemm, (struct monst *, struct monst *,
34                                  BOOLEAN_P, int));
35
36 /* Needed for the special case of monsters wielding vorpal blades (rare).
37  * If we use this a lot it should probably be a parameter to mdamagem()
38  * instead of a global variable.
39  */
40 static int dieroll;
41
42 /* returns mon_nam(mon) relative to other_mon; normal name unless they're
43    the same, in which case the reference is to {him|her|it} self */
44 STATIC_OVL char *
45 mon_nam_too(outbuf, mon, other_mon)
46 char *outbuf;
47 struct monst *mon, *other_mon;
48 {
49     Strcpy(outbuf, mon_nam(mon));
50     if (mon == other_mon)
51         switch (pronoun_gender(mon)) {
52         case 0:
53             Strcpy(outbuf, "himself");
54             break;
55         case 1:
56             Strcpy(outbuf, "herself");
57             break;
58         default:
59             Strcpy(outbuf, "itself");
60             break;
61         }
62     return outbuf;
63 }
64
65 STATIC_OVL void
66 noises(magr, mattk)
67 register struct monst *magr;
68 register struct attack *mattk;
69 {
70     boolean farq = (distu(magr->mx, magr->my) > 15);
71
72     if (!Deaf && (farq != far_noise || moves - noisetime > 10)) {
73         far_noise = farq;
74         noisetime = moves;
75         You_hear("%s%s.",
76                  (mattk->aatyp == AT_EXPL) ? "an explosion" : "some noises",
77                  farq ? " in the distance" : "");
78     }
79 }
80
81 STATIC_OVL
82 void
83 missmm(magr, mdef, mattk)
84 register struct monst *magr, *mdef;
85 struct attack *mattk;
86 {
87     const char *fmt;
88     char buf[BUFSZ], mdef_name[BUFSZ];
89
90     if (vis) {
91         if (!canspotmon(magr))
92             map_invisible(magr->mx, magr->my);
93         if (!canspotmon(mdef))
94             map_invisible(mdef->mx, mdef->my);
95         if (mdef->m_ap_type)
96             seemimic(mdef);
97         if (magr->m_ap_type)
98             seemimic(magr);
99         fmt = (could_seduce(magr, mdef, mattk) && !magr->mcan)
100                   ? "%s pretends to be friendly to"
101                   : "%s misses";
102         Sprintf(buf, fmt, Monnam(magr));
103         pline("%s %s.", buf, mon_nam_too(mdef_name, mdef, magr));
104     } else
105         noises(magr, mattk);
106 }
107
108 /*
109  *  fightm()  -- fight some other monster
110  *
111  *  Returns:
112  *      0 - Monster did nothing.
113  *      1 - If the monster made an attack.  The monster might have died.
114  *
115  *  There is an exception to the above.  If mtmp has the hero swallowed,
116  *  then we report that the monster did nothing so it will continue to
117  *  digest the hero.
118  */
119  /* have monsters fight each other */
120 int
121 fightm(mtmp)
122 register struct monst *mtmp;
123 {
124     register struct monst *mon, *nmon;
125     int result, has_u_swallowed;
126 #ifdef LINT
127     nmon = 0;
128 #endif
129     /* perhaps the monster will resist Conflict */
130     if (resist(mtmp, RING_CLASS, 0, 0))
131         return 0;
132
133     if (u.ustuck == mtmp) {
134         /* perhaps we're holding it... */
135         if (itsstuck(mtmp))
136             return 0;
137     }
138     has_u_swallowed = (u.uswallow && (mtmp == u.ustuck));
139
140     for (mon = fmon; mon; mon = nmon) {
141         nmon = mon->nmon;
142         if (nmon == mtmp)
143             nmon = mtmp->nmon;
144         /* Be careful to ignore monsters that are already dead, since we
145          * might be calling this before we've cleaned them up.  This can
146          * happen if the monster attacked a cockatrice bare-handedly, for
147          * instance.
148          */
149         if (mon != mtmp && !DEADMONSTER(mon)) {
150             if (monnear(mtmp, mon->mx, mon->my)) {
151                 if (!u.uswallow && (mtmp == u.ustuck)) {
152                     if (!rn2(4)) {
153                         pline("%s releases you!", Monnam(mtmp));
154                         u.ustuck = 0;
155                     } else
156                         break;
157                 }
158
159                 /* mtmp can be killed */
160                 bhitpos.x = mon->mx;
161                 bhitpos.y = mon->my;
162                 notonhead = 0;
163                 result = mattackm(mtmp, mon);
164
165                 if (result & MM_AGR_DIED)
166                     return 1; /* mtmp died */
167                 /*
168                  * If mtmp has the hero swallowed, lie and say there
169                  * was no attack (this allows mtmp to digest the hero).
170                  */
171                 if (has_u_swallowed)
172                     return 0;
173
174                 /* Allow attacked monsters a chance to hit back. Primarily
175                  * to allow monsters that resist conflict to respond.
176                  */
177                 if ((result & MM_HIT) && !(result & MM_DEF_DIED) && rn2(4)
178                     && mon->movement >= NORMAL_SPEED) {
179                     mon->movement -= NORMAL_SPEED;
180                     notonhead = 0;
181                     (void) mattackm(mon, mtmp); /* return attack */
182                 }
183
184                 return (result & MM_HIT) ? 1 : 0;
185             }
186         }
187     }
188     return 0;
189 }
190
191 /*
192  * mdisplacem() -- attacker moves defender out of the way;
193  *                 returns same results as mattackm().
194  */
195 int
196 mdisplacem(magr, mdef, quietly)
197 register struct monst *magr, *mdef;
198 boolean quietly;
199 {
200     struct permonst *pa, *pd;
201     int tx, ty, fx, fy;
202
203     /* sanity checks; could matter if we unexpectedly get a long worm */
204     if (!magr || !mdef || magr == mdef)
205         return MM_MISS;
206     pa = magr->data, pd = mdef->data;
207     tx = mdef->mx, ty = mdef->my; /* destination */
208     fx = magr->mx, fy = magr->my; /* current location */
209     if (m_at(fx, fy) != magr || m_at(tx, ty) != mdef)
210         return MM_MISS;
211
212     /* The 1 in 7 failure below matches the chance in attack()
213      * for pet displacement.
214      */
215     if (!rn2(7))
216         return MM_MISS;
217
218     /* Grid bugs cannot displace at an angle. */
219     if (pa == &mons[PM_GRID_BUG] && magr->mx != mdef->mx
220         && magr->my != mdef->my)
221         return MM_MISS;
222
223     /* undetected monster becomes un-hidden if it is displaced */
224     if (mdef->mundetected)
225         mdef->mundetected = 0;
226     if (mdef->m_ap_type && mdef->m_ap_type != M_AP_MONSTER)
227         seemimic(mdef);
228     /* wake up the displaced defender */
229     mdef->msleeping = 0;
230     mdef->mstrategy &= ~STRAT_WAITMASK;
231     finish_meating(mdef);
232
233     /*
234      * Set up the visibility of action.
235      * You can observe monster displacement if you can see both of
236      * the monsters involved.
237      */
238     vis = (canspotmon(magr) && canspotmon(mdef));
239
240     if (touch_petrifies(pd) && !resists_ston(magr)) {
241         if (which_armor(magr, W_ARMG) != 0) {
242             if (poly_when_stoned(pa)) {
243                 mon_to_stone(magr);
244                 return MM_HIT; /* no damage during the polymorph */
245             }
246             if (!quietly && canspotmon(magr))
247                 pline("%s turns to stone!", Monnam(magr));
248             monstone(magr);
249             if (magr->mhp > 0)
250                 return MM_HIT; /* lifesaved */
251             else if (magr->mtame && !vis)
252                 You(brief_feeling, "peculiarly sad");
253             return MM_AGR_DIED;
254         }
255     }
256
257     remove_monster(fx, fy); /* pick up from orig position */
258     remove_monster(tx, ty);
259     place_monster(magr, tx, ty); /* put down at target spot */
260     place_monster(mdef, fx, fy);
261     if (vis && !quietly)
262         pline("%s moves %s out of %s way!", Monnam(magr), mon_nam(mdef),
263               is_rider(pa) ? "the" : mhis(magr));
264     newsym(fx, fy);  /* see it */
265     newsym(tx, ty);  /*   all happen */
266     flush_screen(0); /* make sure it shows up */
267
268     return MM_HIT;
269 }
270
271 /*
272  * mattackm() -- a monster attacks another monster.
273  *
274  *          --------- aggressor died
275  *         /  ------- defender died
276  *        /  /  ----- defender was hit
277  *       /  /  /
278  *      x  x  x
279  *
280  *      0x4     MM_AGR_DIED
281  *      0x2     MM_DEF_DIED
282  *      0x1     MM_HIT
283  *      0x0     MM_MISS
284  *
285  * Each successive attack has a lower probability of hitting.  Some rely on
286  * success of previous attacks.  ** this doen't seem to be implemented -dl **
287  *
288  * In the case of exploding monsters, the monster dies as well.
289  */
290 int
291 mattackm(magr, mdef)
292 register struct monst *magr, *mdef;
293 {
294     int i,          /* loop counter */
295         tmp,        /* amour class difference */
296         strike,     /* hit this attack */
297         attk,       /* attack attempted this time */
298         struck = 0, /* hit at least once */
299         res[NATTK]; /* results of all attacks */
300     struct attack *mattk, alt_attk;
301     struct permonst *pa, *pd;
302
303     if (!magr || !mdef)
304         return MM_MISS; /* mike@genat */
305     if (!magr->mcanmove || magr->msleeping)
306         return MM_MISS;
307     pa = magr->data;
308     pd = mdef->data;
309
310     /* Grid bugs cannot attack at an angle. */
311     if (pa == &mons[PM_GRID_BUG] && magr->mx != mdef->mx
312         && magr->my != mdef->my)
313         return MM_MISS;
314
315     /* Calculate the armour class differential. */
316     tmp = find_mac(mdef) + magr->m_lev;
317     if (mdef->mconf || !mdef->mcanmove || mdef->msleeping) {
318         tmp += 4;
319         mdef->msleeping = 0;
320     }
321
322     /* undetect monsters become un-hidden if they are attacked */
323     if (mdef->mundetected) {
324         mdef->mundetected = 0;
325         newsym(mdef->mx, mdef->my);
326         if (canseemon(mdef) && !sensemon(mdef)) {
327             if (Unaware)
328                 You("dream of %s.", (mdef->data->geno & G_UNIQ)
329                                         ? a_monnam(mdef)
330                                         : makeplural(m_monnam(mdef)));
331             else
332                 pline("Suddenly, you notice %s.", a_monnam(mdef));
333         }
334     }
335
336     /* Elves hate orcs. */
337     if (is_elf(pa) && is_orc(pd))
338         tmp++;
339
340     /* Set up the visibility of action */
341     vis = (cansee(magr->mx, magr->my) && cansee(mdef->mx, mdef->my)
342            && (canspotmon(magr) || canspotmon(mdef)));
343
344     /* Set flag indicating monster has moved this turn.  Necessary since a
345      * monster might get an attack out of sequence (i.e. before its move) in
346      * some cases, in which case this still counts as its move for the round
347      * and it shouldn't move again.
348      */
349     magr->mlstmv = monstermoves;
350
351     /* Now perform all attacks for the monster. */
352     for (i = 0; i < NATTK; i++) {
353         res[i] = MM_MISS;
354         mattk = getmattk(pa, i, res, &alt_attk);
355         otmp = (struct obj *) 0;
356         attk = 1;
357         switch (mattk->aatyp) {
358         case AT_WEAP: /* "hand to hand" attacks */
359             if (magr->weapon_check == NEED_WEAPON || !MON_WEP(magr)) {
360                 magr->weapon_check = NEED_HTH_WEAPON;
361                 if (mon_wield_item(magr) != 0)
362                     return 0;
363             }
364             possibly_unwield(magr, FALSE);
365             otmp = MON_WEP(magr);
366
367             if (otmp) {
368                 if (vis)
369                     mswingsm(magr, mdef, otmp);
370                 tmp += hitval(otmp, mdef);
371             }
372             /*FALLTHRU*/
373         case AT_CLAW:
374         case AT_KICK:
375         case AT_BITE:
376         case AT_STNG:
377         case AT_TUCH:
378         case AT_BUTT:
379         case AT_TENT:
380             /* Nymph that teleported away on first attack? */
381             if (distmin(magr->mx, magr->my, mdef->mx, mdef->my) > 1)
382                 return MM_MISS;
383             /* Monsters won't attack cockatrices physically if they
384              * have a weapon instead.  This instinct doesn't work for
385              * players, or under conflict or confusion.
386              */
387             if (!magr->mconf && !Conflict && otmp && mattk->aatyp != AT_WEAP
388                 && touch_petrifies(mdef->data)) {
389                 strike = 0;
390                 break;
391             }
392             dieroll = rnd(20 + i);
393             strike = (tmp > dieroll);
394             /* KMH -- don't accumulate to-hit bonuses */
395             if (otmp)
396                 tmp -= hitval(otmp, mdef);
397             if (strike) {
398                 res[i] = hitmm(magr, mdef, mattk);
399                 if ((mdef->data == &mons[PM_BLACK_PUDDING]
400                      || mdef->data == &mons[PM_BROWN_PUDDING]) && otmp
401                     && objects[otmp->otyp].oc_material == IRON
402                     && mdef->mhp > 1
403                     && !mdef->mcan) {
404                     if (clone_mon(mdef, 0, 0)) {
405                         if (vis) {
406                             char buf[BUFSZ];
407
408                             Strcpy(buf, Monnam(mdef));
409                             pline("%s divides as %s hits it!", buf,
410                                   mon_nam(magr));
411                         }
412                     }
413                 }
414             } else
415                 missmm(magr, mdef, mattk);
416             break;
417
418         case AT_HUGS: /* automatic if prev two attacks succeed */
419             strike = (i >= 2 && res[i - 1] == MM_HIT && res[i - 2] == MM_HIT);
420             if (strike)
421                 res[i] = hitmm(magr, mdef, mattk);
422
423             break;
424
425         case AT_GAZE:
426             strike = 0;
427             res[i] = gazemm(magr, mdef, mattk);
428             break;
429
430         case AT_EXPL:
431             res[i] = explmm(magr, mdef, mattk);
432             if (res[i] == MM_MISS) { /* cancelled--no attack */
433                 strike = 0;
434                 attk = 0;
435             } else
436                 strike = 1; /* automatic hit */
437             break;
438
439         case AT_ENGL:
440             if (u.usteed && (mdef == u.usteed)) {
441                 strike = 0;
442                 break;
443             }
444             /* Engulfing attacks are directed at the hero if
445              * possible. -dlc
446              */
447             if (u.uswallow && magr == u.ustuck)
448                 strike = 0;
449             else {
450                 if ((strike = (tmp > rnd(20 + i))))
451                     res[i] = gulpmm(magr, mdef, mattk);
452                 else
453                     missmm(magr, mdef, mattk);
454             }
455             break;
456
457         default: /* no attack */
458             strike = 0;
459             attk = 0;
460             break;
461         }
462
463         if (attk && !(res[i] & MM_AGR_DIED))
464             res[i] = passivemm(magr, mdef, strike, res[i] & MM_DEF_DIED);
465
466         if (res[i] & MM_DEF_DIED)
467             return res[i];
468
469         if (res[i] & MM_AGR_DIED)
470             return res[i];
471         /* return if aggressor can no longer attack */
472         if (!magr->mcanmove || magr->msleeping)
473             return res[i];
474         if (res[i] & MM_HIT)
475             struck = 1; /* at least one hit */
476     }
477
478     return (struck ? MM_HIT : MM_MISS);
479 }
480
481 /* Returns the result of mdamagem(). */
482 STATIC_OVL int
483 hitmm(magr, mdef, mattk)
484 register struct monst *magr, *mdef;
485 struct attack *mattk;
486 {
487     if (vis) {
488         int compat;
489         char buf[BUFSZ], mdef_name[BUFSZ];
490
491         if (!canspotmon(magr))
492             map_invisible(magr->mx, magr->my);
493         if (!canspotmon(mdef))
494             map_invisible(mdef->mx, mdef->my);
495         if (mdef->m_ap_type)
496             seemimic(mdef);
497         if (magr->m_ap_type)
498             seemimic(magr);
499         if ((compat = could_seduce(magr, mdef, mattk)) && !magr->mcan) {
500             Sprintf(buf, "%s %s", Monnam(magr),
501                     mdef->mcansee ? "smiles at" : "talks to");
502             pline("%s %s %s.", buf, mon_nam(mdef),
503                   compat == 2 ? "engagingly" : "seductively");
504         } else {
505             char magr_name[BUFSZ];
506
507             Strcpy(magr_name, Monnam(magr));
508             switch (mattk->aatyp) {
509             case AT_BITE:
510                 Sprintf(buf, "%s bites", magr_name);
511                 break;
512             case AT_STNG:
513                 Sprintf(buf, "%s stings", magr_name);
514                 break;
515             case AT_BUTT:
516                 Sprintf(buf, "%s butts", magr_name);
517                 break;
518             case AT_TUCH:
519                 Sprintf(buf, "%s touches", magr_name);
520                 break;
521             case AT_TENT:
522                 Sprintf(buf, "%s tentacles suck", s_suffix(magr_name));
523                 break;
524             case AT_HUGS:
525                 if (magr != u.ustuck) {
526                     Sprintf(buf, "%s squeezes", magr_name);
527                     break;
528                 }
529             default:
530                 Sprintf(buf, "%s hits", magr_name);
531             }
532             pline("%s %s.", buf, mon_nam_too(mdef_name, mdef, magr));
533         }
534     } else
535         noises(magr, mattk);
536
537     return mdamagem(magr, mdef, mattk);
538 }
539
540 /* Returns the same values as mdamagem(). */
541 STATIC_OVL int
542 gazemm(magr, mdef, mattk)
543 register struct monst *magr, *mdef;
544 struct attack *mattk;
545 {
546     char buf[BUFSZ];
547
548     if (vis) {
549         Sprintf(buf, "%s gazes at", Monnam(magr));
550         pline("%s %s...", buf, mon_nam(mdef));
551     }
552
553     if (magr->mcan || !magr->mcansee
554         || (magr->minvis && !perceives(mdef->data)) || !mdef->mcansee
555         || mdef->msleeping) {
556         if (vis)
557             pline("but nothing happens.");
558         return MM_MISS;
559     }
560     /* call mon_reflects 2x, first test, then, if visible, print message */
561     if (magr->data == &mons[PM_MEDUSA] && mon_reflects(mdef, (char *) 0)) {
562         if (canseemon(mdef))
563             (void) mon_reflects(mdef, "The gaze is reflected away by %s %s.");
564         if (mdef->mcansee) {
565             if (mon_reflects(magr, (char *) 0)) {
566                 if (canseemon(magr))
567                     (void) mon_reflects(
568                         magr, "The gaze is reflected away by %s %s.");
569                 return MM_MISS;
570             }
571             if (mdef->minvis && !perceives(magr->data)) {
572                 if (canseemon(magr)) {
573                     pline(
574                       "%s doesn't seem to notice that %s gaze was reflected.",
575                           Monnam(magr), mhis(magr));
576                 }
577                 return MM_MISS;
578             }
579             if (canseemon(magr))
580                 pline("%s is turned to stone!", Monnam(magr));
581             monstone(magr);
582             if (magr->mhp > 0)
583                 return MM_MISS;
584             return MM_AGR_DIED;
585         }
586     }
587
588     return mdamagem(magr, mdef, mattk);
589 }
590
591 /* return True if magr is allowed to swallow mdef, False otherwise */
592 boolean
593 engulf_target(magr, mdef)
594 struct monst *magr, *mdef;
595 {
596     struct rm *lev;
597     int dx, dy;
598
599     /* can't swallow something that's too big */
600     if (mdef->data->msize >= MZ_HUGE)
601         return FALSE;
602
603     /* (hypothetical) engulfers who can pass through walls aren't
604      limited by rock|trees|bars */
605     if ((magr == &youmonst) ? Passes_walls : passes_walls(magr->data))
606         return TRUE;
607
608     /* don't swallow something in a spot where attacker wouldn't
609        otherwise be able to move onto; we don't want to engulf
610        a wall-phaser and end up with a non-phaser inside a wall */
611     dx = mdef->mx, dy = mdef->my;
612     if (mdef == &youmonst)
613         dx = u.ux, dy = u.uy;
614     lev = &levl[dx][dy];
615     if (IS_ROCK(lev->typ) || closed_door(dx, dy) || IS_TREE(lev->typ)
616         /* not passes_bars(); engulfer isn't squeezing through */
617         || (lev->typ == IRONBARS && !is_whirly(magr->data)))
618         return FALSE;
619
620     return TRUE;
621 }
622
623 /* Returns the same values as mattackm(). */
624 STATIC_OVL int
625 gulpmm(magr, mdef, mattk)
626 register struct monst *magr, *mdef;
627 register struct attack *mattk;
628 {
629     xchar ax, ay, dx, dy;
630     int status;
631     char buf[BUFSZ];
632     struct obj *obj;
633
634     if (!engulf_target(magr, mdef))
635         return MM_MISS;
636
637     if (vis) {
638         Sprintf(buf, "%s swallows", Monnam(magr));
639         pline("%s %s.", buf, mon_nam(mdef));
640     }
641     for (obj = mdef->minvent; obj; obj = obj->nobj)
642         (void) snuff_lit(obj);
643
644     /*
645      *  All of this manipulation is needed to keep the display correct.
646      *  There is a flush at the next pline().
647      */
648     ax = magr->mx;
649     ay = magr->my;
650     dx = mdef->mx;
651     dy = mdef->my;
652     /*
653      *  Leave the defender in the monster chain at it's current position,
654      *  but don't leave it on the screen.  Move the aggressor to the
655      *  defender's position.
656      */
657     remove_monster(ax, ay);
658     place_monster(magr, dx, dy);
659     newsym(ax, ay); /* erase old position */
660     newsym(dx, dy); /* update new position */
661
662     status = mdamagem(magr, mdef, mattk);
663
664     if ((status & (MM_AGR_DIED | MM_DEF_DIED))
665         == (MM_AGR_DIED | MM_DEF_DIED)) {
666         ;                              /* both died -- do nothing  */
667     } else if (status & MM_DEF_DIED) { /* defender died */
668         /*
669          *  Note:  remove_monster() was called in relmon(), wiping out
670          *  magr from level.monsters[mdef->mx][mdef->my].  We need to
671          *  put it back and display it.  -kd
672          */
673         place_monster(magr, dx, dy);
674         newsym(dx, dy);
675         /* aggressor moves to <dx,dy> and might encounter trouble there */
676         if (minliquid(magr) || (t_at(dx, dy) && mintrap(magr) == 2))
677             status |= MM_AGR_DIED;
678     } else if (status & MM_AGR_DIED) { /* aggressor died */
679         place_monster(mdef, dx, dy);
680         newsym(dx, dy);
681     } else { /* both alive, put them back */
682         if (cansee(dx, dy))
683             pline("%s is regurgitated!", Monnam(mdef));
684
685         place_monster(magr, ax, ay);
686         place_monster(mdef, dx, dy);
687         newsym(ax, ay);
688         newsym(dx, dy);
689     }
690
691     return status;
692 }
693
694 STATIC_OVL int
695 explmm(magr, mdef, mattk)
696 struct monst *magr, *mdef;
697 struct attack *mattk;
698 {
699     int result;
700
701     if (magr->mcan)
702         return MM_MISS;
703
704     if (cansee(magr->mx, magr->my))
705         pline("%s explodes!", Monnam(magr));
706     else
707         noises(magr, mattk);
708
709     result = mdamagem(magr, mdef, mattk);
710
711     /* Kill off aggressor if it didn't die. */
712     if (!(result & MM_AGR_DIED)) {
713         mondead(magr);
714         if (magr->mhp > 0)
715             return result; /* life saved */
716         result |= MM_AGR_DIED;
717     }
718     if (magr->mtame) /* give this one even if it was visible */
719         You(brief_feeling, "melancholy");
720
721     return result;
722 }
723
724 /*
725  *  See comment at top of mattackm(), for return values.
726  */
727 STATIC_OVL int
728 mdamagem(magr, mdef, mattk)
729 register struct monst *magr, *mdef;
730 register struct attack *mattk;
731 {
732     struct obj *obj;
733     char buf[BUFSZ];
734     struct permonst *pa = magr->data, *pd = mdef->data;
735     int armpro, num, tmp = d((int) mattk->damn, (int) mattk->damd),
736                      res = MM_MISS;
737     boolean cancelled;
738
739     if ((touch_petrifies(pd) /* or flesh_petrifies() */
740          || (mattk->adtyp == AD_DGST && pd == &mons[PM_MEDUSA]))
741         && !resists_ston(magr)) {
742         long protector = attk_protection((int) mattk->aatyp),
743              wornitems = magr->misc_worn_check;
744
745         /* wielded weapon gives same protection as gloves here */
746         if (otmp != 0)
747             wornitems |= W_ARMG;
748
749         if (protector == 0L
750             || (protector != ~0L && (wornitems & protector) != protector)) {
751             if (poly_when_stoned(pa)) {
752                 mon_to_stone(magr);
753                 return MM_HIT; /* no damage during the polymorph */
754             }
755             if (vis)
756                 pline("%s turns to stone!", Monnam(magr));
757             monstone(magr);
758             if (magr->mhp > 0)
759                 return MM_HIT; /* lifesaved */
760             else if (magr->mtame && !vis)
761                 You(brief_feeling, "peculiarly sad");
762             return MM_AGR_DIED;
763         }
764     }
765
766     /* cancellation factor is the same as when attacking the hero */
767     armpro = magic_negation(mdef);
768     cancelled = magr->mcan || !(rn2(10) >= 3 * armpro);
769
770     switch (mattk->adtyp) {
771     case AD_DGST:
772         /* eating a Rider or its corpse is fatal */
773         if (is_rider(pd)) {
774             if (vis)
775                 pline("%s %s!", Monnam(magr),
776                       (pd == &mons[PM_FAMINE])
777                           ? "belches feebly, shrivels up and dies"
778                           : (pd == &mons[PM_PESTILENCE])
779                                 ? "coughs spasmodically and collapses"
780                                 : "vomits violently and drops dead");
781             mondied(magr);
782             if (magr->mhp > 0)
783                 return 0; /* lifesaved */
784             else if (magr->mtame && !vis)
785                 You(brief_feeling, "queasy");
786             return MM_AGR_DIED;
787         }
788         if (flags.verbose && !Deaf)
789             verbalize("Burrrrp!");
790         tmp = mdef->mhp;
791         /* Use up amulet of life saving */
792         if (!!(obj = mlifesaver(mdef)))
793             m_useup(mdef, obj);
794
795         /* Is a corpse for nutrition possible?  It may kill magr */
796         if (!corpse_chance(mdef, magr, TRUE) || magr->mhp < 1)
797             break;
798
799         /* Pets get nutrition from swallowing monster whole.
800          * No nutrition from G_NOCORPSE monster, eg, undead.
801          * DGST monsters don't die from undead corpses
802          */
803         num = monsndx(pd);
804         if (magr->mtame && !magr->isminion
805             && !(mvitals[num].mvflags & G_NOCORPSE)) {
806             struct obj *virtualcorpse = mksobj(CORPSE, FALSE, FALSE);
807             int nutrit;
808
809             set_corpsenm(virtualcorpse, num);
810             nutrit = dog_nutrition(magr, virtualcorpse);
811             dealloc_obj(virtualcorpse);
812
813             /* only 50% nutrition, 25% of normal eating time */
814             if (magr->meating > 1)
815                 magr->meating = (magr->meating + 3) / 4;
816             if (nutrit > 1)
817                 nutrit /= 2;
818             EDOG(magr)->hungrytime += nutrit;
819         }
820         break;
821     case AD_STUN:
822         if (magr->mcan)
823             break;
824         if (canseemon(mdef))
825             pline("%s %s for a moment.", Monnam(mdef),
826                   makeplural(stagger(pd, "stagger")));
827         mdef->mstun = 1;
828         goto physical;
829     case AD_LEGS:
830         if (magr->mcan) {
831             tmp = 0;
832             break;
833         }
834         goto physical;
835     case AD_WERE:
836     case AD_HEAL:
837     case AD_PHYS:
838     physical:
839         if (mattk->aatyp == AT_KICK && thick_skinned(pd)) {
840             tmp = 0;
841         } else if (mattk->aatyp == AT_WEAP) {
842             if (otmp) {
843                 if (otmp->otyp == CORPSE
844                     && touch_petrifies(&mons[otmp->corpsenm]))
845                     goto do_stone;
846                 tmp += dmgval(otmp, mdef);
847                 if (otmp->oartifact) {
848                     (void) artifact_hit(magr, mdef, otmp, &tmp, dieroll);
849                     if (mdef->mhp <= 0)
850                         return (MM_DEF_DIED
851                                 | (grow_up(magr, mdef) ? 0 : MM_AGR_DIED));
852                 }
853                 if (tmp)
854                     rustm(mdef, otmp);
855             }
856         } else if (pa == &mons[PM_PURPLE_WORM] && pd == &mons[PM_SHRIEKER]) {
857             /* hack to enhance mm_aggression(); we don't want purple
858                worm's bite attack to kill a shrieker because then it
859                won't swallow the corpse; but if the target survives,
860                the subsequent engulf attack should accomplish that */
861             if (tmp >= mdef->mhp && mdef->mhp > 1)
862                 tmp = mdef->mhp - 1;
863         }
864         break;
865     case AD_FIRE:
866         if (cancelled) {
867             tmp = 0;
868             break;
869         }
870         if (vis)
871             pline("%s is %s!", Monnam(mdef), on_fire(pd, mattk));
872         if (pd == &mons[PM_STRAW_GOLEM] || pd == &mons[PM_PAPER_GOLEM]) {
873             if (vis)
874                 pline("%s burns completely!", Monnam(mdef));
875             mondied(mdef);
876             if (mdef->mhp > 0)
877                 return 0;
878             else if (mdef->mtame && !vis)
879                 pline("May %s roast in peace.", mon_nam(mdef));
880             return (MM_DEF_DIED | (grow_up(magr, mdef) ? 0 : MM_AGR_DIED));
881         }
882         tmp += destroy_mitem(mdef, SCROLL_CLASS, AD_FIRE);
883         tmp += destroy_mitem(mdef, SPBOOK_CLASS, AD_FIRE);
884         if (resists_fire(mdef)) {
885             if (vis)
886                 pline_The("fire doesn't seem to burn %s!", mon_nam(mdef));
887             shieldeff(mdef->mx, mdef->my);
888             golemeffects(mdef, AD_FIRE, tmp);
889             tmp = 0;
890         }
891         /* only potions damage resistant players in destroy_item */
892         tmp += destroy_mitem(mdef, POTION_CLASS, AD_FIRE);
893         break;
894     case AD_COLD:
895         if (cancelled) {
896             tmp = 0;
897             break;
898         }
899         if (vis)
900             pline("%s is covered in frost!", Monnam(mdef));
901         if (resists_cold(mdef)) {
902             if (vis)
903                 pline_The("frost doesn't seem to chill %s!", mon_nam(mdef));
904             shieldeff(mdef->mx, mdef->my);
905             golemeffects(mdef, AD_COLD, tmp);
906             tmp = 0;
907         }
908         tmp += destroy_mitem(mdef, POTION_CLASS, AD_COLD);
909         break;
910     case AD_ELEC:
911         if (cancelled) {
912             tmp = 0;
913             break;
914         }
915         if (vis)
916             pline("%s gets zapped!", Monnam(mdef));
917         tmp += destroy_mitem(mdef, WAND_CLASS, AD_ELEC);
918         if (resists_elec(mdef)) {
919             if (vis)
920                 pline_The("zap doesn't shock %s!", mon_nam(mdef));
921             shieldeff(mdef->mx, mdef->my);
922             golemeffects(mdef, AD_ELEC, tmp);
923             tmp = 0;
924         }
925         /* only rings damage resistant players in destroy_item */
926         tmp += destroy_mitem(mdef, RING_CLASS, AD_ELEC);
927         break;
928     case AD_ACID:
929         if (magr->mcan) {
930             tmp = 0;
931             break;
932         }
933         if (resists_acid(mdef)) {
934             if (vis)
935                 pline("%s is covered in acid, but it seems harmless.",
936                       Monnam(mdef));
937             tmp = 0;
938         } else if (vis) {
939             pline("%s is covered in acid!", Monnam(mdef));
940             pline("It burns %s!", mon_nam(mdef));
941         }
942         if (!rn2(30))
943             erode_armor(mdef, ERODE_CORRODE);
944         if (!rn2(6))
945             acid_damage(MON_WEP(mdef));
946         break;
947     case AD_RUST:
948         if (magr->mcan)
949             break;
950         if (pd == &mons[PM_IRON_GOLEM]) {
951             if (vis)
952                 pline("%s falls to pieces!", Monnam(mdef));
953             mondied(mdef);
954             if (mdef->mhp > 0)
955                 return 0;
956             else if (mdef->mtame && !vis)
957                 pline("May %s rust in peace.", mon_nam(mdef));
958             return (MM_DEF_DIED | (grow_up(magr, mdef) ? 0 : MM_AGR_DIED));
959         }
960         erode_armor(mdef, ERODE_RUST);
961         mdef->mstrategy &= ~STRAT_WAITFORU;
962         tmp = 0;
963         break;
964     case AD_CORR:
965         if (magr->mcan)
966             break;
967         erode_armor(mdef, ERODE_CORRODE);
968         mdef->mstrategy &= ~STRAT_WAITFORU;
969         tmp = 0;
970         break;
971     case AD_DCAY:
972         if (magr->mcan)
973             break;
974         if (pd == &mons[PM_WOOD_GOLEM] || pd == &mons[PM_LEATHER_GOLEM]) {
975             if (vis)
976                 pline("%s falls to pieces!", Monnam(mdef));
977             mondied(mdef);
978             if (mdef->mhp > 0)
979                 return 0;
980             else if (mdef->mtame && !vis)
981                 pline("May %s rot in peace.", mon_nam(mdef));
982             return (MM_DEF_DIED | (grow_up(magr, mdef) ? 0 : MM_AGR_DIED));
983         }
984         erode_armor(mdef, ERODE_CORRODE);
985         mdef->mstrategy &= ~STRAT_WAITFORU;
986         tmp = 0;
987         break;
988     case AD_STON:
989         if (magr->mcan)
990             break;
991     do_stone:
992         /* may die from the acid if it eats a stone-curing corpse */
993         if (munstone(mdef, FALSE))
994             goto post_stone;
995         if (poly_when_stoned(pd)) {
996             mon_to_stone(mdef);
997             tmp = 0;
998             break;
999         }
1000         if (!resists_ston(mdef)) {
1001             if (vis)
1002                 pline("%s turns to stone!", Monnam(mdef));
1003             monstone(mdef);
1004         post_stone:
1005             if (mdef->mhp > 0)
1006                 return 0;
1007             else if (mdef->mtame && !vis)
1008                 You(brief_feeling, "peculiarly sad");
1009             return (MM_DEF_DIED | (grow_up(magr, mdef) ? 0 : MM_AGR_DIED));
1010         }
1011         tmp = (mattk->adtyp == AD_STON ? 0 : 1);
1012         break;
1013     case AD_TLPT:
1014         if (!cancelled && tmp < mdef->mhp && !tele_restrict(mdef)) {
1015             char mdef_Monnam[BUFSZ];
1016             /* save the name before monster teleports, otherwise
1017                we'll get "it" in the suddenly disappears message */
1018             if (vis)
1019                 Strcpy(mdef_Monnam, Monnam(mdef));
1020             mdef->mstrategy &= ~STRAT_WAITFORU;
1021             (void) rloc(mdef, TRUE);
1022             if (vis && !canspotmon(mdef) && mdef != u.usteed)
1023                 pline("%s suddenly disappears!", mdef_Monnam);
1024         }
1025         break;
1026     case AD_SLEE:
1027         if (!cancelled && !mdef->msleeping
1028             && sleep_monst(mdef, rnd(10), -1)) {
1029             if (vis) {
1030                 Strcpy(buf, Monnam(mdef));
1031                 pline("%s is put to sleep by %s.", buf, mon_nam(magr));
1032             }
1033             mdef->mstrategy &= ~STRAT_WAITFORU;
1034             slept_monst(mdef);
1035         }
1036         break;
1037     case AD_PLYS:
1038         if (!cancelled && mdef->mcanmove) {
1039             if (vis) {
1040                 Strcpy(buf, Monnam(mdef));
1041                 pline("%s is frozen by %s.", buf, mon_nam(magr));
1042             }
1043             paralyze_monst(mdef, rnd(10));
1044         }
1045         break;
1046     case AD_SLOW:
1047         if (!cancelled && mdef->mspeed != MSLOW) {
1048             unsigned int oldspeed = mdef->mspeed;
1049
1050             mon_adjust_speed(mdef, -1, (struct obj *) 0);
1051             mdef->mstrategy &= ~STRAT_WAITFORU;
1052             if (mdef->mspeed != oldspeed && vis)
1053                 pline("%s slows down.", Monnam(mdef));
1054         }
1055         break;
1056     case AD_CONF:
1057         /* Since confusing another monster doesn't have a real time
1058          * limit, setting spec_used would not really be right (though
1059          * we still should check for it).
1060          */
1061         if (!magr->mcan && !mdef->mconf && !magr->mspec_used) {
1062             if (vis)
1063                 pline("%s looks confused.", Monnam(mdef));
1064             mdef->mconf = 1;
1065             mdef->mstrategy &= ~STRAT_WAITFORU;
1066         }
1067         break;
1068     case AD_BLND:
1069         if (can_blnd(magr, mdef, mattk->aatyp, (struct obj *) 0)) {
1070             register unsigned rnd_tmp;
1071
1072             if (vis && mdef->mcansee)
1073                 pline("%s is blinded.", Monnam(mdef));
1074             rnd_tmp = d((int) mattk->damn, (int) mattk->damd);
1075             if ((rnd_tmp += mdef->mblinded) > 127)
1076                 rnd_tmp = 127;
1077             mdef->mblinded = rnd_tmp;
1078             mdef->mcansee = 0;
1079             mdef->mstrategy &= ~STRAT_WAITFORU;
1080         }
1081         tmp = 0;
1082         break;
1083     case AD_HALU:
1084         if (!magr->mcan && haseyes(pd) && mdef->mcansee) {
1085             if (vis)
1086                 pline("%s looks %sconfused.", Monnam(mdef),
1087                       mdef->mconf ? "more " : "");
1088             mdef->mconf = 1;
1089             mdef->mstrategy &= ~STRAT_WAITFORU;
1090         }
1091         tmp = 0;
1092         break;
1093     case AD_CURS:
1094         if (!night() && (pa == &mons[PM_GREMLIN]))
1095             break;
1096         if (!magr->mcan && !rn2(10)) {
1097             mdef->mcan = 1; /* cancelled regardless of lifesave */
1098             mdef->mstrategy &= ~STRAT_WAITFORU;
1099             if (is_were(pd) && pd->mlet != S_HUMAN)
1100                 were_change(mdef);
1101             if (pd == &mons[PM_CLAY_GOLEM]) {
1102                 if (vis) {
1103                     pline("Some writing vanishes from %s head!",
1104                           s_suffix(mon_nam(mdef)));
1105                     pline("%s is destroyed!", Monnam(mdef));
1106                 }
1107                 mondied(mdef);
1108                 if (mdef->mhp > 0)
1109                     return 0;
1110                 else if (mdef->mtame && !vis)
1111                     You(brief_feeling, "strangely sad");
1112                 return (MM_DEF_DIED
1113                         | (grow_up(magr, mdef) ? 0 : MM_AGR_DIED));
1114             }
1115             if (!Deaf) {
1116                 if (!vis)
1117                     You_hear("laughter.");
1118                 else
1119                     pline("%s chuckles.", Monnam(magr));
1120             }
1121         }
1122         break;
1123     case AD_SGLD:
1124         tmp = 0;
1125         if (magr->mcan)
1126             break;
1127         /* technically incorrect; no check for stealing gold from
1128          * between mdef's feet...
1129          */
1130         {
1131             struct obj *gold = findgold(mdef->minvent);
1132             if (!gold)
1133                 break;
1134             obj_extract_self(gold);
1135             add_to_minv(magr, gold);
1136         }
1137         mdef->mstrategy &= ~STRAT_WAITFORU;
1138         if (vis) {
1139             Strcpy(buf, Monnam(magr));
1140             pline("%s steals some gold from %s.", buf, mon_nam(mdef));
1141         }
1142         if (!tele_restrict(magr)) {
1143             (void) rloc(magr, TRUE);
1144             if (vis && !canspotmon(magr))
1145                 pline("%s suddenly disappears!", buf);
1146         }
1147         break;
1148     case AD_DRLI:
1149         if (!cancelled && !rn2(3) && !resists_drli(mdef)) {
1150             tmp = d(2, 6);
1151             if (vis)
1152                 pline("%s suddenly seems weaker!", Monnam(mdef));
1153             mdef->mhpmax -= tmp;
1154             if (mdef->m_lev == 0)
1155                 tmp = mdef->mhp;
1156             else
1157                 mdef->m_lev--;
1158             /* Automatic kill if drained past level 0 */
1159         }
1160         break;
1161     case AD_SSEX:
1162     case AD_SITM: /* for now these are the same */
1163     case AD_SEDU:
1164         if (magr->mcan)
1165             break;
1166         /* find an object to steal, non-cursed if magr is tame */
1167         for (obj = mdef->minvent; obj; obj = obj->nobj)
1168             if (!magr->mtame || !obj->cursed)
1169                 break;
1170
1171         if (obj) {
1172             char onambuf[BUFSZ], mdefnambuf[BUFSZ];
1173
1174             /* make a special x_monnam() call that never omits
1175                the saddle, and save it for later messages */
1176             Strcpy(mdefnambuf,
1177                    x_monnam(mdef, ARTICLE_THE, (char *) 0, 0, FALSE));
1178
1179             otmp = obj;
1180             if (u.usteed == mdef && otmp == which_armor(mdef, W_SADDLE))
1181                 /* "You can no longer ride <steed>." */
1182                 dismount_steed(DISMOUNT_POLY);
1183             obj_extract_self(otmp);
1184             if (otmp->owornmask) {
1185                 mdef->misc_worn_check &= ~otmp->owornmask;
1186                 if (otmp->owornmask & W_WEP)
1187                     mwepgone(mdef);
1188                 otmp->owornmask = 0L;
1189                 update_mon_intrinsics(mdef, otmp, FALSE, FALSE);
1190             }
1191             /* add_to_minv() might free otmp [if it merges] */
1192             if (vis)
1193                 Strcpy(onambuf, doname(otmp));
1194             (void) add_to_minv(magr, otmp);
1195             if (vis) {
1196                 Strcpy(buf, Monnam(magr));
1197                 pline("%s steals %s from %s!", buf, onambuf, mdefnambuf);
1198             }
1199             possibly_unwield(mdef, FALSE);
1200             mdef->mstrategy &= ~STRAT_WAITFORU;
1201             mselftouch(mdef, (const char *) 0, FALSE);
1202             if (mdef->mhp <= 0)
1203                 return (MM_DEF_DIED
1204                         | (grow_up(magr, mdef) ? 0 : MM_AGR_DIED));
1205             if (pa->mlet == S_NYMPH && !tele_restrict(magr)) {
1206                 (void) rloc(magr, TRUE);
1207                 if (vis && !canspotmon(magr))
1208                     pline("%s suddenly disappears!", buf);
1209             }
1210         }
1211         tmp = 0;
1212         break;
1213     case AD_DREN:
1214         if (!cancelled && !rn2(4))
1215             xdrainenergym(mdef, vis && mattk->aatyp != AT_ENGL);
1216         tmp = 0;
1217         break;
1218     case AD_DRST:
1219     case AD_DRDX:
1220     case AD_DRCO:
1221         if (!cancelled && !rn2(8)) {
1222             if (vis)
1223                 pline("%s %s was poisoned!", s_suffix(Monnam(magr)),
1224                       mpoisons_subj(magr, mattk));
1225             if (resists_poison(mdef)) {
1226                 if (vis)
1227                     pline_The("poison doesn't seem to affect %s.",
1228                               mon_nam(mdef));
1229             } else {
1230                 if (rn2(10))
1231                     tmp += rn1(10, 6);
1232                 else {
1233                     if (vis)
1234                         pline_The("poison was deadly...");
1235                     tmp = mdef->mhp;
1236                 }
1237             }
1238         }
1239         break;
1240     case AD_DRIN:
1241         if (notonhead || !has_head(pd)) {
1242             if (vis)
1243                 pline("%s doesn't seem harmed.", Monnam(mdef));
1244             /* Not clear what to do for green slimes */
1245             tmp = 0;
1246             break;
1247         }
1248         if ((mdef->misc_worn_check & W_ARMH) && rn2(8)) {
1249             if (vis) {
1250                 Strcpy(buf, s_suffix(Monnam(mdef)));
1251                 pline("%s helmet blocks %s attack to %s head.", buf,
1252                       s_suffix(mon_nam(magr)), mhis(mdef));
1253             }
1254             break;
1255         }
1256         res = eat_brains(magr, mdef, vis, &tmp);
1257         break;
1258     case AD_SLIM:
1259         if (cancelled)
1260             break; /* physical damage only */
1261         if (!rn2(4) && !slimeproof(pd)) {
1262             if (!munslime(mdef, FALSE) && mdef->mhp > 0) {
1263                 if (newcham(mdef, &mons[PM_GREEN_SLIME], FALSE, vis))
1264                     pd = mdef->data;
1265                 mdef->mstrategy &= ~STRAT_WAITFORU;
1266                 res = MM_HIT;
1267             }
1268             /* munslime attempt could have been fatal,
1269                potentially to multiple monsters (SCR_FIRE) */
1270             if (magr->mhp < 1)
1271                 res |= MM_AGR_DIED;
1272             if (mdef->mhp < 1)
1273                 res |= MM_DEF_DIED;
1274             tmp = 0;
1275         }
1276         break;
1277     case AD_STCK:
1278         if (cancelled)
1279             tmp = 0;
1280         break;
1281     case AD_WRAP: /* monsters cannot grab one another, it's too hard */
1282         if (magr->mcan)
1283             tmp = 0;
1284         break;
1285     case AD_ENCH:
1286         /* there's no msomearmor() function, so just do damage */
1287         /* if (cancelled) break; */
1288         break;
1289     default:
1290         tmp = 0;
1291         break;
1292     }
1293     if (!tmp)
1294         return res;
1295
1296     if ((mdef->mhp -= tmp) < 1) {
1297         if (m_at(mdef->mx, mdef->my) == magr) { /* see gulpmm() */
1298             remove_monster(mdef->mx, mdef->my);
1299             mdef->mhp = 1; /* otherwise place_monster will complain */
1300             place_monster(mdef, mdef->mx, mdef->my);
1301             mdef->mhp = 0;
1302         }
1303         monkilled(mdef, "", (int) mattk->adtyp);
1304         if (mdef->mhp > 0)
1305             return res; /* mdef lifesaved */
1306         else if (res == MM_AGR_DIED)
1307             return (MM_DEF_DIED | MM_AGR_DIED);
1308
1309         if (mattk->adtyp == AD_DGST) {
1310             /* various checks similar to dog_eat and meatobj.
1311              * after monkilled() to provide better message ordering */
1312             if (mdef->cham >= LOW_PM) {
1313                 (void) newcham(magr, (struct permonst *) 0, FALSE, TRUE);
1314             } else if (pd == &mons[PM_GREEN_SLIME] && !slimeproof(pa)) {
1315                 (void) newcham(magr, &mons[PM_GREEN_SLIME], FALSE, TRUE);
1316             } else if (pd == &mons[PM_WRAITH]) {
1317                 (void) grow_up(magr, (struct monst *) 0);
1318                 /* don't grow up twice */
1319                 return (MM_DEF_DIED | (magr->mhp > 0 ? 0 : MM_AGR_DIED));
1320             } else if (pd == &mons[PM_NURSE]) {
1321                 magr->mhp = magr->mhpmax;
1322             }
1323         }
1324         /* caveat: above digestion handling doesn't keep `pa' up to date */
1325
1326         return (MM_DEF_DIED | (grow_up(magr, mdef) ? 0 : MM_AGR_DIED));
1327     }
1328     return (res == MM_AGR_DIED) ? MM_AGR_DIED : MM_HIT;
1329 }
1330
1331 void
1332 paralyze_monst(mon, amt)
1333 struct monst *mon;
1334 int amt;
1335 {
1336     if (amt > 127)
1337         amt = 127;
1338
1339     mon->mcanmove = 0;
1340     mon->mfrozen = amt;
1341     mon->meating = 0; /* terminate any meal-in-progress */
1342     mon->mstrategy &= ~STRAT_WAITFORU;
1343 }
1344
1345 /* `mon' is hit by a sleep attack; return 1 if it's affected, 0 otherwise */
1346 int
1347 sleep_monst(mon, amt, how)
1348 struct monst *mon;
1349 int amt, how;
1350 {
1351     if (resists_sleep(mon)
1352         || (how >= 0 && resist(mon, (char) how, 0, NOTELL))) {
1353         shieldeff(mon->mx, mon->my);
1354     } else if (mon->mcanmove) {
1355         finish_meating(mon); /* terminate any meal-in-progress */
1356         amt += (int) mon->mfrozen;
1357         if (amt > 0) { /* sleep for N turns */
1358             mon->mcanmove = 0;
1359             mon->mfrozen = min(amt, 127);
1360         } else { /* sleep until awakened */
1361             mon->msleeping = 1;
1362         }
1363         return 1;
1364     }
1365     return 0;
1366 }
1367
1368 /* sleeping grabber releases, engulfer doesn't; don't use for paralysis! */
1369 void
1370 slept_monst(mon)
1371 struct monst *mon;
1372 {
1373     if ((mon->msleeping || !mon->mcanmove) && mon == u.ustuck
1374         && !sticks(youmonst.data) && !u.uswallow) {
1375         pline("%s grip relaxes.", s_suffix(Monnam(mon)));
1376         unstuck(mon);
1377     }
1378 }
1379
1380 void
1381 rustm(mdef, obj)
1382 struct monst *mdef;
1383 struct obj *obj;
1384 {
1385     int dmgtyp;
1386
1387     if (!mdef || !obj)
1388         return; /* just in case */
1389     /* AD_ACID is handled in passivemm */
1390     if (dmgtype(mdef->data, AD_CORR))
1391         dmgtyp = ERODE_CORRODE;
1392     else if (dmgtype(mdef->data, AD_RUST))
1393         dmgtyp = ERODE_RUST;
1394     else if (dmgtype(mdef->data, AD_FIRE))
1395         dmgtyp = ERODE_BURN;
1396     else
1397         return;
1398     (void) erode_obj(obj, NULL, dmgtyp, EF_GREASE | EF_VERBOSE);
1399 }
1400
1401 STATIC_OVL void
1402 mswingsm(magr, mdef, otemp)
1403 struct monst *magr, *mdef;
1404 struct obj *otemp;
1405 {
1406     if (flags.verbose && !Blind && mon_visible(magr)) {
1407         pline("%s %s %s%s %s at %s.", Monnam(magr),
1408               (objects[otemp->otyp].oc_dir & PIERCE) ? "thrusts" : "swings",
1409               (otemp->quan > 1L) ? "one of " : "", mhis(magr), xname(otemp),
1410               mon_nam(mdef));
1411     }
1412 }
1413
1414 /*
1415  * Passive responses by defenders.  Does not replicate responses already
1416  * handled above.  Returns same values as mattackm.
1417  */
1418 STATIC_OVL int
1419 passivemm(magr, mdef, mhit, mdead)
1420 register struct monst *magr, *mdef;
1421 boolean mhit;
1422 int mdead;
1423 {
1424     register struct permonst *mddat = mdef->data;
1425     register struct permonst *madat = magr->data;
1426     char buf[BUFSZ];
1427     int i, tmp;
1428
1429     for (i = 0;; i++) {
1430         if (i >= NATTK)
1431             return (mdead | mhit); /* no passive attacks */
1432         if (mddat->mattk[i].aatyp == AT_NONE)
1433             break;
1434     }
1435     if (mddat->mattk[i].damn)
1436         tmp = d((int) mddat->mattk[i].damn, (int) mddat->mattk[i].damd);
1437     else if (mddat->mattk[i].damd)
1438         tmp = d((int) mddat->mlevel + 1, (int) mddat->mattk[i].damd);
1439     else
1440         tmp = 0;
1441
1442     /* These affect the enemy even if defender killed */
1443     switch (mddat->mattk[i].adtyp) {
1444     case AD_ACID:
1445         if (mhit && !rn2(2)) {
1446             Strcpy(buf, Monnam(magr));
1447             if (canseemon(magr))
1448                 pline("%s is splashed by %s acid!", buf,
1449                       s_suffix(mon_nam(mdef)));
1450             if (resists_acid(magr)) {
1451                 if (canseemon(magr))
1452                     pline("%s is not affected.", Monnam(magr));
1453                 tmp = 0;
1454             }
1455         } else
1456             tmp = 0;
1457         if (!rn2(30))
1458             erode_armor(magr, ERODE_CORRODE);
1459         if (!rn2(6))
1460             acid_damage(MON_WEP(magr));
1461         goto assess_dmg;
1462     case AD_ENCH: /* KMH -- remove enchantment (disenchanter) */
1463         if (mhit && !mdef->mcan && otmp) {
1464             (void) drain_item(otmp);
1465             /* No message */
1466         }
1467         break;
1468     default:
1469         break;
1470     }
1471     if (mdead || mdef->mcan)
1472         return (mdead | mhit);
1473
1474     /* These affect the enemy only if defender is still alive */
1475     if (rn2(3))
1476         switch (mddat->mattk[i].adtyp) {
1477         case AD_PLYS: /* Floating eye */
1478             if (tmp > 127)
1479                 tmp = 127;
1480             if (mddat == &mons[PM_FLOATING_EYE]) {
1481                 if (!rn2(4))
1482                     tmp = 127;
1483                 if (magr->mcansee && haseyes(madat) && mdef->mcansee
1484                     && (perceives(madat) || !mdef->minvis)) {
1485                     Sprintf(buf, "%s gaze is reflected by %%s %%s.",
1486                             s_suffix(Monnam(mdef)));
1487                     if (mon_reflects(magr,
1488                                      canseemon(magr) ? buf : (char *) 0))
1489                         return (mdead | mhit);
1490                     Strcpy(buf, Monnam(magr));
1491                     if (canseemon(magr))
1492                         pline("%s is frozen by %s gaze!", buf,
1493                               s_suffix(mon_nam(mdef)));
1494                     paralyze_monst(magr, tmp);
1495                     return (mdead | mhit);
1496                 }
1497             } else { /* gelatinous cube */
1498                 Strcpy(buf, Monnam(magr));
1499                 if (canseemon(magr))
1500                     pline("%s is frozen by %s.", buf, mon_nam(mdef));
1501                 paralyze_monst(magr, tmp);
1502                 return (mdead | mhit);
1503             }
1504             return 1;
1505         case AD_COLD:
1506             if (resists_cold(magr)) {
1507                 if (canseemon(magr)) {
1508                     pline("%s is mildly chilly.", Monnam(magr));
1509                     golemeffects(magr, AD_COLD, tmp);
1510                 }
1511                 tmp = 0;
1512                 break;
1513             }
1514             if (canseemon(magr))
1515                 pline("%s is suddenly very cold!", Monnam(magr));
1516             mdef->mhp += tmp / 2;
1517             if (mdef->mhpmax < mdef->mhp)
1518                 mdef->mhpmax = mdef->mhp;
1519             if (mdef->mhpmax > ((int) (mdef->m_lev + 1) * 8))
1520                 (void) split_mon(mdef, magr);
1521             break;
1522         case AD_STUN:
1523             if (!magr->mstun) {
1524                 magr->mstun = 1;
1525                 if (canseemon(magr))
1526                     pline("%s %s...", Monnam(magr),
1527                           makeplural(stagger(magr->data, "stagger")));
1528             }
1529             tmp = 0;
1530             break;
1531         case AD_FIRE:
1532             if (resists_fire(magr)) {
1533                 if (canseemon(magr)) {
1534                     pline("%s is mildly warmed.", Monnam(magr));
1535                     golemeffects(magr, AD_FIRE, tmp);
1536                 }
1537                 tmp = 0;
1538                 break;
1539             }
1540             if (canseemon(magr))
1541                 pline("%s is suddenly very hot!", Monnam(magr));
1542             break;
1543         case AD_ELEC:
1544             if (resists_elec(magr)) {
1545                 if (canseemon(magr)) {
1546                     pline("%s is mildly tingled.", Monnam(magr));
1547                     golemeffects(magr, AD_ELEC, tmp);
1548                 }
1549                 tmp = 0;
1550                 break;
1551             }
1552             if (canseemon(magr))
1553                 pline("%s is jolted with electricity!", Monnam(magr));
1554             break;
1555         default:
1556             tmp = 0;
1557             break;
1558         }
1559     else
1560         tmp = 0;
1561
1562 assess_dmg:
1563     if ((magr->mhp -= tmp) <= 0) {
1564         monkilled(magr, "", (int) mddat->mattk[i].adtyp);
1565         return (mdead | mhit | MM_AGR_DIED);
1566     }
1567     return (mdead | mhit);
1568 }
1569
1570 /* hero or monster has successfully hit target mon with drain energy attack */
1571 void
1572 xdrainenergym(mon, givemsg)
1573 struct monst *mon;
1574 boolean givemsg;
1575 {
1576     if (mon->mspec_used < 20 /* limit draining */
1577         && (attacktype(mon->data, AT_MAGC)
1578             || attacktype(mon->data, AT_BREA))) {
1579         mon->mspec_used += d(2, 2);
1580         if (givemsg)
1581             pline("%s seems lethargic.", Monnam(mon));
1582     }
1583 }
1584
1585 /* "aggressive defense"; what type of armor prevents specified attack
1586    from touching its target? */
1587 long
1588 attk_protection(aatyp)
1589 int aatyp;
1590 {
1591     long w_mask = 0L;
1592
1593     switch (aatyp) {
1594     case AT_NONE:
1595     case AT_SPIT:
1596     case AT_EXPL:
1597     case AT_BOOM:
1598     case AT_GAZE:
1599     case AT_BREA:
1600     case AT_MAGC:
1601         w_mask = ~0L; /* special case; no defense needed */
1602         break;
1603     case AT_CLAW:
1604     case AT_TUCH:
1605     case AT_WEAP:
1606         w_mask = W_ARMG; /* caller needs to check for weapon */
1607         break;
1608     case AT_KICK:
1609         w_mask = W_ARMF;
1610         break;
1611     case AT_BUTT:
1612         w_mask = W_ARMH;
1613         break;
1614     case AT_HUGS:
1615         w_mask = (W_ARMC | W_ARMG); /* attacker needs both to be protected */
1616         break;
1617     case AT_BITE:
1618     case AT_STNG:
1619     case AT_ENGL:
1620     case AT_TENT:
1621     default:
1622         w_mask = 0L; /* no defense available */
1623         break;
1624     }
1625     return w_mask;
1626 }
1627
1628 /*mhitm.c*/