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