1 /* SCCS Id: @(#)mhitm.c 3.4 2003/01/02 */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
9 extern boolean notonhead;
13 static NEARDATA boolean vis, far_noise;
14 static NEARDATA long noisetime;
15 static NEARDATA struct obj *otmp;
17 static const char brief_feeling[] =
18 "have a %s feeling for a moment, then it passes.";
20 STATIC_DCL char *FDECL(mon_nam_too, (char *,struct monst *,struct monst *));
21 STATIC_DCL void FDECL(mrustm, (struct monst *, struct monst *, struct obj *));
22 STATIC_DCL int FDECL(hitmm, (struct monst *,struct monst *,struct attack *));
23 STATIC_DCL int FDECL(gazemm, (struct monst *,struct monst *,struct attack *));
24 STATIC_DCL int FDECL(gulpmm, (struct monst *,struct monst *,struct attack *));
25 STATIC_DCL int FDECL(explmm, (struct monst *,struct monst *,struct attack *));
26 STATIC_DCL int FDECL(mdamagem, (struct monst *,struct monst *,struct attack *));
27 STATIC_DCL void FDECL(mswingsm, (struct monst *, struct monst *, struct obj *));
28 STATIC_DCL void FDECL(noises,(struct monst *,struct attack *));
29 STATIC_DCL void FDECL(missmm,(struct monst *,struct monst *,struct attack *));
30 STATIC_DCL int FDECL(passivemm, (struct monst *, struct monst *, BOOLEAN_P, int));
32 /* Needed for the special case of monsters wielding vorpal blades (rare).
33 * If we use this a lot it should probably be a parameter to mdamagem()
34 * instead of a global variable.
38 /* returns mon_nam(mon) relative to other_mon; normal name unless they're
39 the same, in which case the reference is to {him|her|it} self */
41 mon_nam_too(outbuf, mon, other_mon)
43 struct monst *mon, *other_mon;
45 Strcpy(outbuf, mon_nam(mon));
47 switch (pronoun_gender(mon)) {
48 case 0: Strcpy(outbuf, "himself"); break;
49 case 1: Strcpy(outbuf, "herself"); break;
50 default: Strcpy(outbuf, "itself"); break;
57 register struct monst *magr;
58 register struct attack *mattk;
60 boolean farq = (distu(magr->mx, magr->my) > 15);
62 if(flags.soundok && (farq != far_noise || moves-noisetime > 10)) {
66 (mattk->aatyp == AT_EXPL) ? "an explosion" : "some noises",
67 farq ? " in the distance" : "");
73 missmm(magr, mdef, mattk)
74 register struct monst *magr, *mdef;
78 char buf[BUFSZ], mdef_name[BUFSZ];
81 if (!canspotmon(magr))
82 map_invisible(magr->mx, magr->my);
83 if (!canspotmon(mdef))
84 map_invisible(mdef->mx, mdef->my);
85 if (mdef->m_ap_type) seemimic(mdef);
86 if (magr->m_ap_type) seemimic(magr);
87 fmt = (could_seduce(magr,mdef,mattk) && !magr->mcan) ?
88 "%s pretends to be friendly to" : "%s misses";
89 Sprintf(buf, fmt, Monnam(magr));
90 pline("%s %s.", buf, mon_nam_too(mdef_name, mdef, magr));
91 } else noises(magr, mattk);
95 * fightm() -- fight some other monster
98 * 0 - Monster did nothing.
99 * 1 - If the monster made an attack. The monster might have died.
101 * There is an exception to the above. If mtmp has the hero swallowed,
102 * then we report that the monster did nothing so it will continue to
106 fightm(mtmp) /* have monsters fight each other */
107 register struct monst *mtmp;
109 register struct monst *mon, *nmon;
110 int result, has_u_swallowed;
114 /* perhaps the monster will resist Conflict */
115 if(resist(mtmp, RING_CLASS, 0, 0))
118 if(u.ustuck == mtmp) {
119 /* perhaps we're holding it... */
123 has_u_swallowed = (u.uswallow && (mtmp == u.ustuck));
125 for(mon = fmon; mon; mon = nmon) {
127 if(nmon == mtmp) nmon = mtmp->nmon;
128 /* Be careful to ignore monsters that are already dead, since we
129 * might be calling this before we've cleaned them up. This can
130 * happen if the monster attacked a cockatrice bare-handedly, for
133 if(mon != mtmp && !DEADMONSTER(mon)) {
134 if(monnear(mtmp,mon->mx,mon->my)) {
135 if(!u.uswallow && (mtmp == u.ustuck)) {
137 pline("%s releases you!", Monnam(mtmp));
143 /* mtmp can be killed */
147 result = mattackm(mtmp,mon);
149 if (result & MM_AGR_DIED) return 1; /* mtmp died */
151 * If mtmp has the hero swallowed, lie and say there
152 * was no attack (this allows mtmp to digest the hero).
154 if (has_u_swallowed) return 0;
156 /* Allow attacked monsters a chance to hit back. Primarily
157 * to allow monsters that resist conflict to respond.
159 if ((result & MM_HIT) && !(result & MM_DEF_DIED) &&
160 rn2(4) && mon->movement >= NORMAL_SPEED) {
161 mon->movement -= NORMAL_SPEED;
163 (void) mattackm(mon, mtmp); /* return attack */
166 return ((result & MM_HIT) ? 1 : 0);
174 * mattackm() -- a monster attacks another monster.
176 * This function returns a result bitfield:
178 * --------- aggressor died
179 * / ------- defender died
180 * / / ----- defender was hit
189 * Each successive attack has a lower probability of hitting. Some rely on the
190 * success of previous attacks. ** this doen't seem to be implemented -dl **
192 * In the case of exploding monsters, the monster dies as well.
196 register struct monst *magr,*mdef;
198 int i, /* loop counter */
199 tmp, /* amour class difference */
200 strike, /* hit this attack */
201 attk, /* attack attempted this time */
202 struck = 0, /* hit at least once */
203 res[NATTK]; /* results of all attacks */
204 struct attack *mattk, alt_attk;
205 struct permonst *pa, *pd;
207 if (!magr || !mdef) return(MM_MISS); /* mike@genat */
208 if (!magr->mcanmove || magr->msleeping) return(MM_MISS);
209 pa = magr->data; pd = mdef->data;
211 /* Grid bugs cannot attack at an angle. */
212 if (pa == &mons[PM_GRID_BUG] && magr->mx != mdef->mx
213 && magr->my != mdef->my)
216 /* Calculate the armour class differential. */
217 tmp = find_mac(mdef) + magr->m_lev;
218 if (mdef->mconf || !mdef->mcanmove || mdef->msleeping) {
223 /* undetect monsters become un-hidden if they are attacked */
224 if (mdef->mundetected) {
225 mdef->mundetected = 0;
226 newsym(mdef->mx, mdef->my);
227 if(canseemon(mdef) && !sensemon(mdef)) {
228 if (u.usleep) You("dream of %s.",
229 (mdef->data->geno & G_UNIQ) ?
230 a_monnam(mdef) : makeplural(m_monnam(mdef)));
231 else pline("Suddenly, you notice %s.", a_monnam(mdef));
235 /* Elves hate orcs. */
236 if (is_elf(pa) && is_orc(pd)) tmp++;
239 /* Set up the visibility of action */
240 vis = (cansee(magr->mx,magr->my) && cansee(mdef->mx,mdef->my) && (canspotmon(magr) || canspotmon(mdef)));
242 /* Set flag indicating monster has moved this turn. Necessary since a
243 * monster might get an attack out of sequence (i.e. before its move) in
244 * some cases, in which case this still counts as its move for the round
245 * and it shouldn't move again.
247 magr->mlstmv = monstermoves;
249 /* Now perform all attacks for the monster. */
250 for (i = 0; i < NATTK; i++) {
252 mattk = getmattk(pa, i, res, &alt_attk);
253 otmp = (struct obj *)0;
255 switch (mattk->aatyp) {
256 case AT_WEAP: /* "hand to hand" attacks */
257 if (magr->weapon_check == NEED_WEAPON || !MON_WEP(magr)) {
258 magr->weapon_check = NEED_HTH_WEAPON;
259 if (mon_wield_item(magr) != 0) return 0;
261 possibly_unwield(magr, FALSE);
262 otmp = MON_WEP(magr);
265 if (vis) mswingsm(magr, mdef, otmp);
266 tmp += hitval(otmp, mdef);
276 /* Nymph that teleported away on first attack? */
277 if (distmin(magr->mx,magr->my,mdef->mx,mdef->my) > 1)
279 /* Monsters won't attack cockatrices physically if they
280 * have a weapon instead. This instinct doesn't work for
281 * players, or under conflict or confusion.
283 if (!magr->mconf && !Conflict && otmp &&
284 mattk->aatyp != AT_WEAP && touch_petrifies(mdef->data)) {
288 dieroll = rnd(20 + i);
289 strike = (tmp > dieroll);
290 /* KMH -- don't accumulate to-hit bonuses */
292 tmp -= hitval(otmp, mdef);
294 res[i] = hitmm(magr, mdef, mattk);
295 if((mdef->data == &mons[PM_BLACK_PUDDING] || mdef->data == &mons[PM_BROWN_PUDDING])
296 && otmp && objects[otmp->otyp].oc_material == IRON
297 && mdef->mhp > 1 && !mdef->mcan)
299 if (clone_mon(mdef, 0, 0)) {
303 Strcpy(buf, Monnam(mdef));
304 pline("%s divides as %s hits it!", buf, mon_nam(magr));
309 missmm(magr, mdef, mattk);
312 case AT_HUGS: /* automatic if prev two attacks succeed */
313 strike = (i >= 2 && res[i-1] == MM_HIT && res[i-2] == MM_HIT);
315 res[i] = hitmm(magr, mdef, mattk);
320 strike = 0; /* will not wake up a sleeper */
321 res[i] = gazemm(magr, mdef, mattk);
325 res[i] = explmm(magr, mdef, mattk);
326 if (res[i] == MM_MISS) { /* cancelled--no attack */
330 strike = 1; /* automatic hit */
335 if (u.usteed && (mdef == u.usteed)) {
340 /* Engulfing attacks are directed at the hero if
343 if (u.uswallow && magr == u.ustuck)
346 if ((strike = (tmp > rnd(20+i))))
347 res[i] = gulpmm(magr, mdef, mattk);
349 missmm(magr, mdef, mattk);
353 default: /* no attack */
359 if (attk && !(res[i] & MM_AGR_DIED))
360 res[i] = passivemm(magr, mdef, strike, res[i] & MM_DEF_DIED);
362 if (res[i] & MM_DEF_DIED) return res[i];
365 * Wake up the defender. NOTE: this must follow the check
366 * to see if the defender died. We don't want to modify
367 * unallocated monsters!
369 if (strike) mdef->msleeping = 0;
371 if (res[i] & MM_AGR_DIED) return res[i];
372 /* return if aggressor can no longer attack */
373 if (!magr->mcanmove || magr->msleeping) return res[i];
374 if (res[i] & MM_HIT) struck = 1; /* at least one hit */
377 return(struck ? MM_HIT : MM_MISS);
380 /* Returns the result of mdamagem(). */
382 hitmm(magr, mdef, mattk)
383 register struct monst *magr,*mdef;
384 struct attack *mattk;
388 char buf[BUFSZ], mdef_name[BUFSZ];
390 if (!canspotmon(magr))
391 map_invisible(magr->mx, magr->my);
392 if (!canspotmon(mdef))
393 map_invisible(mdef->mx, mdef->my);
394 if(mdef->m_ap_type) seemimic(mdef);
395 if(magr->m_ap_type) seemimic(magr);
396 if((compat = could_seduce(magr,mdef,mattk)) && !magr->mcan) {
397 Sprintf(buf, "%s %s", Monnam(magr),
398 mdef->mcansee ? "smiles at" : "talks to");
399 pline("%s %s %s.", buf, mon_nam(mdef),
401 "engagingly" : "seductively");
403 char magr_name[BUFSZ];
405 Strcpy(magr_name, Monnam(magr));
406 switch (mattk->aatyp) {
408 Sprintf(buf,"%s bites", magr_name);
411 Sprintf(buf,"%s stings", magr_name);
414 Sprintf(buf,"%s butts", magr_name);
417 Sprintf(buf,"%s touches", magr_name);
420 Sprintf(buf, "%s tentacles suck",
421 s_suffix(magr_name));
424 if (magr != u.ustuck) {
425 Sprintf(buf,"%s squeezes", magr_name);
429 Sprintf(buf,"%s hits", magr_name);
431 pline("%s %s.", buf, mon_nam_too(mdef_name, mdef, magr));
433 } else noises(magr, mattk);
434 return(mdamagem(magr, mdef, mattk));
437 /* Returns the same values as mdamagem(). */
439 gazemm(magr, mdef, mattk)
440 register struct monst *magr, *mdef;
441 struct attack *mattk;
446 Sprintf(buf,"%s gazes at", Monnam(magr));
447 pline("%s %s...", buf, mon_nam(mdef));
450 if (magr->mcan || !magr->mcansee ||
451 (magr->minvis && !perceives(mdef->data)) ||
452 !mdef->mcansee || mdef->msleeping) {
453 if(vis) pline("but nothing happens.");
456 /* call mon_reflects 2x, first test, then, if visible, print message */
457 if (magr->data == &mons[PM_MEDUSA] && mon_reflects(mdef, (char *)0)) {
459 (void) mon_reflects(mdef,
460 "The gaze is reflected away by %s %s.");
462 if (mon_reflects(magr, (char *)0)) {
464 (void) mon_reflects(magr,
465 "The gaze is reflected away by %s %s.");
468 if (mdef->minvis && !perceives(magr->data)) {
469 if (canseemon(magr)) {
470 pline("%s doesn't seem to notice that %s gaze was reflected.",
471 Monnam(magr), mhis(magr));
476 pline("%s is turned to stone!", Monnam(magr));
478 if (magr->mhp > 0) return (MM_MISS);
479 return (MM_AGR_DIED);
483 return(mdamagem(magr, mdef, mattk));
486 /* Returns the same values as mattackm(). */
488 gulpmm(magr, mdef, mattk)
489 register struct monst *magr, *mdef;
490 register struct attack *mattk;
492 xchar ax, ay, dx, dy;
497 if (mdef->data->msize >= MZ_HUGE) return MM_MISS;
500 Sprintf(buf,"%s swallows", Monnam(magr));
501 pline("%s %s.", buf, mon_nam(mdef));
503 for (obj = mdef->minvent; obj; obj = obj->nobj)
504 (void) snuff_lit(obj);
507 * All of this maniuplation is needed to keep the display correct.
508 * There is a flush at the next pline().
515 * Leave the defender in the monster chain at it's current position,
516 * but don't leave it on the screen. Move the agressor to the def-
519 remove_monster(ax, ay);
520 place_monster(magr, dx, dy);
521 newsym(ax,ay); /* erase old position */
522 newsym(dx,dy); /* update new position */
524 status = mdamagem(magr, mdef, mattk);
526 if ((status & MM_AGR_DIED) && (status & MM_DEF_DIED)) {
527 ; /* both died -- do nothing */
529 else if (status & MM_DEF_DIED) { /* defender died */
531 * Note: remove_monster() was called in relmon(), wiping out
532 * magr from level.monsters[mdef->mx][mdef->my]. We need to
533 * put it back and display it. -kd
535 place_monster(magr, dx, dy);
538 else if (status & MM_AGR_DIED) { /* agressor died */
539 place_monster(mdef, dx, dy);
542 else { /* both alive, put them back */
544 pline("%s is regurgitated!", Monnam(mdef));
546 place_monster(magr, ax, ay);
547 place_monster(mdef, dx, dy);
556 explmm(magr, mdef, mattk)
557 register struct monst *magr, *mdef;
558 register struct attack *mattk;
565 if(cansee(magr->mx, magr->my))
566 pline("%s explodes!", Monnam(magr));
567 else noises(magr, mattk);
569 result = mdamagem(magr, mdef, mattk);
571 /* Kill off agressor if it didn't die. */
572 if (!(result & MM_AGR_DIED)) {
574 if (magr->mhp > 0) return result; /* life saved */
575 result |= MM_AGR_DIED;
577 if (magr->mtame) /* give this one even if it was visible */
578 You(brief_feeling, "melancholy");
584 * See comment at top of mattackm(), for return values.
587 mdamagem(magr, mdef, mattk)
588 register struct monst *magr, *mdef;
589 register struct attack *mattk;
593 struct permonst *pa = magr->data, *pd = mdef->data;
594 int armpro, num, tmp = d((int)mattk->damn, (int)mattk->damd);
597 if (touch_petrifies(pd) && !resists_ston(magr)) {
598 long protector = attk_protection((int)mattk->aatyp),
599 wornitems = magr->misc_worn_check;
601 /* wielded weapon gives same protection as gloves here */
602 if (otmp != 0) wornitems |= W_ARMG;
604 if (protector == 0L ||
605 (protector != ~0L && (wornitems & protector) != protector)) {
606 if (poly_when_stoned(pa)) {
608 return MM_HIT; /* no damage during the polymorph */
610 if (vis) pline("%s turns to stone!", Monnam(magr));
612 if (magr->mhp > 0) return 0;
613 else if (magr->mtame && !vis)
614 You(brief_feeling, "peculiarly sad");
619 /* cancellation factor is the same as when attacking the hero */
620 armpro = magic_negation(mdef);
621 cancelled = magr->mcan || !((rn2(3) >= armpro) || !rn2(50));
623 switch(mattk->adtyp) {
625 /* eating a Rider or its corpse is fatal */
626 if (is_rider(mdef->data)) {
628 pline("%s %s!", Monnam(magr),
629 mdef->data == &mons[PM_FAMINE] ?
630 "belches feebly, shrivels up and dies" :
631 mdef->data == &mons[PM_PESTILENCE] ?
632 "coughs spasmodically and collapses" :
633 "vomits violently and drops dead");
635 if (magr->mhp > 0) return 0; /* lifesaved */
636 else if (magr->mtame && !vis)
637 You(brief_feeling, "queasy");
640 if(flags.verbose && flags.soundok) verbalize("Burrrrp!");
642 /* Use up amulet of life saving */
643 if (!!(obj = mlifesaver(mdef))) m_useup(mdef, obj);
645 /* Is a corpse for nutrition possible? It may kill magr */
646 if (!corpse_chance(mdef, magr, TRUE) || magr->mhp < 1)
649 /* Pets get nutrition from swallowing monster whole.
650 * No nutrition from G_NOCORPSE monster, eg, undead.
651 * DGST monsters don't die from undead corpses
653 num = monsndx(mdef->data);
654 if (magr->mtame && !magr->isminion &&
655 !(mvitals[num].mvflags & G_NOCORPSE)) {
656 struct obj *virtualcorpse = mksobj(CORPSE, FALSE, FALSE);
659 virtualcorpse->corpsenm = num;
660 virtualcorpse->owt = weight(virtualcorpse);
661 nutrit = dog_nutrition(magr, virtualcorpse);
662 dealloc_obj(virtualcorpse);
664 /* only 50% nutrition, 25% of normal eating time */
665 if (magr->meating > 1) magr->meating = (magr->meating+3)/4;
666 if (nutrit > 1) nutrit /= 2;
667 EDOG(magr)->hungrytime += nutrit;
671 if (magr->mcan) break;
673 pline("%s %s for a moment.", Monnam(mdef),
674 makeplural(stagger(mdef->data, "stagger")));
687 if (mattk->aatyp == AT_KICK && thick_skinned(pd)) {
689 } else if(mattk->aatyp == AT_WEAP) {
691 if (otmp->otyp == CORPSE &&
692 touch_petrifies(&mons[otmp->corpsenm]))
694 tmp += dmgval(otmp, mdef);
695 if (otmp->oartifact) {
696 (void)artifact_hit(magr,mdef, otmp, &tmp, dieroll);
698 return (MM_DEF_DIED |
699 (grow_up(magr,mdef) ? 0 : MM_AGR_DIED));
702 mrustm(magr, mdef, otmp);
704 } else if (magr->data == &mons[PM_PURPLE_WORM] &&
705 mdef->data == &mons[PM_SHRIEKER]) {
706 /* hack to enhance mm_aggression(); we don't want purple
707 worm's bite attack to kill a shrieker because then it
708 won't swallow the corpse; but if the target survives,
709 the subsequent engulf attack should accomplish that */
710 if (tmp >= mdef->mhp) tmp = mdef->mhp - 1;
719 pline("%s is %s!", Monnam(mdef),
720 on_fire(mdef->data, mattk));
721 if (pd == &mons[PM_STRAW_GOLEM] ||
722 pd == &mons[PM_PAPER_GOLEM]) {
723 if (vis) pline("%s burns completely!", Monnam(mdef));
725 if (mdef->mhp > 0) return 0;
726 else if (mdef->mtame && !vis)
727 pline("May %s roast in peace.", mon_nam(mdef));
728 return (MM_DEF_DIED | (grow_up(magr,mdef) ?
731 tmp += destroy_mitem(mdef, SCROLL_CLASS, AD_FIRE);
732 tmp += destroy_mitem(mdef, SPBOOK_CLASS, AD_FIRE);
733 if (resists_fire(mdef)) {
735 pline_The("fire doesn't seem to burn %s!",
737 shieldeff(mdef->mx, mdef->my);
738 golemeffects(mdef, AD_FIRE, tmp);
741 /* only potions damage resistant players in destroy_item */
742 tmp += destroy_mitem(mdef, POTION_CLASS, AD_FIRE);
749 if (vis) pline("%s is covered in frost!", Monnam(mdef));
750 if (resists_cold(mdef)) {
752 pline_The("frost doesn't seem to chill %s!",
754 shieldeff(mdef->mx, mdef->my);
755 golemeffects(mdef, AD_COLD, tmp);
758 tmp += destroy_mitem(mdef, POTION_CLASS, AD_COLD);
765 if (vis) pline("%s gets zapped!", Monnam(mdef));
766 tmp += destroy_mitem(mdef, WAND_CLASS, AD_ELEC);
767 if (resists_elec(mdef)) {
768 if (vis) pline_The("zap doesn't shock %s!", mon_nam(mdef));
769 shieldeff(mdef->mx, mdef->my);
770 golemeffects(mdef, AD_ELEC, tmp);
773 /* only rings damage resistant players in destroy_item */
774 tmp += destroy_mitem(mdef, RING_CLASS, AD_ELEC);
781 if (resists_acid(mdef)) {
783 pline("%s is covered in acid, but it seems harmless.",
787 pline("%s is covered in acid!", Monnam(mdef));
788 pline("It burns %s!", mon_nam(mdef));
790 if (!rn2(30)) erode_armor(mdef, TRUE);
791 if (!rn2(6)) erode_obj(MON_WEP(mdef), TRUE, TRUE);
794 if (magr->mcan) break;
795 if (pd == &mons[PM_IRON_GOLEM]) {
796 if (vis) pline("%s falls to pieces!", Monnam(mdef));
798 if (mdef->mhp > 0) return 0;
799 else if (mdef->mtame && !vis)
800 pline("May %s rust in peace.", mon_nam(mdef));
801 return (MM_DEF_DIED | (grow_up(magr,mdef) ?
804 hurtmarmor(mdef, AD_RUST);
805 mdef->mstrategy &= ~STRAT_WAITFORU;
809 if (magr->mcan) break;
810 hurtmarmor(mdef, AD_CORR);
811 mdef->mstrategy &= ~STRAT_WAITFORU;
815 if (magr->mcan) break;
816 if (pd == &mons[PM_WOOD_GOLEM] ||
817 pd == &mons[PM_LEATHER_GOLEM]) {
818 if (vis) pline("%s falls to pieces!", Monnam(mdef));
820 if (mdef->mhp > 0) return 0;
821 else if (mdef->mtame && !vis)
822 pline("May %s rot in peace.", mon_nam(mdef));
823 return (MM_DEF_DIED | (grow_up(magr,mdef) ?
826 hurtmarmor(mdef, AD_DCAY);
827 mdef->mstrategy &= ~STRAT_WAITFORU;
831 if (magr->mcan) break;
833 /* may die from the acid if it eats a stone-curing corpse */
834 if (munstone(mdef, FALSE)) goto post_stone;
835 if (poly_when_stoned(pd)) {
840 if (!resists_ston(mdef)) {
841 if (vis) pline("%s turns to stone!", Monnam(mdef));
843 post_stone: if (mdef->mhp > 0) return 0;
844 else if (mdef->mtame && !vis)
845 You(brief_feeling, "peculiarly sad");
846 return (MM_DEF_DIED | (grow_up(magr,mdef) ?
849 tmp = (mattk->adtyp == AD_STON ? 0 : 1);
852 if (!cancelled && tmp < mdef->mhp && !tele_restrict(mdef)) {
853 char mdef_Monnam[BUFSZ];
854 /* save the name before monster teleports, otherwise
855 we'll get "it" in the suddenly disappears message */
856 if (vis) Strcpy(mdef_Monnam, Monnam(mdef));
857 mdef->mstrategy &= ~STRAT_WAITFORU;
858 (void) rloc(mdef, FALSE);
859 if (vis && !canspotmon(mdef)
864 pline("%s suddenly disappears!", mdef_Monnam);
868 if (!cancelled && !mdef->msleeping &&
869 sleep_monst(mdef, rnd(10), -1)) {
871 Strcpy(buf, Monnam(mdef));
872 pline("%s is put to sleep by %s.", buf, mon_nam(magr));
874 mdef->mstrategy &= ~STRAT_WAITFORU;
879 if(!cancelled && mdef->mcanmove) {
881 Strcpy(buf, Monnam(mdef));
882 pline("%s is frozen by %s.", buf, mon_nam(magr));
885 mdef->mfrozen = rnd(10);
886 mdef->mstrategy &= ~STRAT_WAITFORU;
890 if (!cancelled && mdef->mspeed != MSLOW) {
891 unsigned int oldspeed = mdef->mspeed;
893 mon_adjust_speed(mdef, -1, (struct obj *)0);
894 mdef->mstrategy &= ~STRAT_WAITFORU;
895 if (mdef->mspeed != oldspeed && vis)
896 pline("%s slows down.", Monnam(mdef));
900 /* Since confusing another monster doesn't have a real time
901 * limit, setting spec_used would not really be right (though
902 * we still should check for it).
904 if (!magr->mcan && !mdef->mconf && !magr->mspec_used) {
905 if (vis) pline("%s looks confused.", Monnam(mdef));
907 mdef->mstrategy &= ~STRAT_WAITFORU;
911 if (can_blnd(magr, mdef, mattk->aatyp, (struct obj*)0)) {
912 register unsigned rnd_tmp;
914 if (vis && mdef->mcansee)
915 pline("%s is blinded.", Monnam(mdef));
916 rnd_tmp = d((int)mattk->damn, (int)mattk->damd);
917 if ((rnd_tmp += mdef->mblinded) > 127) rnd_tmp = 127;
918 mdef->mblinded = rnd_tmp;
920 mdef->mstrategy &= ~STRAT_WAITFORU;
925 if (!magr->mcan && haseyes(pd) && mdef->mcansee) {
926 if (vis) pline("%s looks %sconfused.",
927 Monnam(mdef), mdef->mconf ? "more " : "");
929 mdef->mstrategy &= ~STRAT_WAITFORU;
934 if (!night() && (pa == &mons[PM_GREMLIN])) break;
935 if (!magr->mcan && !rn2(10)) {
936 mdef->mcan = 1; /* cancelled regardless of lifesave */
937 mdef->mstrategy &= ~STRAT_WAITFORU;
938 if (is_were(pd) && pd->mlet != S_HUMAN)
940 if (pd == &mons[PM_CLAY_GOLEM]) {
942 pline("Some writing vanishes from %s head!",
943 s_suffix(mon_nam(mdef)));
944 pline("%s is destroyed!", Monnam(mdef));
947 if (mdef->mhp > 0) return 0;
948 else if (mdef->mtame && !vis)
949 You(brief_feeling, "strangely sad");
950 return (MM_DEF_DIED | (grow_up(magr,mdef) ?
954 if (!vis) You_hear("laughter.");
955 else pline("%s chuckles.", Monnam(magr));
962 if (magr->mcan || !mdef->mgold) break;
963 /* technically incorrect; no check for stealing gold from
964 * between mdef's feet...
966 magr->mgold += mdef->mgold;
969 if (magr->mcan) break;
970 /* technically incorrect; no check for stealing gold from
971 * between mdef's feet...
974 struct obj *gold = findgold(mdef->minvent);
976 obj_extract_self(gold);
977 add_to_minv(magr, gold);
980 mdef->mstrategy &= ~STRAT_WAITFORU;
982 Strcpy(buf, Monnam(magr));
983 pline("%s steals some gold from %s.", buf, mon_nam(mdef));
985 if (!tele_restrict(magr)) {
986 (void) rloc(magr, FALSE);
987 if (vis && !canspotmon(magr))
988 pline("%s suddenly disappears!", buf);
992 if (!cancelled && !rn2(3) && !resists_drli(mdef)) {
995 pline("%s suddenly seems weaker!", Monnam(mdef));
997 if (mdef->m_lev == 0)
1000 /* Automatic kill if drained past level 0 */
1006 case AD_SITM: /* for now these are the same */
1008 if (magr->mcan) break;
1009 /* find an object to steal, non-cursed if magr is tame */
1010 for (obj = mdef->minvent; obj; obj = obj->nobj)
1011 if (!magr->mtame || !obj->cursed)
1015 char onambuf[BUFSZ], mdefnambuf[BUFSZ];
1017 /* make a special x_monnam() call that never omits
1018 the saddle, and save it for later messages */
1019 Strcpy(mdefnambuf, x_monnam(mdef, ARTICLE_THE, (char *)0, 0, FALSE));
1023 if (u.usteed == mdef &&
1024 otmp == which_armor(mdef, W_SADDLE))
1025 /* "You can no longer ride <steed>." */
1026 dismount_steed(DISMOUNT_POLY);
1028 obj_extract_self(otmp);
1029 if (otmp->owornmask) {
1030 mdef->misc_worn_check &= ~otmp->owornmask;
1031 if (otmp->owornmask & W_WEP)
1032 setmnotwielded(mdef,otmp);
1033 otmp->owornmask = 0L;
1034 update_mon_intrinsics(mdef, otmp, FALSE, FALSE);
1036 /* add_to_minv() might free otmp [if it merges] */
1038 Strcpy(onambuf, doname(otmp));
1039 (void) add_to_minv(magr, otmp);
1041 Strcpy(buf, Monnam(magr));
1042 pline("%s steals %s from %s!", buf,
1043 onambuf, mdefnambuf);
1045 possibly_unwield(mdef, FALSE);
1046 mdef->mstrategy &= ~STRAT_WAITFORU;
1047 mselftouch(mdef, (const char *)0, FALSE);
1049 return (MM_DEF_DIED | (grow_up(magr,mdef) ?
1051 if (magr->data->mlet == S_NYMPH &&
1052 !tele_restrict(magr)) {
1053 (void) rloc(magr, FALSE);
1054 if (vis && !canspotmon(magr))
1055 pline("%s suddenly disappears!", buf);
1063 if (!cancelled && !rn2(8)) {
1065 pline("%s %s was poisoned!", s_suffix(Monnam(magr)),
1066 mpoisons_subj(magr, mattk));
1067 if (resists_poison(mdef)) {
1069 pline_The("poison doesn't seem to affect %s.",
1072 if (rn2(10)) tmp += rn1(10,6);
1074 if (vis) pline_The("poison was deadly...");
1081 if (notonhead || !has_head(pd)) {
1082 if (vis) pline("%s doesn't seem harmed.", Monnam(mdef));
1083 /* Not clear what to do for green slimes */
1087 if ((mdef->misc_worn_check & W_ARMH) && rn2(8)) {
1089 Strcpy(buf, s_suffix(Monnam(mdef)));
1090 pline("%s helmet blocks %s attack to %s head.",
1091 buf, s_suffix(mon_nam(magr)),
1096 if (vis) pline("%s brain is eaten!", s_suffix(Monnam(mdef)));
1098 if (vis) pline("%s doesn't notice.", Monnam(mdef));
1101 tmp += rnd(10); /* fakery, since monsters lack INT scores */
1102 if (magr->mtame && !magr->isminion) {
1103 EDOG(magr)->hungrytime += rnd(60);
1106 if (tmp >= mdef->mhp && vis)
1107 pline("%s last thought fades away...",
1108 s_suffix(Monnam(mdef)));
1111 if (cancelled) break; /* physical damage only */
1112 if (!rn2(4) && !flaming(mdef->data) &&
1113 mdef->data != &mons[PM_GREEN_SLIME]) {
1114 (void) newcham(mdef, &mons[PM_GREEN_SLIME], FALSE, vis);
1115 mdef->mstrategy &= ~STRAT_WAITFORU;
1120 if (cancelled) tmp = 0;
1122 case AD_WRAP: /* monsters cannot grab one another, it's too hard */
1123 if (magr->mcan) tmp = 0;
1126 /* there's no msomearmor() function, so just do damage */
1127 /* if (cancelled) break; */
1132 if(!tmp) return(MM_MISS);
1134 if((mdef->mhp -= tmp) < 1) {
1135 if (m_at(mdef->mx, mdef->my) == magr) { /* see gulpmm() */
1136 remove_monster(mdef->mx, mdef->my);
1137 mdef->mhp = 1; /* otherwise place_monster will complain */
1138 place_monster(mdef, mdef->mx, mdef->my);
1141 monkilled(mdef, "", (int)mattk->adtyp);
1142 if (mdef->mhp > 0) return 0; /* mdef lifesaved */
1144 if (mattk->adtyp == AD_DGST) {
1145 /* various checks similar to dog_eat and meatobj.
1146 * after monkilled() to provide better message ordering */
1147 if (mdef->cham != CHAM_ORDINARY) {
1148 (void) newcham(magr, (struct permonst *)0, FALSE, TRUE);
1149 } else if (mdef->data == &mons[PM_GREEN_SLIME]) {
1150 (void) newcham(magr, &mons[PM_GREEN_SLIME], FALSE, TRUE);
1151 } else if (mdef->data == &mons[PM_WRAITH]) {
1152 (void) grow_up(magr, (struct monst *)0);
1153 /* don't grow up twice */
1154 return (MM_DEF_DIED | (magr->mhp > 0 ? 0 : MM_AGR_DIED));
1155 } else if (mdef->data == &mons[PM_NURSE]) {
1156 magr->mhp = magr->mhpmax;
1160 return (MM_DEF_DIED | (grow_up(magr,mdef) ? 0 : MM_AGR_DIED));
1171 noattacks(ptr) /* returns 1 if monster doesn't attack */
1172 struct permonst *ptr;
1176 for(i = 0; i < NATTK; i++)
1177 if(ptr->mattk[i].aatyp) return(0);
1182 /* `mon' is hit by a sleep attack; return 1 if it's affected, 0 otherwise */
1184 sleep_monst(mon, amt, how)
1188 if (resists_sleep(mon) ||
1189 (how >= 0 && resist(mon, (char)how, 0, NOTELL))) {
1190 shieldeff(mon->mx, mon->my);
1191 } else if (mon->mcanmove) {
1192 amt += (int) mon->mfrozen;
1193 if (amt > 0) { /* sleep for N turns */
1195 mon->mfrozen = min(amt, 127);
1196 } else { /* sleep until awakened */
1204 /* sleeping grabber releases, engulfer doesn't; don't use for paralysis! */
1209 if ((mon->msleeping || !mon->mcanmove) && mon == u.ustuck &&
1210 !sticks(youmonst.data) && !u.uswallow) {
1211 pline("%s grip relaxes.", s_suffix(Monnam(mon)));
1220 mrustm(magr, mdef, obj)
1221 register struct monst *magr, *mdef;
1222 register struct obj *obj;
1226 if (!magr || !mdef || !obj) return; /* just in case */
1228 if (dmgtype(mdef->data, AD_CORR))
1230 else if (dmgtype(mdef->data, AD_RUST))
1236 (is_acid ? is_corrodeable(obj) : is_rustprone(obj)) &&
1237 (is_acid ? obj->oeroded2 : obj->oeroded) < MAX_ERODE) {
1238 if (obj->greased || obj->oerodeproof || (obj->blessed && rn2(3))) {
1239 if (cansee(mdef->mx, mdef->my) && flags.verbose)
1240 pline("%s weapon is not affected.",
1241 s_suffix(Monnam(magr)));
1242 if (obj->greased && !rn2(2)) obj->greased = 0;
1244 if (cansee(mdef->mx, mdef->my)) {
1245 pline("%s %s%s!", s_suffix(Monnam(magr)),
1246 aobjnam(obj, (is_acid ? "corrode" : "rust")),
1247 (is_acid ? obj->oeroded2 : obj->oeroded)
1250 if (is_acid) obj->oeroded2++;
1251 else obj->oeroded++;
1257 mswingsm(magr, mdef, otemp)
1258 register struct monst *magr, *mdef;
1259 register struct obj *otemp;
1262 if (!flags.verbose || Blind || !mon_visible(magr)) return;
1263 Strcpy(buf, mon_nam(mdef));
1264 pline("%s %s %s %s at %s.", Monnam(magr),
1265 (objects[otemp->otyp].oc_dir & PIERCE) ? "thrusts" : "swings",
1266 mhis(magr), singular(otemp, xname), buf);
1270 * Passive responses by defenders. Does not replicate responses already
1271 * handled above. Returns same values as mattackm.
1274 passivemm(magr,mdef,mhit,mdead)
1275 register struct monst *magr, *mdef;
1279 register struct permonst *mddat = mdef->data;
1280 register struct permonst *madat = magr->data;
1285 if(i >= NATTK) return (mdead | mhit); /* no passive attacks */
1286 if(mddat->mattk[i].aatyp == AT_NONE) break;
1288 if (mddat->mattk[i].damn)
1289 tmp = d((int)mddat->mattk[i].damn,
1290 (int)mddat->mattk[i].damd);
1291 else if(mddat->mattk[i].damd)
1292 tmp = d((int)mddat->mlevel+1, (int)mddat->mattk[i].damd);
1296 /* These affect the enemy even if defender killed */
1297 switch(mddat->mattk[i].adtyp) {
1299 if (mhit && !rn2(2)) {
1300 Strcpy(buf, Monnam(magr));
1302 pline("%s is splashed by %s acid!",
1303 buf, s_suffix(mon_nam(mdef)));
1304 if (resists_acid(magr)) {
1306 pline("%s is not affected.", Monnam(magr));
1311 case AD_ENCH: /* KMH -- remove enchantment (disenchanter) */
1312 if (mhit && !mdef->mcan && otmp) {
1313 (void) drain_item(otmp);
1320 if (mdead || mdef->mcan) return (mdead|mhit);
1322 /* These affect the enemy only if defender is still alive */
1323 if (rn2(3)) switch(mddat->mattk[i].adtyp) {
1324 case AD_PLYS: /* Floating eye */
1325 if (tmp > 127) tmp = 127;
1326 if (mddat == &mons[PM_FLOATING_EYE]) {
1327 if (!rn2(4)) tmp = 127;
1328 if (magr->mcansee && haseyes(madat) && mdef->mcansee &&
1329 (perceives(madat) || !mdef->minvis)) {
1330 Sprintf(buf, "%s gaze is reflected by %%s %%s.",
1331 s_suffix(mon_nam(mdef)));
1332 if (mon_reflects(magr,
1333 canseemon(magr) ? buf : (char *)0))
1335 Strcpy(buf, Monnam(magr));
1337 pline("%s is frozen by %s gaze!",
1338 buf, s_suffix(mon_nam(mdef)));
1340 magr->mfrozen = tmp;
1341 return (mdead|mhit);
1343 } else { /* gelatinous cube */
1344 Strcpy(buf, Monnam(magr));
1346 pline("%s is frozen by %s.", buf, mon_nam(mdef));
1348 magr->mfrozen = tmp;
1349 return (mdead|mhit);
1353 if (resists_cold(magr)) {
1354 if (canseemon(magr)) {
1355 pline("%s is mildly chilly.", Monnam(magr));
1356 golemeffects(magr, AD_COLD, tmp);
1362 pline("%s is suddenly very cold!", Monnam(magr));
1363 mdef->mhp += tmp / 2;
1364 if (mdef->mhpmax < mdef->mhp) mdef->mhpmax = mdef->mhp;
1365 if (mdef->mhpmax > ((int) (mdef->m_lev+1) * 8))
1366 (void)split_mon(mdef, magr);
1371 if (canseemon(magr))
1372 pline("%s %s...", Monnam(magr),
1373 makeplural(stagger(magr->data, "stagger")));
1378 if (resists_fire(magr)) {
1379 if (canseemon(magr)) {
1380 pline("%s is mildly warmed.", Monnam(magr));
1381 golemeffects(magr, AD_FIRE, tmp);
1387 pline("%s is suddenly very hot!", Monnam(magr));
1390 if (resists_elec(magr)) {
1391 if (canseemon(magr)) {
1392 pline("%s is mildly tingled.", Monnam(magr));
1393 golemeffects(magr, AD_ELEC, tmp);
1399 pline("%s is jolted with electricity!", Monnam(magr));
1407 if((magr->mhp -= tmp) <= 0) {
1408 monkilled(magr, "", (int)mddat->mattk[i].adtyp);
1409 return (mdead | mhit | MM_AGR_DIED);
1411 return (mdead | mhit);
1414 /* "aggressive defense"; what type of armor prevents specified attack
1415 from touching its target? */
1417 attk_protection(aatyp)
1430 w_mask = ~0L; /* special case; no defense needed */
1435 w_mask = W_ARMG; /* caller needs to check for weapon */
1444 w_mask = (W_ARMC|W_ARMG); /* attacker needs both to be protected */
1451 w_mask = 0L; /* no defense available */