1 /* NetHack 3.7	mhitm.c	$NHDT-Date: 1614910020 2021/03/05 02:07:00 $  $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.192 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) Robert Patrick Rankin, 2011. */
4 /* NetHack may be freely redistributed.  See license for details. */
5 
6 #include "hack.h"
7 #include "artifact.h"
8 
9 static const char brief_feeling[] =
10     "have a %s feeling for a moment, then it passes.";
11 
12 static int hitmm(struct monst *, struct monst *, struct attack *, struct obj *,
13                  int);
14 static int gazemm(struct monst *, struct monst *, struct attack *);
15 static int gulpmm(struct monst *, struct monst *, struct attack *);
16 static int explmm(struct monst *, struct monst *, struct attack *);
17 static int mdamagem(struct monst *, struct monst *, struct attack *,
18                     struct obj *, int);
19 static void mswingsm(struct monst *, struct monst *, struct obj *);
20 static void noises(struct monst *, struct attack *);
21 static void pre_mm_attack(struct monst *, struct monst *);
22 static void missmm(struct monst *, struct monst *, struct attack *);
23 static int passivemm(struct monst *, struct monst *, boolean, int,
24                      struct obj *);
25 
26 static void
noises(register struct monst * magr,register struct attack * mattk)27 noises(register struct monst *magr, register struct attack *mattk)
28 {
29     boolean farq = (distu(magr->mx, magr->my) > 15);
30 
31     if (!Deaf && (farq != g.far_noise || g.moves - g.noisetime > 10)) {
32         g.far_noise = farq;
33         g.noisetime = g.moves;
34         You_hear("%s%s.",
35                  (mattk->aatyp == AT_EXPL) ? "an explosion" : "some noises",
36                  farq ? " in the distance" : "");
37     }
38 }
39 
40 static void
pre_mm_attack(struct monst * magr,struct monst * mdef)41 pre_mm_attack(struct monst *magr, struct monst *mdef)
42 {
43     boolean showit = FALSE;
44 
45     /* unhiding or unmimicking happens even if hero can't see it
46        because the formerly concealed monster is now in action */
47     if (M_AP_TYPE(mdef)) {
48         seemimic(mdef);
49         showit |= g.vis;
50     } else if (mdef->mundetected) {
51         mdef->mundetected = 0;
52         showit |= g.vis;
53     }
54     if (M_AP_TYPE(magr)) {
55         seemimic(magr);
56         showit |= g.vis;
57     } else if (magr->mundetected) {
58         magr->mundetected = 0;
59         showit |= g.vis;
60     }
61 
62     if (g.vis) {
63         if (!canspotmon(magr))
64             map_invisible(magr->mx, magr->my);
65         else if (showit)
66             newsym(magr->mx, magr->my);
67         if (!canspotmon(mdef))
68             map_invisible(mdef->mx, mdef->my);
69         else if (showit)
70             newsym(mdef->mx, mdef->my);
71     }
72 }
73 
74 DISABLE_WARNING_FORMAT_NONLITERAL
75 
76 static
77 void
missmm(register struct monst * magr,register struct monst * mdef,struct attack * mattk)78 missmm(register struct monst *magr, register struct monst *mdef,
79        struct attack *mattk)
80 {
81     const char *fmt;
82     char buf[BUFSZ];
83 
84     pre_mm_attack(magr, mdef);
85 
86     if (g.vis) {
87         fmt = (could_seduce(magr, mdef, mattk) && !magr->mcan)
88                   ? "%s pretends to be friendly to"
89                   : "%s misses";
90         Sprintf(buf, fmt, Monnam(magr));
91         pline("%s %s.", buf, mon_nam_too(mdef, magr));
92     } else
93         noises(magr, mattk);
94 }
95 
96 RESTORE_WARNING_FORMAT_NONLITERAL
97 
98 /*
99  *  fightm()  -- fight some other monster
100  *
101  *  Returns:
102  *      0 - Monster did nothing.
103  *      1 - If the monster made an attack.  The monster might have died.
104  *
105  *  There is an exception to the above.  If mtmp has the hero swallowed,
106  *  then we report that the monster did nothing so it will continue to
107  *  digest the hero.
108  */
109  /* have monsters fight each other */
110 int
fightm(register struct monst * mtmp)111 fightm(register struct monst *mtmp)
112 {
113     register struct monst *mon, *nmon;
114     int result, has_u_swallowed;
115 #ifdef LINT
116     nmon = 0;
117 #endif
118     /* perhaps the monster will resist Conflict */
119     if (resist(mtmp, RING_CLASS, 0, 0))
120         return 0;
121 
122     if (u.ustuck == mtmp) {
123         /* perhaps we're holding it... */
124         if (itsstuck(mtmp))
125             return 0;
126     }
127     has_u_swallowed = (u.uswallow && (mtmp == u.ustuck));
128 
129     for (mon = fmon; mon; mon = nmon) {
130         nmon = mon->nmon;
131         if (nmon == mtmp)
132             nmon = mtmp->nmon;
133         /* Be careful to ignore monsters that are already dead, since we
134          * might be calling this before we've cleaned them up.  This can
135          * happen if the monster attacked a cockatrice bare-handedly, for
136          * instance.
137          */
138         if (mon != mtmp && !DEADMONSTER(mon)) {
139             if (monnear(mtmp, mon->mx, mon->my)) {
140                 if (!u.uswallow && (mtmp == u.ustuck)) {
141                     if (!rn2(4)) {
142                         set_ustuck((struct monst *) 0);
143                         pline("%s releases you!", Monnam(mtmp));
144                     } else
145                         break;
146                 }
147 
148                 /* mtmp can be killed */
149                 g.bhitpos.x = mon->mx;
150                 g.bhitpos.y = mon->my;
151                 g.notonhead = 0;
152                 result = mattackm(mtmp, mon);
153 
154                 if (result & MM_AGR_DIED)
155                     return 1; /* mtmp died */
156                 /*
157                  * If mtmp has the hero swallowed, lie and say there
158                  * was no attack (this allows mtmp to digest the hero).
159                  */
160                 if (has_u_swallowed)
161                     return 0;
162 
163                 /* Allow attacked monsters a chance to hit back. Primarily
164                  * to allow monsters that resist conflict to respond.
165                  */
166                 if ((result & MM_HIT) && !(result & MM_DEF_DIED) && rn2(4)
167                     && mon->movement >= NORMAL_SPEED) {
168                     mon->movement -= NORMAL_SPEED;
169                     g.notonhead = 0;
170                     (void) mattackm(mon, mtmp); /* return attack */
171                 }
172 
173                 return (result & MM_HIT) ? 1 : 0;
174             }
175         }
176     }
177     return 0;
178 }
179 
180 /*
181  * mdisplacem() -- attacker moves defender out of the way;
182  *                 returns same results as mattackm().
183  */
184 int
mdisplacem(register struct monst * magr,register struct monst * mdef,boolean quietly)185 mdisplacem(register struct monst *magr, register struct monst *mdef,
186            boolean quietly)
187 {
188     struct permonst *pa, *pd;
189     int tx, ty, fx, fy;
190 
191     fuzl_p2("mdisplacem", "magr", magr, "mdef", mdef);
192     /* sanity checks; could matter if we unexpectedly get a long worm */
193     if (!magr || !mdef || magr == mdef)
194         return MM_MISS;
195     pa = magr->data, pd = mdef->data;
196     tx = mdef->mx, ty = mdef->my; /* destination */
197     fx = magr->mx, fy = magr->my; /* current location */
198     if (m_at(fx, fy) != magr || m_at(tx, ty) != mdef)
199         return MM_MISS;
200 
201     /* The 1 in 7 failure below matches the chance in do_attack()
202      * for pet displacement.
203      */
204     if (!rn2(7))
205         return MM_MISS;
206 
207     /* Grid bugs cannot displace at an angle. */
208     if (pa == &mons[PM_GRID_BUG] && magr->mx != mdef->mx
209         && magr->my != mdef->my)
210         return MM_MISS;
211 
212     /* undetected monster becomes un-hidden if it is displaced */
213     if (mdef->mundetected)
214         mdef->mundetected = 0;
215     /* wake up the displaced defender */
216     wakeup(mdef, FALSE, TRUE);
217     mdef->mstrategy &= ~STRAT_WAITMASK;
218 
219     /*
220      * Set up the visibility of action.
221      * You can observe monster displacement if you can see both of
222      * the monsters involved.
223      */
224     g.vis = (canspotmon(magr) && canspotmon(mdef));
225 
226     if (touch_petrifies(pd) && !resists_ston(magr)) {
227         if (which_armor(magr, W_ARMG) != 0) {
228             if (poly_when_stoned(pa)) {
229                 mon_to_stone(magr);
230                 return MM_HIT; /* no damage during the polymorph */
231             }
232             if (!quietly && canspotmon(magr))
233                 pline("%s turns to stone!", Monnam(magr));
234             monstone(magr);
235             if (!DEADMONSTER(magr))
236                 return MM_HIT; /* lifesaved */
237             else if (magr->mtame && !g.vis)
238                 You(brief_feeling, "peculiarly sad");
239             return MM_AGR_DIED;
240         }
241     }
242 
243     remove_monster(fx, fy); /* pick up from orig position */
244     remove_monster(tx, ty);
245     place_monster(magr, tx, ty); /* put down at target spot */
246     place_monster(mdef, fx, fy);
247     /* the monster that moves can decide to hide in its new spot; the displaced
248      * monster is forced out of hiding even if it can hide in its new spot */
249     if (hides_under(magr->data)) {
250         hideunder(magr);
251     }
252     mdef->mundetected = 0;
253     if (g.vis && !quietly)
254         pline("%s moves %s out of %s way!", Monnam(magr), mon_nam(mdef),
255               is_rider(pa) ? "the" : mhis(magr));
256     newsym(fx, fy);  /* see it */
257     newsym(tx, ty);  /*   all happen */
258     flush_screen(0); /* make sure it shows up */
259 
260     return MM_HIT;
261 }
262 
263 /*
264  * mattackm() -- a monster attacks another monster.
265  *
266  *          --------- aggressor died
267  *         /  ------- defender died
268  *        /  /  ----- defender was hit
269  *       /  /  /
270  *      x  x  x
271  *
272  *      0x8     MM_AGR_DONE
273  *      0x4     MM_AGR_DIED
274  *      0x2     MM_DEF_DIED
275  *      0x1     MM_HIT
276  *      0x0     MM_MISS
277  *
278  * Each successive attack has a lower probability of hitting.  Some rely on
279  * success of previous attacks.  ** this doen't seem to be implemented -dl **
280  *
281  * In the case of exploding monsters, the monster dies as well.
282  */
283 int
mattackm(register struct monst * magr,register struct monst * mdef)284 mattackm(register struct monst *magr, register struct monst *mdef)
285 {
286     int i,          /* loop counter */
287         tmp,        /* amour class difference */
288         strike = 0, /* hit this attack */
289         attk,       /* attack attempted this time */
290         struck = 0, /* hit at least once */
291         res[NATTK], /* results of all attacks */
292         dieroll = 0,
293         saved_mhp = (mdef ? mdef->mhp : 0); /* for print_mon_wounded() */
294     struct attack *mattk, alt_attk;
295     struct obj *mwep;
296     struct permonst *pa, *pd;
297 
298     if (!magr || !mdef)
299         return MM_MISS; /* mike@genat */
300     if (!magr->mcanmove || magr->msleeping)
301         return MM_MISS;
302     pa = magr->data;
303     pd = mdef->data;
304 
305     /* Grid bugs cannot attack at an angle. */
306     if (pa == &mons[PM_GRID_BUG] && magr->mx != mdef->mx
307         && magr->my != mdef->my)
308         return MM_MISS;
309 
310     /* Calculate the armour class differential. */
311     tmp = find_mac(mdef) + magr->m_lev;
312     if (mdef->mconf || !mdef->mcanmove || mdef->msleeping) {
313         tmp += 4;
314         wakeup(mdef, FALSE, TRUE);
315     }
316 
317     /* undetect monsters become un-hidden if they are attacked */
318     if (mdef->mundetected) {
319         mdef->mundetected = 0;
320         newsym(mdef->mx, mdef->my);
321         if (canseemon(mdef) && !sensemon(mdef)) {
322             if (Unaware) {
323                 boolean justone = (mdef->data->geno & G_UNIQ) != 0L;
324                 const char *montype;
325 
326                 montype = noname_monnam(mdef, justone ? ARTICLE_THE
327                                                       : ARTICLE_NONE);
328                 if (!justone)
329                     montype = makeplural(montype);
330                 You("dream of %s.", montype);
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     g.vis = ((cansee(magr->mx, magr->my) && canspotmon(magr))
342              || (cansee(mdef->mx, mdef->my) && 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 = g.monstermoves;
350 
351     /* controls whether a mind flayer uses all of its tentacle-for-DRIN
352        attacks; when fighting a headless monster, stop after the first
353        one because repeating the same failing hit (or even an ordinary
354        tentacle miss) is very verbose and makes the flayer look stupid */
355     g.skipdrin = FALSE;
356 
357     /* Now perform all attacks for the monster. */
358     for (i = 0; i < NATTK; i++) {
359         res[i] = MM_MISS;
360         mattk = getmattk(magr, mdef, i, res, &alt_attk);
361         mwep = (struct obj *) 0;
362         attk = 1;
363         /* reduce verbosity for mind flayer attacking creature without a
364            head (or worm's tail); this is similar to monster with multiple
365            attacks after a wildmiss against displaced or invisible hero */
366         if (g.skipdrin && mattk->aatyp == AT_TENT && mattk->adtyp == AD_DRIN)
367             continue;
368 
369         switch (mattk->aatyp) {
370         case AT_WEAP: /* "hand to hand" attacks */
371             if (distmin(magr->mx, magr->my, mdef->mx, mdef->my) > 1) {
372                 /* D: Do a ranged attack here! */
373                 strike = (thrwmm(magr, mdef) == MM_MISS) ? 0 : 1;
374                 if (strike)
375                     /* don't really know if we hit or not; pretend we did */
376                     res[i] |= MM_HIT;
377                 if (DEADMONSTER(mdef))
378                     res[i] = MM_DEF_DIED;
379                 if (DEADMONSTER(magr))
380                     res[i] |= MM_AGR_DIED;
381                 break;
382             }
383             if (magr->weapon_check == NEED_WEAPON || !MON_WEP(magr)) {
384                 magr->weapon_check = NEED_HTH_WEAPON;
385                 if (mon_wield_item(magr) != 0)
386                     return MM_MISS;
387             }
388             possibly_unwield(magr, FALSE);
389             if ((mwep = MON_WEP(magr)) != 0) {
390                 if (g.vis)
391                     mswingsm(magr, mdef, mwep);
392                 tmp += hitval(mwep, mdef);
393             }
394             /*FALLTHRU*/
395         case AT_CLAW:
396         case AT_KICK:
397         case AT_BITE:
398         case AT_STNG:
399         case AT_TUCH:
400         case AT_BUTT:
401         case AT_TENT:
402             /* Nymph that teleported away on first attack? */
403             if (distmin(magr->mx, magr->my, mdef->mx, mdef->my) > 1)
404                 /* Continue because the monster may have a ranged attack. */
405                 continue;
406             /* Monsters won't attack cockatrices physically if they
407              * have a weapon instead.  This instinct doesn't work for
408              * players, or under conflict or confusion.
409              */
410             if (!magr->mconf && !Conflict && mwep && mattk->aatyp != AT_WEAP
411                 && touch_petrifies(mdef->data)) {
412                 strike = 0;
413                 break;
414             }
415             dieroll = rnd(20 + i);
416             strike = (tmp > dieroll);
417             /* KMH -- don't accumulate to-hit bonuses */
418             if (mwep)
419                 tmp -= hitval(mwep, mdef);
420             if (strike) {
421                 res[i] = hitmm(magr, mdef, mattk, mwep, dieroll);
422                 if ((mdef->data == &mons[PM_BLACK_PUDDING]
423                      || mdef->data == &mons[PM_BROWN_PUDDING])
424                     && (mwep && (mwep->material == IRON
425                                  || mwep->material == METAL))
426                     && mdef->mhp > 1 && !mdef->mcan) {
427                     struct monst *mclone;
428 
429                     if ((mclone = clone_mon(mdef, 0, 0)) != 0) {
430                         if (g.vis && canspotmon(mdef)) {
431                             char buf[BUFSZ];
432 
433                             Strcpy(buf, Monnam(mdef));
434                             pline("%s divides as %s hits it!", buf,
435                                   mon_nam(magr));
436                         }
437                         mintrap(mclone);
438                     }
439                 }
440             } else
441                 missmm(magr, mdef, mattk);
442             break;
443 
444         case AT_HUGS: /* automatic if prev two attacks succeed */
445             strike = (i >= 2 && res[i - 1] == MM_HIT && res[i - 2] == MM_HIT);
446             if (strike)
447                 res[i] = hitmm(magr, mdef, mattk, (struct obj *) 0, 0);
448 
449             break;
450 
451         case AT_GAZE:
452             strike = 0;
453             res[i] = gazemm(magr, mdef, mattk);
454             break;
455 
456         case AT_EXPL:
457             /* D: Prevent explosions from a distance */
458             if (distmin(magr->mx,magr->my,mdef->mx,mdef->my) > 1)
459                 continue;
460 
461             res[i] = explmm(magr, mdef, mattk);
462             if (res[i] == MM_MISS) { /* cancelled--no attack */
463                 strike = 0;
464                 attk = 0;
465             } else
466                 strike = 1; /* automatic hit */
467             break;
468 
469         case AT_ENGL:
470             if (mdef->data == &mons[PM_SHADE]) { /* no silver teeth... */
471                 if (g.vis)
472                     pline("%s attempt to engulf %s is futile.",
473                           s_suffix(Monnam(magr)), mon_nam(mdef));
474                 strike = 0;
475                 break;
476             }
477             if (u.usteed && mdef == u.usteed) {
478                 strike = 0;
479                 break;
480             }
481             /* D: Prevent engulf from a distance */
482             if (distmin(magr->mx, magr->my, mdef->mx, mdef->my) > 1)
483                 continue;
484             /* Engulfing attacks are directed at the hero if possible. -dlc */
485             if (u.uswallow && magr == u.ustuck)
486                 strike = 0;
487             else if ((strike = (tmp > rnd(20 + i))) != 0)
488                 res[i] = gulpmm(magr, mdef, mattk);
489             else
490                 missmm(magr, mdef, mattk);
491             break;
492 
493         case AT_BREA:
494             if (!monnear(magr, mdef->mx, mdef->my)) {
495                 strike = (breamm(magr, mattk, mdef) == MM_MISS) ? 0 : 1;
496 
497                 /* We don't really know if we hit or not; pretend we did. */
498                 if (strike)
499                     res[i] |= MM_HIT;
500                 if (DEADMONSTER(mdef))
501                     res[i] = MM_DEF_DIED;
502                 if (DEADMONSTER(magr))
503                     res[i] |= MM_AGR_DIED;
504             }
505             else
506                 strike = 0;
507             break;
508 
509         case AT_SPIT:
510             if (!monnear(magr, mdef->mx, mdef->my)) {
511                 strike = (spitmm(magr, mattk, mdef) == MM_MISS) ? 0 : 1;
512 
513                 /* We don't really know if we hit or not; pretend we did. */
514                 if (strike)
515                     res[i] |= MM_HIT;
516                 if (DEADMONSTER(mdef))
517                     res[i] = MM_DEF_DIED;
518                 if (DEADMONSTER(magr))
519                     res[i] |= MM_AGR_DIED;
520             }
521             break;
522 
523         default: /* no attack */
524             strike = 0;
525             attk = 0;
526             break;
527         }
528 
529         if (attk && !(res[i] & MM_AGR_DIED)
530             && distmin(magr->mx, magr->my, mdef->mx, mdef->my) <= 1)
531             res[i] = passivemm(magr, mdef, strike,
532                                (res[i] & MM_DEF_DIED), mwep);
533 
534         if (res[i] & MM_DEF_DIED)
535             return res[i];
536         if (res[i] & MM_AGR_DIED)
537             return res[i];
538         /* return if aggressor can no longer attack */
539         if (!magr->mcanmove || magr->msleeping)
540             return res[i];
541         if (res[i] & MM_HIT)
542             struck = 1; /* at least one hit */
543     }
544     if (struck && mdef->mtame) {
545         print_mon_wounded(mdef, saved_mhp);
546     }
547 
548     return (struck ? MM_HIT : MM_MISS);
549 }
550 
551 /* Returns the result of mdamagem(). */
552 static int
hitmm(register struct monst * magr,register struct monst * mdef,struct attack * mattk,struct obj * mwep,int dieroll)553 hitmm(register struct monst *magr, register struct monst *mdef,
554       struct attack *mattk, struct obj *mwep, int dieroll)
555 {
556     boolean weaponhit = ((mattk->aatyp == AT_WEAP
557                           || (mattk->aatyp == AT_CLAW && mwep)));
558     pre_mm_attack(magr, mdef);
559 
560     /* Possibly awaken nearby monsters */
561     if ((!is_silent(magr->data) || !helpless(mdef)) && rn2(10)) {
562         wake_nearto(magr->mx, magr->my, combat_noise(magr->data));
563     }
564 
565     if (g.vis) {
566         int compat;
567         char buf[BUFSZ];
568 
569         if ((compat = could_seduce(magr, mdef, mattk)) && !magr->mcan) {
570             Sprintf(buf, "%s %s", Monnam(magr),
571                     mdef->mcansee ? "smiles at" : "talks to");
572             pline("%s %s %s.", buf, mon_nam(mdef),
573                   compat == 2 ? "engagingly" : "seductively");
574         } else if (shade_miss(magr, mdef, mwep, FALSE, TRUE)) {
575             return MM_MISS; /* bypass mdamagem() */
576         } else {
577             char magr_name[BUFSZ];
578 
579             Strcpy(magr_name, Monnam(magr));
580             buf[0] = '\0';
581             switch (mattk->aatyp) {
582             case AT_BITE:
583                 Snprintf(buf, sizeof(buf), "%s bites", magr_name);
584                 break;
585             case AT_STNG:
586                 Snprintf(buf, sizeof(buf), "%s stings", magr_name);
587                 break;
588             case AT_BUTT:
589                 Snprintf(buf, sizeof(buf), "%s butts", magr_name);
590                 break;
591             case AT_TUCH:
592                 Snprintf(buf, sizeof(buf), "%s touches", magr_name);
593                 break;
594             case AT_TENT:
595                 Snprintf(buf, sizeof(buf), "%s tentacles suck", s_suffix(magr_name));
596                 break;
597             case AT_HUGS:
598                 if (magr != u.ustuck) {
599                     Snprintf(buf, sizeof(buf), "%s squeezes", magr_name);
600                     break;
601                 }
602                 /*FALLTHRU*/
603             default:
604                 if (!weaponhit || !mwep || !mwep->oartifact)
605                     Snprintf(buf, sizeof(buf), "%s hits", magr_name);
606                 break;
607             }
608             if (*buf)
609                 pline("%s %s.", buf, mon_nam_too(mdef, magr));
610         }
611     } else
612         noises(magr, mattk);
613 
614     return mdamagem(magr, mdef, mattk, mwep, dieroll);
615 }
616 
617 /* Returns the same values as mdamagem(). */
618 static int
gazemm(struct monst * magr,struct monst * mdef,struct attack * mattk)619 gazemm(struct monst *magr, struct monst *mdef, struct attack *mattk)
620 {
621     char buf[BUFSZ];
622     /* an Archon's gaze affects target even if Archon itself is blinded */
623     boolean archon = (magr->data == &mons[PM_ARCHON]
624                       && mattk->adtyp == AD_BLND),
625             altmesg = (archon && !magr->mcansee);
626 
627     if (g.vis) {
628         if (mdef->data->mlet == S_MIMIC && M_AP_TYPE(mdef) != M_AP_NOTHING)
629             seemimic(mdef);
630         Sprintf(buf, "%s gazes %s",
631                 altmesg ? Adjmonnam(magr, "blinded") : Monnam(magr),
632                 altmesg ? "toward" : "at");
633         pline("%s %s...", buf,
634               canspotmon(mdef) ? mon_nam(mdef) : "something");
635     }
636 
637     if (magr->mcan || !mdef->mcansee
638         || (archon ? resists_blnd(mdef) : !magr->mcansee)
639         || (magr->minvis && !perceives(mdef->data)) || mdef->msleeping) {
640         if (g.vis && canspotmon(mdef))
641             pline("but nothing happens.");
642         return MM_MISS;
643     }
644     /* call mon_reflects 2x, first test, then, if visible, print message */
645     if (magr->data == &mons[PM_MEDUSA] && mon_reflects(mdef, (char *) 0)) {
646         if (canseemon(mdef))
647             (void) mon_reflects(mdef, "The gaze is reflected away by %s %s.");
648         if (mdef->mcansee) {
649             if (mon_reflects(magr, (char *) 0)) {
650                 if (canseemon(magr))
651                     (void) mon_reflects(magr,
652                                       "The gaze is reflected away by %s %s.");
653                 return MM_MISS;
654             }
655             if (mdef->minvis && !perceives(magr->data)) {
656                 if (canseemon(magr)) {
657                     pline(
658                       "%s doesn't seem to notice that %s gaze was reflected.",
659                           Monnam(magr), mhis(magr));
660                 }
661                 return MM_MISS;
662             }
663             if (canseemon(magr))
664                 pline("%s is turned to stone!", Monnam(magr));
665             monstone(magr);
666             if (!DEADMONSTER(magr))
667                 return MM_MISS;
668             return MM_AGR_DIED;
669         }
670     } else if (archon) {
671         mhitm_ad_blnd(magr, mattk, mdef, (struct mhitm_data *) 0);
672         /* an Archon's blinding radiance also stuns;
673            this is different from the way the hero gets stunned because
674            a stunned monster recovers randomly instead of via countdown;
675            both cases make an effort to prevent the target from being
676            continuously stunned due to repeated gaze attacks */
677         if (rn2(2))
678             mdef->mstun = 1;
679     }
680 
681     return mdamagem(magr, mdef, mattk, (struct obj *) 0, 0);
682 }
683 
684 /* return True if magr is allowed to swallow mdef, False otherwise */
685 boolean
engulf_target(struct monst * magr,struct monst * mdef)686 engulf_target(struct monst *magr, struct monst *mdef)
687 {
688     struct rm *lev;
689     int dx, dy;
690 
691     /* can't swallow something that's too big */
692     if (mdef->data->msize >= MZ_HUGE)
693         return FALSE;
694 
695     /* (hypothetical) engulfers who can pass through walls aren't
696      limited by rock|trees|bars */
697     if ((magr == &g.youmonst) ? Passes_walls : passes_walls(magr->data))
698         return TRUE;
699 
700     /* don't swallow something in a spot where attacker wouldn't
701        otherwise be able to move onto; we don't want to engulf
702        a wall-phaser and end up with a non-phaser inside a wall */
703     dx = mdef->mx, dy = mdef->my;
704     if (mdef == &g.youmonst)
705         dx = u.ux, dy = u.uy;
706     lev = &levl[dx][dy];
707     if (IS_ROCK(lev->typ) || closed_door(dx, dy) || IS_TREE(lev->typ)
708         /* not passes_bars(); engulfer isn't squeezing through */
709         || (lev->typ == IRONBARS && !is_whirly(magr->data)))
710         return FALSE;
711 
712     return TRUE;
713 }
714 
715 /* Returns the same values as mattackm(). */
716 static int
gulpmm(register struct monst * magr,register struct monst * mdef,register struct attack * mattk)717 gulpmm(register struct monst *magr, register struct monst *mdef,
718        register struct attack *mattk)
719 {
720     xchar ax, ay, dx, dy;
721     int status;
722     char buf[BUFSZ];
723     struct obj *obj;
724 
725     if (!engulf_target(magr, mdef))
726         return MM_MISS;
727 
728     if (g.vis) {
729         /* [this two-part formatting dates back to when only one x_monnam
730            result could be included in an expression because the next one
731            would overwrite first's result -- that's no longer the case] */
732         Sprintf(buf, "%s swallows", Monnam(magr));
733         pline("%s %s.", buf, mon_nam(mdef));
734     }
735     for (obj = mdef->minvent; obj; obj = obj->nobj)
736         (void) snuff_lit(obj);
737 
738     if (is_vampshifter(mdef)
739         && newcham(mdef, &mons[mdef->cham], FALSE, FALSE)) {
740         if (g.vis) {
741             /* 'it' -- previous form is no longer available and
742                using that would be excessively verbose */
743             pline("%s expels %s.", Monnam(magr),
744                   canspotmon(mdef) ? "it" : something);
745             if (canspotmon(mdef))
746                 pline("It turns into %s.", a_monnam(mdef));
747         }
748         return MM_HIT; /* bypass mdamagem() */
749     }
750 
751     /*
752      *  All of this manipulation is needed to keep the display correct.
753      *  There is a flush at the next pline().
754      */
755     ax = magr->mx;
756     ay = magr->my;
757     dx = mdef->mx;
758     dy = mdef->my;
759     /*
760      *  Leave the defender in the monster chain at it's current position,
761      *  but don't leave it on the screen.  Move the aggressor to the
762      *  defender's position.
763      */
764     remove_monster(dx, dy);
765     remove_monster(ax, ay);
766     place_monster(magr, dx, dy);
767     newsym(ax, ay); /* erase old position */
768     newsym(dx, dy); /* update new position */
769 
770     status = mdamagem(magr, mdef, mattk, (struct obj *) 0, 0);
771 
772     if ((status & (MM_AGR_DIED | MM_DEF_DIED))
773         == (MM_AGR_DIED | MM_DEF_DIED)) {
774         ;                              /* both died -- do nothing  */
775     } else if (status & MM_DEF_DIED) { /* defender died */
776         /*
777          *  Note:  remove_monster() was called in relmon(), wiping out
778          *  magr from level.monsters[mdef->mx][mdef->my].  We need to
779          *  put it back and display it.  -kd
780          */
781         if (!goodpos(dx, dy, magr, MM_IGNOREWATER))
782             dx = ax, dy = ay;
783         place_monster(magr, dx, dy);
784         newsym(dx, dy);
785         /* aggressor moves to <dx,dy> and might encounter trouble there */
786         if (minliquid(magr) || (t_at(dx, dy) && mintrap(magr) == 2))
787             status |= MM_AGR_DIED;
788     } else if (status & MM_AGR_DIED) { /* aggressor died */
789         place_monster(mdef, dx, dy);
790         newsym(dx, dy);
791     } else {                           /* both alive, put them back */
792         if (cansee(dx, dy))
793             pline("%s is regurgitated!", Monnam(mdef));
794 
795         remove_monster(dx,dy);
796         place_monster(magr, ax, ay);
797         place_monster(mdef, dx, dy);
798         newsym(ax, ay);
799         newsym(dx, dy);
800     }
801 
802     return status;
803 }
804 
805 static int
explmm(struct monst * magr,struct monst * mdef,struct attack * mattk)806 explmm(struct monst *magr, struct monst *mdef, struct attack *mattk)
807 {
808     int result;
809 
810     if (magr->mcan)
811         return MM_MISS;
812 
813     if (cansee(magr->mx, magr->my))
814         pline("%s explodes!", Monnam(magr));
815     else
816         noises(magr, mattk);
817 
818     /* monster explosion types which actually create an explosion */
819     if (mattk->adtyp == AD_FIRE || mattk->adtyp == AD_COLD
820         || mattk->adtyp == AD_ELEC) {
821         mon_explodes(magr, mattk);
822         /* unconditionally set AGR_DIED here; lifesaving is accounted below */
823         result = MM_AGR_DIED | (DEADMONSTER(mdef) ? MM_DEF_DIED : 0);
824     }
825     else {
826         result = mdamagem(magr, mdef, mattk, (struct obj *) 0, 0);
827     }
828 
829     /* Kill off aggressor if it didn't die. */
830     if (!(result & MM_AGR_DIED)) {
831         boolean was_leashed = (magr->mleashed != 0);
832 
833         mondead(magr);
834         if (!DEADMONSTER(magr))
835             return result; /* life saved */
836         result |= MM_AGR_DIED;
837 
838         /* mondead() -> m_detach() -> m_unleash() always suppresses
839            the m_unleash() slack message, so deliver it here instead */
840         if (was_leashed)
841             Your("leash falls slack.");
842     }
843     if (magr->mtame) /* give this one even if it was visible */
844         You(brief_feeling, "melancholy");
845 
846     return result;
847 }
848 
849 /*
850  *  See comment at top of mattackm(), for return values.
851  */
852 static int
mdamagem(struct monst * magr,struct monst * mdef,struct attack * mattk,struct obj * mwep,int dieroll)853 mdamagem(struct monst *magr, struct monst *mdef,
854          struct attack *mattk, struct obj *mwep, int dieroll)
855 {
856     struct permonst *pa = magr->data, *pd = mdef->data;
857     struct mhitm_data mhm;
858     mhm.damage = d((int) mattk->damn, (int) mattk->damd);
859     mhm.hitflags = MM_MISS;
860     mhm.permdmg = 0;
861     mhm.specialdmg = 0;
862     mhm.dieroll = dieroll;
863     mhm.done = FALSE;
864 
865     if ((touch_petrifies(pd) /* or flesh_petrifies() */
866          || (mattk->adtyp == AD_DGST && pd == &mons[PM_MEDUSA]))
867         && !resists_ston(magr)) {
868         long protector = attk_protection((int) mattk->aatyp),
869              wornitems = magr->misc_worn_check;
870 
871         /* wielded weapon gives same protection as gloves here */
872         if (mwep)
873             wornitems |= W_ARMG;
874 
875         if (protector == 0L
876             || (protector != ~0L && (wornitems & protector) != protector)) {
877             if (poly_when_stoned(pa)) {
878                 mon_to_stone(magr);
879                 return MM_HIT; /* no damage during the polymorph */
880             }
881             if (g.vis && canspotmon(magr))
882                 pline("%s turns to stone!", Monnam(magr));
883             monstone(magr);
884             if (!DEADMONSTER(magr))
885                 return MM_HIT; /* lifesaved */
886             else if (magr->mtame && !g.vis)
887                 You(brief_feeling, "peculiarly sad");
888             return MM_AGR_DIED;
889         }
890     }
891 
892     /* check for special damage sources (e.g. hated material) */
893     long armask = attack_contact_slots(magr, mattk->aatyp);
894     struct obj* hated_obj;
895     mhm.damage += special_dmgval(magr, mdef, armask, &hated_obj);
896     if (hated_obj) {
897         searmsg(magr, mdef, hated_obj, FALSE);
898     }
899 
900     mhitm_adtyping(magr, mattk, mdef, &mhm);
901     if (mhm.done)
902         return mhm.hitflags;
903 
904     if (!mhm.damage)
905         return mhm.hitflags;
906 
907     if ((mdef->mhp -= mhm.damage) < 1) {
908         if (m_at(mdef->mx, mdef->my) == magr) { /* see gulpmm() */
909             remove_monster(mdef->mx, mdef->my);
910             mdef->mhp = 1; /* otherwise place_monster will complain */
911             place_monster(mdef, mdef->mx, mdef->my);
912             mdef->mhp = 0;
913         }
914         if (mattk->aatyp == AT_WEAP || mattk->aatyp == AT_CLAW)
915             g.mkcorpstat_norevive = troll_baned(mdef, mwep) ? TRUE : FALSE;
916         /* Determine if we should be turning the dying monster into a zombie. */
917         if (!mwep && zombie_maker(magr)
918             && ((mattk->aatyp == AT_TUCH
919                  || mattk->aatyp == AT_CLAW
920                  || mattk->aatyp == AT_BITE)
921                 && zombie_form(mdef->data) != NON_PM)) {
922             g.zombify = magr->mtame ? ZOMBIFY_TAME : ZOMBIFY_HOSTILE;
923         }
924         monkilled(mdef, "", (int) mattk->adtyp);
925         g.zombify = 0; /* reset */
926         g.mkcorpstat_norevive = FALSE;
927         if (!DEADMONSTER(mdef))
928             return mhm.hitflags; /* mdef lifesaved */
929         else if (mhm.hitflags == MM_AGR_DIED)
930             return (MM_DEF_DIED | MM_AGR_DIED);
931 
932         if (mattk->adtyp == AD_DGST) {
933             /* various checks similar to dog_eat and meatobj.
934              * after monkilled() to provide better message ordering */
935             if (mdef->cham >= LOW_PM) {
936                 (void) newcham(magr, (struct permonst *) 0, FALSE, TRUE);
937             } else if (pd == &mons[PM_GREEN_SLIME] && !slimeproof(pa)) {
938                 (void) newcham(magr, &mons[PM_GREEN_SLIME], FALSE, TRUE);
939             } else if (pd == &mons[PM_WRAITH]) {
940                 (void) grow_up(magr, (struct monst *) 0);
941                 /* don't grow up twice */
942                 return (MM_DEF_DIED | (!DEADMONSTER(magr) ? 0 : MM_AGR_DIED));
943             } else if (pd == &mons[PM_NURSE]) {
944                 magr->mhp = magr->mhpmax;
945             }
946         }
947         /* caveat: above digestion handling doesn't keep `pa' up to date */
948 
949         return (MM_DEF_DIED | (grow_up(magr, mdef) ? 0 : MM_AGR_DIED));
950     }
951     return (mhm.hitflags == MM_AGR_DIED) ? MM_AGR_DIED : MM_HIT;
952 }
953 
954 int
mon_poly(struct monst * magr,struct monst * mdef,int dmg)955 mon_poly(struct monst *magr, struct monst *mdef, int dmg)
956 {
957     static const char freaky[] = " undergoes a freakish metamorphosis";
958 
959     if (mdef == &g.youmonst) {
960         if (Antimagic) {
961             shieldeff(u.ux, u.uy);
962         } else if (Unchanging) {
963             ; /* just take a little damage */
964         } else {
965             /* system shock might take place in polyself() */
966             if (u.ulycn == NON_PM) {
967                 You("are subjected to a freakish metamorphosis.");
968                 polyself(0);
969             } else if (u.umonnum != u.ulycn) {
970                 You_feel("an unnatural urge coming on.");
971                 you_were();
972             } else {
973                 You_feel("a natural urge coming on.");
974                 you_unwere(FALSE);
975             }
976             dmg = 0;
977         }
978     } else {
979         char Before[BUFSZ];
980 
981         Strcpy(Before, Monnam(mdef));
982         if (resists_magm(mdef)) {
983             /* Magic resistance */
984             if (g.vis)
985                 shieldeff(mdef->mx, mdef->my);
986         } else if (resist(mdef, WAND_CLASS, 0, TELL)) {
987             /* general resistance to magic... */
988             ;
989         } else if (!rn2(25) && mdef->cham == NON_PM
990                    && (mdef->mcan
991                        || pm_to_cham(monsndx(mdef->data)) != NON_PM)) {
992             /* system shock; this variation takes away half of mon's HP
993                rather than kill outright */
994             if (g.vis)
995                 pline("%s shudders!", Before);
996 
997             dmg += (mdef->mhpmax + 1) / 2;
998             mdef->mhp -= dmg;
999             dmg = 0;
1000             if (DEADMONSTER(mdef)) {
1001                 if (magr == &g.youmonst)
1002                     xkilled(mdef, XKILL_GIVEMSG | XKILL_NOCORPSE);
1003                 else
1004                     monkilled(mdef, "", AD_RBRE);
1005             }
1006         } else if (newcham(mdef, (struct permonst *) 0, FALSE, FALSE)) {
1007             if (g.vis) { /* either seen or adjacent */
1008                 boolean was_seen = !!strcmpi("It", Before),
1009                         verbosely = flags.verbose || !was_seen;
1010 
1011                 if (canspotmon(mdef))
1012                     pline("%s%s%s turns into %s.", Before,
1013                           verbosely ? freaky : "", verbosely ? " and" : "",
1014                           x_monnam(mdef, ARTICLE_A, (char *) 0,
1015                                    (SUPPRESS_NAME | SUPPRESS_IT
1016                                     | SUPPRESS_INVISIBLE), FALSE));
1017                 else if (was_seen || magr == &g.youmonst)
1018                     pline("%s%s%s.", Before, freaky,
1019                           !was_seen ? "" : " and disappears");
1020             }
1021             dmg = 0;
1022             if (can_teleport(magr->data)) {
1023                 if (magr == &g.youmonst)
1024                     tele();
1025                 else if (!tele_restrict(magr))
1026                     (void) rloc(magr, TRUE);
1027             }
1028         } else {
1029             if (g.vis && flags.verbose)
1030                 pline1(nothing_happens);
1031         }
1032     }
1033     return dmg;
1034 }
1035 
1036 void
paralyze_monst(struct monst * mon,int amt)1037 paralyze_monst(struct monst *mon, int amt)
1038 {
1039     if (amt > 127)
1040         amt = 127;
1041 
1042     mon->mcanmove = 0;
1043     mon->mfrozen = amt;
1044     mon->meating = 0; /* terminate any meal-in-progress */
1045     mon->mstrategy &= ~STRAT_WAITFORU;
1046 }
1047 
1048 /* `mon' is hit by a sleep attack; return 1 if it's affected, 0 otherwise */
1049 int
sleep_monst(struct monst * mon,int amt,int how)1050 sleep_monst(struct monst *mon, int amt, int how)
1051 {
1052     if (resists_sleep(mon)
1053         || (how >= 0 && resist(mon, (char) how, 0, NOTELL))) {
1054         shieldeff(mon->mx, mon->my);
1055     } else if (mon->mcanmove) {
1056         finish_meating(mon); /* terminate any meal-in-progress */
1057         amt += (int) mon->mfrozen;
1058         if (amt > 0) { /* sleep for N turns */
1059             mon->mcanmove = 0;
1060             mon->mfrozen = min(amt, 127);
1061         } else { /* sleep until awakened */
1062             mon->msleeping = 1;
1063         }
1064         return 1;
1065     }
1066     return 0;
1067 }
1068 
1069 /* sleeping grabber releases, engulfer doesn't; don't use for paralysis! */
1070 void
slept_monst(struct monst * mon)1071 slept_monst(struct monst *mon)
1072 {
1073     if ((mon->msleeping || !mon->mcanmove) && mon == u.ustuck
1074         && !sticks(g.youmonst.data) && !u.uswallow) {
1075         pline("%s grip relaxes.", s_suffix(Monnam(mon)));
1076         unstuck(mon);
1077     }
1078 }
1079 
1080 void
rustm(struct monst * mdef,struct obj * obj)1081 rustm(struct monst *mdef, struct obj *obj)
1082 {
1083     int dmgtyp = -1, chance = 1;
1084 
1085     if (!mdef || !obj)
1086         return; /* just in case */
1087     /* AD_ACID and AD_ENCH are handled in passivemm() and passiveum() */
1088     if (dmgtype(mdef->data, AD_CORR)) {
1089         dmgtyp = ERODE_CORRODE;
1090     } else if (dmgtype(mdef->data, AD_RUST)) {
1091         dmgtyp = ERODE_RUST;
1092     } else if (dmgtype(mdef->data, AD_FIRE)
1093                /* steam vortex: fire resist applies, fire damage doesn't */
1094                && mdef->data != &mons[PM_STEAM_VORTEX]) {
1095         dmgtyp = ERODE_BURN;
1096         chance = 6;
1097     }
1098 
1099     if (dmgtyp >= 0 && !rn2(chance))
1100         (void) erode_obj(obj, (char *) 0, dmgtyp, EF_GREASE | EF_VERBOSE);
1101 }
1102 
1103 static void
mswingsm(struct monst * magr,struct monst * mdef,struct obj * otemp)1104 mswingsm(struct monst *magr, struct monst *mdef, struct obj *otemp)
1105 {
1106     if (flags.verbose && !Blind && mon_visible(magr)) {
1107         pline("%s %s %s%s %s at %s.", Monnam(magr),
1108               (objects[otemp->otyp].oc_dir & PIERCE) ? "thrusts" : "swings",
1109               (otemp->quan > 1L) ? "one of " : "", mhis(magr), xname(otemp),
1110               mon_nam(mdef));
1111     }
1112 }
1113 
1114 /*
1115  * Passive responses by defenders.  Does not replicate responses already
1116  * handled above.  Returns same values as mattackm.
1117  */
1118 static int
passivemm(register struct monst * magr,register struct monst * mdef,boolean mhitb,int mdead,struct obj * mwep)1119 passivemm(register struct monst *magr, register struct monst *mdef,
1120           boolean mhitb, int mdead, struct obj *mwep)
1121 {
1122     register struct permonst *mddat = mdef->data;
1123     register struct permonst *madat = magr->data;
1124     char buf[BUFSZ];
1125     int i, tmp;
1126     int mhit = mhitb ? MM_HIT : MM_MISS;
1127 
1128     for (i = 0;; i++) {
1129         if (i >= NATTK)
1130             return (mdead | mhit); /* no passive attacks */
1131         if (mddat->mattk[i].aatyp == AT_NONE)
1132             break;
1133     }
1134     if (mddat->mattk[i].damn)
1135         tmp = d((int) mddat->mattk[i].damn, (int) mddat->mattk[i].damd);
1136     else if (mddat->mattk[i].damd)
1137         tmp = d((int) mddat->mlevel + 1, (int) mddat->mattk[i].damd);
1138     else
1139         tmp = 0;
1140 
1141     /* These affect the enemy even if defender killed */
1142     switch (mddat->mattk[i].adtyp) {
1143     case AD_ACID:
1144         if (mhitb && !rn2(2)) {
1145             Strcpy(buf, Monnam(magr));
1146             if (canseemon(magr))
1147                 pline("%s is splashed by %s %s!", buf,
1148                       s_suffix(mon_nam(mdef)), hliquid("acid"));
1149             if (resists_acid(magr)) {
1150                 if (canseemon(magr))
1151                     pline("%s is not affected.", Monnam(magr));
1152                 tmp = 0;
1153             }
1154         } else
1155             tmp = 0;
1156         if (!rn2(30))
1157             erode_armor(magr, ERODE_CORRODE);
1158         if (!rn2(6))
1159             acid_damage(MON_WEP(magr));
1160         goto assess_dmg;
1161     /* Grudge patch. */
1162     case AD_MAGM:
1163       /* wrath of gods for attacking Oracle */
1164         if(resists_magm(magr)) {
1165             if(canseemon(magr)) {
1166                 shieldeff(magr->mx, magr->my);
1167                 pline("A hail of magic missiles narrowly misses %s!",
1168                     mon_nam(magr));
1169               }
1170         } else {
1171             if(canseemon(magr))
1172                 pline(magr->data == &mons[PM_WOODCHUCK] ? "ZOT!" :
1173                     "%s is hit by magic missiles appearing from thin air!",
1174                     Monnam(magr));
1175             goto assess_dmg;
1176         } break;
1177     case AD_ENCH: /* KMH -- remove enchantment (disenchanter) */
1178         if (mhitb && !mdef->mcan && mwep) {
1179             (void) drain_item(mwep, FALSE);
1180             /* No message */
1181         }
1182         break;
1183     default:
1184         break;
1185     }
1186     if (mdead || mdef->mcan)
1187         return (mdead | mhit);
1188 
1189     /* These affect the enemy only if defender is still alive */
1190     if (rn2(3))
1191         switch (mddat->mattk[i].adtyp) {
1192         case AD_PLYS: /* Floating eye */
1193             if (tmp > 127)
1194                 tmp = 127;
1195             if (mddat == &mons[PM_FLOATING_EYE]) {
1196                 if (!rn2(4))
1197                     tmp = 127;
1198                 if (magr->mcansee && haseyes(madat) && mdef->mcansee
1199                     && (perceives(madat) || !mdef->minvis)) {
1200                     /* construct format string; guard against '%' in Monnam */
1201                     Strcpy(buf, s_suffix(Monnam(mdef)));
1202                     (void) strNsubst(buf, "%", "%%", 0);
1203                     Strcat(buf, " gaze is reflected by %s %s.");
1204                     if (mon_reflects(magr,
1205                                      canseemon(magr) ? buf : (char *) 0))
1206                         return (mdead | mhit);
1207                     Strcpy(buf, Monnam(magr));
1208                     if (canseemon(magr))
1209                         pline("%s is frozen by %s gaze!", buf,
1210                               s_suffix(mon_nam(mdef)));
1211                     paralyze_monst(magr, tmp);
1212                     return (mdead | mhit);
1213                 }
1214             } else { /* gelatinous cube */
1215                 Strcpy(buf, Monnam(magr));
1216                 if (canseemon(magr))
1217                     pline("%s is frozen by %s.", buf, mon_nam(mdef));
1218                 paralyze_monst(magr, tmp);
1219                 return (mdead | mhit);
1220             }
1221             return 1;
1222         case AD_COLD:
1223             if (resists_cold(magr)) {
1224                 if (canseemon(magr)) {
1225                     pline("%s is mildly chilly.", Monnam(magr));
1226                     golemeffects(magr, AD_COLD, tmp);
1227                 }
1228                 tmp = 0;
1229                 break;
1230             }
1231             if (canseemon(magr))
1232                 pline("%s is suddenly very cold!", Monnam(magr));
1233             mdef->mhp += tmp / 2;
1234             if (mdef->mhpmax < mdef->mhp)
1235                 mdef->mhpmax = mdef->mhp;
1236             if (mdef->mhpmax > ((int) (mdef->m_lev + 1) * 8))
1237                 (void) split_mon(mdef, magr);
1238             break;
1239         case AD_STUN:
1240             if (!magr->mstun) {
1241                 magr->mstun = 1;
1242                 if (canseemon(magr))
1243                     pline("%s %s...", Monnam(magr),
1244                           makeplural(stagger(magr->data, "stagger")));
1245             }
1246             tmp = 0;
1247             break;
1248         case AD_FIRE:
1249             if (resists_fire(magr)) {
1250                 if (canseemon(magr)) {
1251                     pline("%s is mildly warmed.", Monnam(magr));
1252                     golemeffects(magr, AD_FIRE, tmp);
1253                 }
1254                 tmp = 0;
1255                 break;
1256             }
1257             if (canseemon(magr))
1258                 pline("%s is suddenly very hot!", Monnam(magr));
1259             break;
1260         case AD_ELEC:
1261             if (resists_elec(magr)) {
1262                 if (canseemon(magr)) {
1263                     pline("%s is mildly tingled.", Monnam(magr));
1264                     golemeffects(magr, AD_ELEC, tmp);
1265                 }
1266                 tmp = 0;
1267                 break;
1268             }
1269             if (canseemon(magr))
1270                 pline("%s is jolted with electricity!", Monnam(magr));
1271             break;
1272         default:
1273             tmp = 0;
1274             break;
1275         }
1276     else
1277         tmp = 0;
1278 
1279  assess_dmg:
1280     if ((magr->mhp -= tmp) <= 0) {
1281         monkilled(magr, "", (int) mddat->mattk[i].adtyp);
1282         return (mdead | mhit | MM_AGR_DIED);
1283     }
1284     return (mdead | mhit);
1285 }
1286 
1287 /* hero or monster has successfully hit target mon with drain energy attack */
1288 void
xdrainenergym(struct monst * mon,boolean givemsg)1289 xdrainenergym(struct monst *mon, boolean givemsg)
1290 {
1291     if (mon->mspec_used < 20 /* limit draining */
1292         && (attacktype(mon->data, AT_MAGC)
1293             || attacktype(mon->data, AT_BREA))) {
1294         mon->mspec_used += d(2, 2);
1295         if (givemsg)
1296             pline("%s seems lethargic.", Monnam(mon));
1297     }
1298 }
1299 
1300 /* "aggressive defense"; what type of armor prevents specified attack
1301    from touching its target? */
1302 long
attk_protection(int aatyp)1303 attk_protection(int aatyp)
1304 {
1305     long w_mask = 0L;
1306 
1307     switch (aatyp) {
1308     case AT_NONE:
1309     case AT_SPIT:
1310     case AT_EXPL:
1311     case AT_BOOM:
1312     case AT_GAZE:
1313     case AT_BREA:
1314     case AT_MAGC:
1315         w_mask = ~0L; /* special case; no defense needed */
1316         break;
1317     case AT_CLAW:
1318     case AT_TUCH:
1319     case AT_WEAP:
1320         w_mask = W_ARMG; /* caller needs to check for weapon */
1321         break;
1322     case AT_KICK:
1323         w_mask = W_ARMF;
1324         break;
1325     case AT_BUTT:
1326         w_mask = W_ARMH;
1327         break;
1328     case AT_HUGS:
1329         w_mask = (W_ARMC | W_ARMG); /* attacker needs both to be protected */
1330         break;
1331     case AT_BITE:
1332     case AT_STNG:
1333     case AT_ENGL:
1334     case AT_TENT:
1335     default:
1336         w_mask = 0L; /* no defense available */
1337         break;
1338     }
1339     return w_mask;
1340 }
1341 
1342 /*mhitm.c*/
1343