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