1 /* NetHack 3.7	uhitm.c	$NHDT-Date: 1617035737 2021/03/29 16:35:37 $  $NHDT-Branch: NetHack-3.7 $:$NHDT-Revision: 1.300 $ */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /*-Copyright (c) Robert Patrick Rankin, 2012. */
4 /* NetHack may be freely redistributed.  See license for details. */
5 
6 #include "hack.h"
7 
8 static const char brief_feeling[] =
9     "have a %s feeling for a moment, then it passes.";
10 
11 static boolean attack_check_conducts(struct obj *);
12 static boolean known_hitum(struct monst *, struct obj *, int *, int, int,
13                            struct attack *, int);
14 static boolean theft_petrifies(struct obj *);
15 static void steal_it(struct monst *, struct attack *);
16 static int really_steal(struct obj *, struct monst *);
17 static void mhitm_really_poison(struct monst *, struct attack *,
18                                 struct monst *, struct mhitm_data *);
19 static boolean should_cleave(void);
20 static boolean hitum_cleave(struct monst *, struct attack *);
21 static boolean hitum(struct monst *, struct attack *);
22 static boolean hmon_hitmon(struct monst *, struct obj *, int, int);
23 static int joust(struct monst *, struct obj *);
24 static void demonpet(void);
25 static boolean m_slips_free(struct monst *, struct attack *);
26 static void start_engulf(struct monst *);
27 static void end_engulf(void);
28 static int gulpum(struct monst *, struct attack *);
29 static boolean hmonas(struct monst *);
30 static void nohandglow(struct monst *);
31 static boolean shade_aware(struct obj *);
32 
33 #define PROJECTILE(obj) ((obj) && is_ammo(obj))
34 
35 /* return values from really_steal() */
36 enum really_steal_returns {
37     STEAL_SUCCESS = 0,
38     STEAL_DROPPED = 1, /* stole item but it didn't get into inventory */
39     STEAL_ABORT   = 2  /* something made us stop stealing (petrification) */
40 };
41 
42 /* multi_reason is usually a literal string; here we generate one that
43    has the causing monster's type included */
44 void
dynamic_multi_reason(struct monst * mon,const char * verb,boolean by_gaze)45 dynamic_multi_reason(struct monst *mon, const char *verb, boolean by_gaze)
46 {
47     /* combination of noname_monnam() and m_monnam(), more or less;
48        accurate regardless of visibility or hallucination (only seen
49        if game ends) and without personal name (M2_PNAME excepted) */
50     char *who = x_monnam(mon, ARTICLE_A, (char *) 0,
51                          (SUPPRESS_IT | SUPPRESS_INVISIBLE
52                           | SUPPRESS_HALLUCINATION | SUPPRESS_SADDLE
53                           | SUPPRESS_NAME),
54                          FALSE),
55          *p = g.multireasonbuf;
56 
57     /* prefix info for done_in_by() */
58     Sprintf(p, "%u:", mon->m_id);
59     p = eos(p);
60     Sprintf(p, "%s by %s%s", verb,
61             !by_gaze ? who : s_suffix(who),
62             !by_gaze ? "" : " gaze");
63     g.multi_reason = p;
64 }
65 
66 void
erode_armor(struct monst * mdef,int hurt)67 erode_armor(struct monst *mdef, int hurt)
68 {
69     struct obj *target;
70 
71     /* What the following code does: it keeps looping until it
72      * finds a target for the rust monster.
73      * Head, feet, etc... not covered by metal, or covered by
74      * rusty metal, are not targets.  However, your body always
75      * is, no matter what covers it.
76      */
77     while (1) {
78         switch (rn2(5)) {
79         case 0:
80             target = which_armor(mdef, W_ARMH);
81             if (!target
82                 || erode_obj(target, xname(target), hurt, EF_GREASE)
83                        == ER_NOTHING)
84                 continue;
85             break;
86         case 1:
87             target = which_armor(mdef, W_ARMC);
88             if (target) {
89                 (void) erode_obj(target, xname(target), hurt,
90                                  EF_GREASE | EF_VERBOSE);
91                 break;
92             }
93             if ((target = which_armor(mdef, W_ARM)) != (struct obj *) 0) {
94                 (void) erode_obj(target, xname(target), hurt,
95                                  EF_GREASE | EF_VERBOSE);
96             } else if ((target = which_armor(mdef, W_ARMU))
97                        != (struct obj *) 0) {
98                 (void) erode_obj(target, xname(target), hurt,
99                                  EF_GREASE | EF_VERBOSE);
100             }
101             break;
102         case 2:
103             target = which_armor(mdef, W_ARMS);
104             if (!target
105                 || erode_obj(target, xname(target), hurt, EF_GREASE)
106                        == ER_NOTHING)
107                 continue;
108             break;
109         case 3:
110             target = which_armor(mdef, W_ARMG);
111             if (!target
112                 || erode_obj(target, xname(target), hurt, EF_GREASE)
113                        == ER_NOTHING)
114                 continue;
115             break;
116         case 4:
117             target = which_armor(mdef, W_ARMF);
118             if (!target
119                 || erode_obj(target, xname(target), hurt, EF_GREASE)
120                        == ER_NOTHING)
121                 continue;
122             break;
123         }
124         break; /* Out of while loop */
125     }
126 }
127 
128 /* FALSE means it's OK to attack */
129 boolean
attack_checks(struct monst * mtmp,struct obj * wep)130 attack_checks(struct monst *mtmp,
131               struct obj *wep) /* uwep for do_attack(), null for kick_monster() */
132 {
133     int glyph;
134 
135     /* if you're close enough to attack, alert any waiting monster */
136     mtmp->mstrategy &= ~STRAT_WAITMASK;
137 
138     if (u.uswallow && mtmp == u.ustuck)
139         return attack_check_conducts(wep);
140 
141     if (g.context.forcefight) {
142         /* Do this in the caller, after we checked that the monster
143          * didn't die from the blow.  Reason: putting the 'I' there
144          * causes the hero to forget the square's contents since
145          * both 'I' and remembered contents are stored in .glyph.
146          * If the monster dies immediately from the blow, the 'I' will
147          * not stay there, so the player will have suddenly forgotten
148          * the square's contents for no apparent reason.
149         if (!canspotmon(mtmp)
150             && !glyph_is_invisible(levl[g.bhitpos.x][g.bhitpos.y].glyph))
151             map_invisible(g.bhitpos.x, g.bhitpos.y);
152          */
153         return attack_check_conducts(wep);
154     }
155 
156     /* cache the shown glyph;
157        cases which might change it (by placing or removing
158        'rembered, unseen monster' glyph or revealing a mimic)
159        always return without further reference to this */
160     glyph = glyph_at(g.bhitpos.x, g.bhitpos.y);
161 
162     /* Put up an invisible monster marker, but with exceptions for
163      * monsters that hide and monsters you've been warned about.
164      * The former already prints a warning message and
165      * prevents you from hitting the monster just via the hidden monster
166      * code below; if we also did that here, similar behavior would be
167      * happening two turns in a row.  The latter shows a glyph on
168      * the screen, so you know something is there.
169      */
170     if (!canspotmon(mtmp)
171         && !glyph_is_warning(glyph) && !glyph_is_invisible(glyph)
172         && !(!Blind && mtmp->mundetected && hides_under(mtmp->data))) {
173         pline("Wait!  There's %s there you can't see!", something);
174         map_invisible(g.bhitpos.x, g.bhitpos.y);
175         /* if it was an invisible mimic, treat it as if we stumbled
176          * onto a visible mimic
177          */
178         if (M_AP_TYPE(mtmp) && !Protection_from_shape_changers) {
179             if (!u.ustuck && !mtmp->mflee && dmgtype(mtmp->data, AD_STCK)
180                 /* applied pole-arm attack is too far to get stuck */
181                 && distu(mtmp->mx, mtmp->my) <= 2)
182                 set_ustuck(mtmp);
183         }
184         /* #H7329 - if hero is on engraved "Elbereth", this will end up
185          * assessing an alignment penalty and removing the engraving
186          * even though no attack actually occurs.  Since it also angers
187          * peacefuls, we're operating as if an attack attempt did occur
188          * and the Elbereth behavior is consistent.
189          */
190         wakeup(mtmp, TRUE, TRUE); /* always necessary; also un-mimics mimics */
191         return TRUE;
192     }
193 
194     if (M_AP_TYPE(mtmp) && !Protection_from_shape_changers && !sensemon(mtmp)
195         && !glyph_is_warning(glyph)) {
196         /* If a hidden mimic was in a square where a player remembers
197          * some (probably different) unseen monster, the player is in
198          * luck--he attacks it even though it's hidden.
199          */
200         if (glyph_is_invisible(glyph)) {
201             seemimic(mtmp);
202             return attack_check_conducts(wep);
203         }
204         stumble_onto_mimic(mtmp);
205         return TRUE;
206     }
207 
208     if (mtmp->mundetected && !canseemon(mtmp)
209         && !glyph_is_warning(glyph)
210         && (hides_under(mtmp->data) || mtmp->data->mlet == S_EEL)) {
211         mtmp->mundetected = 0;
212         wakeup(mtmp, FALSE, FALSE);
213         newsym(mtmp->mx, mtmp->my);
214         if (glyph_is_invisible(glyph)) {
215             seemimic(mtmp);
216             return attack_check_conducts(wep);
217         }
218         if (!((Blind ? Blind_telepat : Unblind_telepat) || Detect_monsters)) {
219 
220             if (!Blind && Hallucination) {
221                 pline("A wild %s appeared!", l_monnam(mtmp));
222                 return TRUE;
223             }
224             else if (Blind || (is_pool(mtmp->mx, mtmp->my) && !Underwater)) {
225                 pline("Wait!  There's a hidden monster there!");
226             }
227             else if (concealed_spot(mtmp->mx, mtmp->my)) {
228                 struct obj *obj = g.level.objects[mtmp->mx][mtmp->my];
229                 pline("Wait!  There's %s hiding under %s%s!", an(l_monnam(mtmp)),
230                       obj ? "" : "the ",
231                       obj ? doname(obj) : explain_terrain(mtmp->mx, mtmp->my));
232             }
233             return TRUE;
234         }
235     }
236 
237     /*
238      * make sure to wake up a monster from the above cases if the
239      * hero can sense that the monster is there.
240      */
241     if ((mtmp->mundetected || M_AP_TYPE(mtmp)) && sensemon(mtmp)) {
242         mtmp->mundetected = 0;
243         wakeup(mtmp, TRUE, TRUE);
244     }
245 
246     /* Intelligent chaotic weapons (Stormbringer) want blood */
247     if (wep && wep->oartifact == ART_STORMBRINGER) {
248         if (mtmp->mpeaceful) /* only print message if it was peaceful */
249             g.override_confirmation = TRUE;
250         return FALSE; /* no attack_check_weaponless */
251     }
252 
253     if (flags.confirm && mtmp->mpeaceful
254         && !Confusion && !Hallucination && !Stunned) {
255         /* The player has the ability to displace peacefuls, so I'm not sure if
256          * this code ever actually gets reached. */
257         if (canspotmon(mtmp)) {
258             char qbuf[QBUFSZ];
259 
260             Sprintf(qbuf, "Really attack %s?", mon_nam(mtmp));
261             if (!paranoid_query(ParanoidHit, qbuf)) {
262                 g.context.move = 0;
263                 return TRUE;
264             }
265         }
266     }
267 
268     return attack_check_conducts(wep);
269 }
270 
271 /* If the player is about to break a conduct by attacking but it's reasonably
272  * likely that they're trying to preserve that conduct, ask them to confirm.
273  * The two conducts that are at risk by attacking are weaponless and pacifist.
274  *
275  * It is assumed that the return value from this function will be returned
276  * directly from attack_checks(), and like that, TRUE means not attack whereas
277  * FALSE means OK to attack. This also sets context.move to 0 as a side effect
278  * (so that it takes no time) if the player elects not to attack.
279  */
280 static boolean
attack_check_conducts(struct obj * wep)281 attack_check_conducts(struct obj* wep)
282 {
283     /* Weaponless: about to break weaponless conduct as a non-beginner
284      * (common situation is forgetting to unwield a pick-axe) */
285     if (wep && (wep->oclass == WEAPON_CLASS || is_weptool(wep))
286             && !u.uconduct.weaphit && !flags.beginner) {
287         if (!paranoid_query(TRUE, "Break weaponless conduct?")) {
288             g.context.move = 0;
289             return TRUE;
290         }
291     }
292     /* Pacifist: about to hit anything at all. Note that pacifists can hit
293      * things without breaking the conduct, so long as they never directly kill
294      * anything, but they *probably* aren't trying to do this. Still, if they
295      * pressed F to forcefight, assume the user is trying to bypass this prompt.
296      * Use turns rather than beginner status to determine if it is early enough,
297      * since it's hard for pacifists to gain score.
298      */
299     static boolean said_yes_to_attack = FALSE;
300     if (!said_yes_to_attack && !u.uconduct.killer && !g.context.forcefight
301         && g.moves > 100) {
302         pline("Killing a monster violates pacifist conduct.");
303         if (!paranoid_query(TRUE, "Attack anyway?")) {
304             g.context.move = 0;
305             return TRUE;
306         }
307         said_yes_to_attack = TRUE; /* don't ask again */
308     }
309 
310     return FALSE;
311 }
312 
313 /*
314  * It is unchivalrous for a knight to attack the defenseless or from behind.
315  */
316 void
check_caitiff(struct monst * mtmp)317 check_caitiff(struct monst *mtmp)
318 {
319     if (u.ualign.record <= -10)
320         return;
321 
322     if (Role_if(PM_KNIGHT) && u.ualign.type == A_LAWFUL
323         && (!mtmp->mcanmove || mtmp->msleeping
324             || (mtmp->mflee && !mtmp->mavenge))) {
325         You("caitiff!");
326         adjalign(-1);
327     } else if (Role_if(PM_SAMURAI) && mtmp->mpeaceful) {
328         /* attacking peaceful creatures is bad for the samurai's giri */
329         You("dishonorably attack the innocent!");
330         adjalign(-1);
331     }
332 }
333 
334 int
find_roll_to_hit(struct monst * mtmp,uchar aatyp,struct obj * weapon,int * attk_count,int * role_roll_penalty)335 find_roll_to_hit(struct monst *mtmp,
336                  uchar aatyp,        /* usually AT_WEAP or AT_KICK */
337                  struct obj *weapon, /* uwep or uswapwep or NULL */
338                  int *attk_count,
339                  int *role_roll_penalty)
340 {
341     int tmp, tmp2;
342 
343     *role_roll_penalty = 0; /* default is `none' */
344 
345     tmp = 1 + Luck + abon() + find_mac(mtmp) + u.uhitinc
346           + maybe_polyd(g.youmonst.data->mlevel, u.ulevel);
347 
348     /* some actions should occur only once during multiple attacks */
349     if (!(*attk_count)++) {
350         /* knight's chivalry or samurai's giri */
351         check_caitiff(mtmp);
352     }
353 
354     /* adjust vs. (and possibly modify) monster state */
355     if (mtmp->mstun)
356         tmp += 2;
357     if (mtmp->mflee)
358         tmp += 2;
359 
360     if (mtmp->msleeping) {
361         wakeup(mtmp, FALSE, FALSE);
362         tmp += 2;
363     }
364     if (!mtmp->mcanmove) {
365         tmp += 4;
366         if (!rn2(10)) {
367             mtmp->mcanmove = 1;
368             mtmp->mfrozen = 0;
369         }
370     }
371 
372     /* role/race adjustments */
373     if (Role_if(PM_MONK) && !Upolyd) {
374         if (uarm)
375             tmp -= (*role_roll_penalty = CUMBERSOME_ARMOR_PENALTY);
376         else if (!uwep && !uarms)
377             tmp += (u.ulevel / 3) + 2;
378     }
379     if (is_orc(mtmp->data)
380         && maybe_polyd(is_elf(g.youmonst.data), Race_if(PM_ELF)))
381         tmp++;
382 
383     /* encumbrance: with a lot of luggage, your agility diminishes */
384     if ((tmp2 = near_capacity()) != 0)
385         tmp -= (tmp2 * 2) - 1;
386     if (u.utrap)
387         tmp -= 3;
388 
389     /*
390      * hitval applies if making a weapon attack while wielding a weapon;
391      * weapon_hit_bonus applies if doing a weapon attack even bare-handed
392      * or if kicking as martial artist
393      */
394     if (aatyp == AT_WEAP || aatyp == AT_CLAW) {
395         if (weapon)
396             tmp += hitval(weapon, mtmp);
397         tmp += weapon_hit_bonus(weapon);
398     } else if (aatyp == AT_KICK && martial_bonus()) {
399         tmp += weapon_hit_bonus((struct obj *) 0);
400     }
401 
402     return tmp;
403 }
404 
405 /* try to attack; return False if monster evaded;
406    u.dx and u.dy must be set */
407 boolean
do_attack(struct monst * mtmp)408 do_attack(struct monst *mtmp)
409 {
410     register struct permonst *mdat = mtmp->data;
411 
412     /* This section of code provides protection against accidentally
413      * hitting peaceful (like '@') and tame (like 'd') monsters.
414      * Protection is provided as long as player is not: blind, confused,
415      * hallucinating or stunned.
416      * changes by wwp 5/16/85
417      * More changes 12/90, -dkh-. if its tame and safepet, (and protected
418      * 07/92) then we assume that you're not trying to attack. Instead,
419      * you'll usually just swap places if this is a movement command
420      */
421     /* Intelligent chaotic weapons (Stormbringer) want blood */
422     if (is_safemon(mtmp) && !g.context.forcefight) {
423         if (!uwep || uwep->oartifact != ART_STORMBRINGER) {
424             /* There are some additional considerations: this won't work
425              * if in a shop or Punished or you miss a random roll or
426              * if you can walk thru walls and your pet cannot (KAA) or
427              * if your pet is a long worm with a tail.
428              * There's also a chance of displacing a "frozen" monster:
429              * sleeping monsters might magically walk in their sleep.
430              * This block of code used to only be called for pets; now
431              * that it also applies to peacefuls, non-pets mustn't be
432              * forced to flee.
433              */
434             boolean foo = (Punished || !rn2(7)
435                            || (is_longworm(mtmp->data) && mtmp->wormno)
436                            || (IS_ROCK(levl[u.ux][u.uy].typ)
437                                && !passes_walls(mtmp->data))),
438                     inshop = FALSE;
439             char *p;
440 
441             /* only check for in-shop if don't already have reason to stop */
442             if (!foo) {
443                 for (p = in_rooms(mtmp->mx, mtmp->my, SHOPBASE); *p; p++)
444                     if (tended_shop(&g.rooms[*p - ROOMOFFSET])) {
445                         inshop = TRUE;
446                         break;
447                     }
448             }
449             if (inshop || foo) {
450                 char buf[BUFSZ];
451 
452                 if (mtmp->mtame) /* see 'additional considerations' above */
453                 monflee(mtmp, rnd(6), FALSE, FALSE);
454                 Strcpy(buf, y_monnam(mtmp));
455                 buf[0] = highc(buf[0]);
456                 You("stop.  %s is in the way!", buf);
457                 end_running(TRUE);
458                 return TRUE;
459             } else if (mtmp->mfrozen || mtmp->msleeping || !mtmp->mcanmove
460                        || (mtmp->data->mmove == 0 && rn2(6))) {
461                 pline("%s doesn't seem to move!", Monnam(mtmp));
462                 end_running(TRUE);
463                 return TRUE;
464             } else
465                 return FALSE;
466         }
467     }
468 
469     /* possibly set in attack_checks;
470        examined in known_hitum, called via hitum or hmonas below */
471     g.override_confirmation = FALSE;
472     /* attack_checks() used to use <u.ux+u.dx,u.uy+u.dy> directly, now
473        it uses g.bhitpos instead; it might map an invisible monster there */
474     g.bhitpos.x = u.ux + u.dx;
475     g.bhitpos.y = u.uy + u.dy;
476     g.notonhead = (g.bhitpos.x != mtmp->mx || g.bhitpos.y != mtmp->my);
477     if (attack_checks(mtmp, uwep))
478         return TRUE;
479 
480     if (Upolyd && noattacks(g.youmonst.data)) {
481         /* certain "pacifist" monsters don't attack */
482         You("have no way to attack monsters physically.");
483         mtmp->mstrategy &= ~STRAT_WAITMASK;
484         goto atk_done;
485     }
486 
487     if (check_capacity("You cannot fight while so heavily loaded.")
488         /* consume extra nutrition during combat; maybe pass out */
489         || overexertion())
490         goto atk_done;
491 
492     if (u.twoweap && !can_twoweapon())
493         untwoweapon();
494 
495     if (g.unweapon) {
496         g.unweapon = FALSE;
497         if (flags.verbose) {
498             if (uwep)
499                 You("begin bashing monsters with %s.", yname(uwep));
500             else if (!cantwield(g.youmonst.data))
501                 You("begin %s monsters with your %s %s.",
502                     ing_suffix(Role_if(PM_MONK) ? "strike" : "bash"),
503                     uarmg ? "gloved" : "bare", /* Del Lamb */
504                     makeplural(body_part(HAND)));
505         }
506     }
507     exercise(A_STR, TRUE); /* you're exercising muscles */
508     /* andrew@orca: prevent unlimited pick-axe attacks */
509     u_wipe_engr(3);
510 
511     /* Is the "it died" check actually correct? */
512     if (mdat->mlet == S_LEPRECHAUN && !mtmp->mfrozen && !mtmp->msleeping
513         && !mtmp->mconf && mtmp->mcansee && !rn2(7)
514         && (m_move(mtmp, 0) == 2 /* it died */
515             || mtmp->mx != u.ux + u.dx
516             || mtmp->my != u.uy + u.dy)) { /* it moved */
517         You("miss wildly and stumble forwards.");
518         return FALSE;
519     }
520 
521     if (Upolyd)
522         (void) hmonas(mtmp);
523     else
524         (void) hitum(mtmp, g.youmonst.data->mattk);
525     mtmp->mstrategy &= ~STRAT_WAITMASK;
526 
527  atk_done:
528     /* see comment in attack_checks() */
529     /* we only need to check for this if we did an attack_checks()
530      * and it returned 0 (it's okay to attack), and the monster didn't
531      * evade.
532      */
533     if (g.context.forcefight && !DEADMONSTER(mtmp) && !canspotmon(mtmp)
534         && !glyph_is_invisible(levl[u.ux + u.dx][u.uy + u.dy].glyph)
535         /* thiefstone may have teleported target */
536         && (m_at(u.ux + u.dx, u.uy + u.dy) == mtmp
537             || !cansee(u.ux + u.dx, u.uy + u.dy))
538         && !(u.uswallow && mtmp == u.ustuck))
539         map_invisible(u.ux + u.dx, u.uy + u.dy);
540 
541     return TRUE;
542 }
543 
544 /* really hit target monster; returns TRUE if it still lives */
545 static boolean
known_hitum(struct monst * mon,struct obj * weapon,int * mhit,int rollneeded,int armorpenalty,struct attack * uattk,int dieroll)546 known_hitum(struct monst *mon, struct obj *weapon, int *mhit, int rollneeded,
547             int armorpenalty, /* for monks */
548             struct attack *uattk,
549             int dieroll)
550 {
551     boolean malive = TRUE,
552             /* hmon() might destroy weapon; remember aspect for cutworm */
553             slice_or_chop = (weapon && (is_blade(weapon) || is_axe(weapon)));
554 
555     if (g.override_confirmation) {
556         /* this may need to be generalized if weapons other than
557            Stormbringer acquire similar anti-social behavior... */
558         if (flags.verbose)
559             Your("bloodthirsty blade attacks!");
560     }
561 
562     if (!*mhit) {
563         missum(mon, uattk, (rollneeded + armorpenalty > dieroll));
564     } else {
565         int oldhp = mon->mhp;
566         long oldweaphit = u.uconduct.weaphit;
567 
568         /* KMH, conduct */
569         if (weapon && (weapon->oclass == WEAPON_CLASS || is_weptool(weapon)))
570             u.uconduct.weaphit++;
571 
572         /* we hit the monster; be careful: it might die or
573            be knocked into a different location */
574         g.notonhead = (mon->mx != g.bhitpos.x || mon->my != g.bhitpos.y);
575         malive = hmon(mon, weapon, HMON_MELEE, dieroll);
576         if (malive) {
577             /* monster still alive */
578             if (!rn2(25) && mon->mhp < mon->mhpmax / 2
579                 && !(u.uswallow && mon == u.ustuck)) {
580                 /* maybe should regurgitate if swallowed? */
581                 monflee(mon, !rn2(3) ? rnd(100) : 0, FALSE, TRUE);
582 
583                 if (u.ustuck == mon && !u.uswallow && !sticks(g.youmonst.data))
584                     set_ustuck((struct monst *) 0);
585             }
586             /* Vorpal Blade hit converted to miss */
587             /* could be headless monster or worm tail */
588             if (mon->mhp == oldhp) {
589                 *mhit = 0;
590                 /* a miss does not break conduct */
591                 u.uconduct.weaphit = oldweaphit;
592             }
593             if (mon->wormno && *mhit)
594                 cutworm(mon, g.bhitpos.x, g.bhitpos.y, slice_or_chop);
595         }
596         if (u.uconduct.weaphit && !oldweaphit)
597             livelog_write_string(LL_CONDUCT,
598                                  "hit with a wielded weapon for the first time");
599     }
600     return malive;
601 }
602 
603 /* return TRUE iff no peaceful targets are found in cleaving range to the left
604  * and right of the target space
605  * assumes u.dx and u.dy have been set */
606 static boolean
should_cleave(void)607 should_cleave(void)
608 {
609     int i;
610     boolean bystanders = FALSE;
611     /* find the direction toward primary target */
612     int dir = xytod(u.dx, u.dy);
613     if (dir > 7) {
614         impossible("should_cleave: unknown target direction");
615         return FALSE; /* better safe than sorry */
616     }
617     /* loop over dir+1 % 8 and dir+7 % 8 (the clockwise and anticlockwise
618      * directions); a monster standing at dir itself is NOT checked */
619     for (i = dir + 1; i <= dir + 7; i += 6) {
620         int realdir = i % 8;
621         struct monst *mtmp = m_at(u.ux + xdir[realdir], u.uy + ydir[realdir]);
622         if (mtmp && canspotmon(mtmp) && mtmp->mpeaceful) {
623             bystanders = TRUE;
624         }
625     }
626     if (bystanders) {
627         pline("You will hit peaceful creatures if you attack in an arc.");
628         if (!paranoid_query(ParanoidHit, "Do it anyway?")) {
629             return FALSE;
630         }
631     }
632     return TRUE;
633 }
634 
635 /* hit the monster next to you and the monsters to the left and right of it;
636    return False if the primary target is killed, True otherwise */
637 static boolean
hitum_cleave(struct monst * target,struct attack * uattk)638 hitum_cleave(struct monst *target, /* non-Null; forcefight at nothing doesn't
639                                       cleave... */
640              struct attack *uattk) /* ... but we don't enforce that here; Null
641                                       works ok */
642 {
643     /* swings will be delivered in alternate directions; with consecutive
644        attacks it will simulate normal swing and backswing; when swings
645        are non-consecutive, hero will sometimes start a series of attacks
646        with a backswing--that doesn't impact actual play, just spoils the
647        simulation attempt a bit */
648     static boolean clockwise = FALSE;
649     unsigned i;
650     coord save_bhitpos;
651     int count, umort, x = u.ux, y = u.uy;
652 
653     /* find the direction toward primary target */
654     for (i = 0; i < 8; ++i)
655         if (xdir[i] == u.dx && ydir[i] == u.dy)
656             break;
657     if (i == 8) {
658         impossible("hitum_cleave: unknown target direction [%d,%d,%d]?",
659                    u.dx, u.dy, u.dz);
660         return TRUE; /* target hasn't been killed */
661     }
662     /* adjust direction by two so that loop's increment (for clockwise)
663        or decrement (for counter-clockwise) will point at the spot next
664        to primary target */
665     i = (i + (clockwise ? 6 : 2)) % 8;
666     umort = u.umortality; /* used to detect life-saving */
667     save_bhitpos = g.bhitpos;
668 
669     /*
670      * Three attacks:  adjacent to primary, primary, adjacent on other
671      * side.  Primary target must be present or we wouldn't have gotten
672      * here (forcefight at thin air won't 'cleave').  However, the
673      * first attack might kill it (gas spore explosion, weak long worm
674      * occupying both spots) so we don't assume that it's still present
675      * on the second attack.
676      */
677     for (count = 3; count > 0; --count) {
678         struct monst *mtmp;
679         int tx, ty, tmp, dieroll, mhit, attknum, armorpenalty;
680 
681         /* ++i, wrap 8 to i=0 /or/ --i, wrap -1 to i=7 */
682         i = (i + (clockwise ? 1 : 7)) % 8;
683 
684         tx = x + xdir[i], ty = y + ydir[i]; /* current target location */
685         if (!isok(tx, ty))
686             continue;
687         mtmp = m_at(tx, ty);
688         if (!mtmp) {
689             if (glyph_is_invisible(levl[tx][ty].glyph))
690                 (void) unmap_invisible(tx, ty);
691             continue;
692         }
693 
694         tmp = find_roll_to_hit(mtmp, uattk->aatyp, uwep,
695                                &attknum, &armorpenalty);
696         dieroll = rnd(20);
697         mhit = (tmp > dieroll);
698         g.bhitpos.x = tx, g.bhitpos.y = ty; /* normally set up by
699 					       do_attack() */
700         (void) known_hitum(mtmp, uwep, &mhit, tmp, armorpenalty,
701                            uattk, dieroll);
702         (void) passive(mtmp, uwep, mhit, !DEADMONSTER(mtmp), AT_WEAP, !uwep);
703 
704         /* stop attacking if weapon is gone or hero got killed and
705            life-saved after passive counter-attack */
706         if (!uwep || u.umortality > umort)
707             break;
708     }
709     /* set up for next time */
710     clockwise = !clockwise; /* alternate */
711     g.bhitpos = save_bhitpos; /* in case somebody relies on bhitpos
712                              * designating the primary target */
713 
714     /* return False if primary target died, True otherwise; note: if 'target'
715        was nonNull upon entry then it's still nonNull even if *target died */
716     return (target && DEADMONSTER(target)) ? FALSE : TRUE;
717 }
718 
719 /* hit target monster; returns TRUE if it still lives */
720 static boolean
hitum(struct monst * mon,struct attack * uattk)721 hitum(struct monst *mon, struct attack *uattk)
722 {
723     boolean malive, wep_was_destroyed = FALSE;
724     struct obj *wepbefore = uwep;
725     int armorpenalty, attknum = 0, x = u.ux + u.dx, y = u.uy + u.dy,
726                       tmp = find_roll_to_hit(mon, uattk->aatyp, uwep,
727                                              &attknum, &armorpenalty);
728     int dieroll = rnd(20);
729     int mhit = (tmp > dieroll || u.uswallow);
730 
731     /* Cleaver attacks three spots, 'mon' and one on either side of 'mon';
732        it can't be part of dual-wielding but we guard against that anyway;
733        cleave return value reflects status of primary target ('mon') */
734     if (uwep && uwep->oartifact == ART_CLEAVER && !u.twoweap
735         && !u.uswallow && !u.ustuck && !NODIAG(u.umonnum) && should_cleave())
736         return hitum_cleave(mon, uattk);
737 
738     if (tmp > dieroll)
739         exercise(A_DEX, TRUE);
740     /* g.bhitpos is set up by caller */
741     malive = known_hitum(mon, uwep, &mhit, tmp, armorpenalty, uattk, dieroll);
742     if (wepbefore && !uwep)
743         wep_was_destroyed = TRUE;
744     (void) passive(mon, uwep, mhit, malive, AT_WEAP, wep_was_destroyed);
745 
746     /* second attack for two-weapon combat; won't occur if Stormbringer
747        overrode confirmation (assumes Stormbringer is primary weapon)
748        or if the monster was killed or knocked to different location */
749     if (u.twoweap && !g.override_confirmation && malive && m_at(x, y) == mon) {
750         tmp = find_roll_to_hit(mon, uattk->aatyp, uswapwep, &attknum,
751                                &armorpenalty);
752         dieroll = rnd(20);
753         mhit = (tmp > dieroll || u.uswallow);
754         malive = known_hitum(mon, uswapwep, &mhit, tmp, armorpenalty, uattk,
755                              dieroll);
756         /* second passive counter-attack only occurs if second attack hits */
757         if (mhit)
758             (void) passive(mon, uswapwep, mhit, malive, AT_WEAP, !uswapwep);
759     }
760     return malive;
761 }
762 
763 /* general "damage monster" routine; return True if mon still alive */
764 boolean
hmon(struct monst * mon,struct obj * obj,int thrown,int dieroll)765 hmon(struct monst *mon,
766      struct obj *obj,
767      int thrown, /* HMON_xxx (0 => hand-to-hand, other => ranged) */
768      int dieroll)
769 {
770     boolean result, anger_guards;
771 
772     anger_guards = (mon->mpeaceful
773                     && (mon->ispriest || mon->isshk || is_watch(mon->data)));
774     result = hmon_hitmon(mon, obj, thrown, dieroll);
775     if (mon->ispriest && !rn2(2))
776         ghod_hitsu(mon);
777     if (anger_guards)
778         (void) angry_guards(!!Deaf);
779     return result;
780 }
781 
782 /* guts of hmon() */
783 static boolean
hmon_hitmon(struct monst * mon,struct obj * obj,int thrown,int dieroll)784 hmon_hitmon(struct monst *mon,
785            struct obj *obj,
786            int thrown, /* HMON_xxx (0 => hand-to-hand, other => ranged) */
787            int dieroll)
788 {
789     int tmp;
790     struct permonst *mdat = mon->data;
791     /* The basic reason we need all these booleans is that we don't want
792      * a "hit" message when a monster dies, so we have to know how much
793      * damage it did _before_ outputting a hit message, but any messages
794      * associated with the damage don't come out until _after_ outputting
795      * a hit message.
796      */
797     boolean hittxt = FALSE, destroyed = FALSE, already_killed = FALSE;
798     boolean get_dmg_bonus = TRUE;
799     boolean ispoisoned = FALSE, needpoismsg = FALSE,
800             unpoisonmsg = FALSE;
801     boolean lightobj = FALSE;
802     boolean use_weapon_skill = FALSE, train_weapon_skill = FALSE;
803     boolean unarmed = !uwep && !uarm && !uarms;
804     boolean hand_to_hand = (thrown == HMON_MELEE
805                             /* not grapnels; applied implies uwep */
806                             || (thrown == HMON_APPLIED && is_pole(uwep)));
807     int jousting = 0;
808     struct obj *hated_obj = NULL;
809     int artimsg = ARTIFACTHIT_NOMSG;
810     int wtype;
811     struct obj *monwep;
812     char saved_oname[BUFSZ];
813     int saved_mhp = mon->mhp;
814 
815     saved_oname[0] = '\0';
816 
817     /* Awaken nearby monsters. A stealthy hero causes less noise. */
818     if (!(is_silent(g.youmonst.data) && helpless(mon))
819         && rn2(Stealth ? 10 : 5)) {
820         int base_combat_noise = combat_noise(&mons[g.urace.malenum]);
821         wake_nearto(mon->mx, mon->my, Stealth ? base_combat_noise/2
822                                               : base_combat_noise);
823     }
824 
825     wakeup(mon, TRUE, TRUE);
826     if (!obj) { /* attack with bare hands */
827         if (noncorporeal(mdat)) {
828             tmp = 0;
829         }
830         else {
831             /* note: 1..2 or 1..4 can be substantiallly increased by
832                strength bonus or skill bonus, usually both... */
833             tmp = rnd(!martial_bonus() ? 2 : 4);
834             use_weapon_skill = TRUE;
835             train_weapon_skill = (tmp > 1);
836         }
837 
838         /* Blessed gloves give bonuses when fighting 'bare-handed'.  So do
839            rings or gloves made of a hated material.  Note:  rings are worn
840            under gloves, so you don't get both bonuses, and two hated rings
841            don't give double bonus. */
842         tmp += special_dmgval(&g.youmonst, mon, (W_ARMG | W_RINGL | W_RINGR),
843                               &hated_obj);
844     } else {
845         if (!(artifact_light(obj) && obj->lamplit))
846             Strcpy(saved_oname, cxname(obj));
847         else
848             Strcpy(saved_oname, bare_artifactname(obj));
849         if (obj->oclass == WEAPON_CLASS || is_weptool(obj)
850             || obj->oclass == GEM_CLASS) {
851             /* is it not a melee weapon? */
852             if (/* if you strike with a bow... */
853                 is_launcher(obj)
854                 /* or strike with a missile in your hand... */
855                 || (!thrown && (is_missile(obj) || is_ammo(obj)))
856                 /* or use a pole at short range and not mounted... */
857                 || (!thrown && !u.usteed && is_pole(obj))
858                 /* or throw a missile without the proper bow... */
859                 || (is_ammo(obj) && (thrown != HMON_THROWN
860                                      || !ammo_and_launcher(obj, uwep)))) {
861                 /* then do only 1-2 points of damage and don't use or
862                    train weapon's skill */
863                 if (noncorporeal(mdat) && !shade_glare(obj))
864                     tmp = 0;
865                 else
866                     tmp = rnd(2);
867                 if (mon_hates_material(mon, obj->material)) {
868                     hated_obj = obj;
869                     tmp += rnd(sear_damage(obj->material));
870                 }
871                 if (!thrown && obj == uwep) {
872                     if (obj->otyp == THIEFSTONE && obj->blessed
873                         && mon->data == &mons[PM_GOLD_GOLEM]) {
874                         if (thiefstone_tele_mon(obj, mon)) {
875                             hittxt = TRUE;
876                         }
877                     }
878                     else if (obj->otyp == BOOMERANG && rnl(4) == 4 - 1) {
879                         boolean more_than_1 = (obj->quan > 1L);
880 
881                         pline("As you hit %s, %s%s breaks into splinters.",
882                             mon_nam(mon), more_than_1 ? "one of " : "",
883                             yname(obj));
884                         if (!more_than_1)
885                             uwepgone(); /* set g.unweapon */
886                         useup(obj);
887                         if (!more_than_1)
888                             obj = (struct obj *) 0;
889                         hittxt = TRUE;
890                         if (noncorporeal(mdat))
891                             tmp++;
892                     }
893                 }
894             } else {
895                 /* "normal" weapon usage */
896                 use_weapon_skill = TRUE;
897                 tmp = dmgval(obj, mon);
898                 /* a minimal hit doesn't exercise proficiency */
899                 train_weapon_skill = (tmp > 1);
900                 /* special attack actions */
901                 if (!train_weapon_skill || mon == u.ustuck || u.twoweap
902                     /* Cleaver can hit up to three targets at once so don't
903                        let it also hit from behind or shatter foes' weapons */
904                     || (hand_to_hand && obj->oartifact == ART_CLEAVER)) {
905                     ; /* no special bonuses */
906                 }
907                 else if (mdat->mlet == S_VAMPIRE && obj->material == WOOD
908                     && (objects[obj->otyp].oc_dir & PIERCE)
909                     && which_armor(mon, W_ARM) == 0 && dieroll == 1) {
910                     /* Critical hit with a piercing wooden weapon = staked
911                      * through the heart! */
912                     tmp = mon->mhp + 100;
913                     if (Hallucination)
914                         pline("Staked through the heart, and you're to blame!");
915                     else
916                         You("stake %s through the heart!", mon_nam(mon));
917                     hittxt = TRUE;
918                     /* don't let negative daminc prevent from killing (and
919                      * positive won't matter anyway) */
920                     get_dmg_bonus = FALSE;
921                     /* also don't let skill-based damage penalties prevent this
922                      * from killing; cancel this out now (valid_weapon_attack is
923                      * guaranteed from the above if) */
924                     tmp -= weapon_dam_bonus(uwep);
925                 }
926                 else if (mdat->mlet == S_GIANT && uslinging()
927                          && thrown == HMON_THROWN
928                          && ammo_and_launcher(obj, uwep)
929                          && P_SKILL(P_SLING) >= P_SKILLED && dieroll > 1
930                          && !rn2(P_SKILL(P_SLING) == P_SKILLED ? 2 : 1)) {
931                     /* With a critical hit, a skilled slinger can bring down
932                      * even the mightiest of giants. */
933                     tmp = mon->mhp + 100;
934                     pline("%s crushes %s forehead!", The(mshot_xname(obj)),
935                           s_suffix(mon_nam(mon)));
936                     hittxt = TRUE;
937                     /* Same as above; account for negative udaminc and skill
938                      * damage penalties. (In the really odd situation where for
939                      * some reason being Skilled+ gives a penalty?) */
940                     get_dmg_bonus = FALSE;
941                     tmp -= weapon_dam_bonus(uwep);
942                 }
943                 else if (Role_if(PM_ROGUE) && !Upolyd && hand_to_hand
944                            /* multi-shot throwing is too powerful here */
945                          && (mon->mflee || mon->mtrapped || mon->mfrozen
946                              || !mon->mcanmove || mon->msleeping || mon->mstun
947                              || mon->mconf || mon->mblinded)) {
948                     /* Cap the contribution of ulevel based on skill level.
949                      * Restricted (or no associated skill) - d2 maximum
950                      * Unskilled                           - d4 maximum
951                      * Basic                               - d10 maximum
952                      * Skilled                             - d20 maximum
953                      * Expert                              - d30 maximum
954                      * Then give Basic and above a small flat bonus that is not
955                      * tied to ulevel.
956                      */
957                     int cap = 2, bonus = 0;
958                     const char *adjective, *adverb1, *adverb2;
959                     adjective = NULL; /* not "" because it goes to adj_monnam */
960                     adverb1 = "deftly ";
961                     adverb2 = "";
962                     /* "You [adverb1] strike the [adjective] foo [adverb2]!" */
963                     if (mon->msleeping) {
964                         adjective = "sleeping";
965                     }
966                     else if (mon->mblinded) {
967                         adjective = "blinded";
968                     }
969                     else if (mon->mconf || mon->mstun) {
970                         adjective = "off-balance";
971                     }
972                     else if (mon->mtrapped || mon->mfrozen || !mon->mcanmove) {
973                         adjective = "helpless";
974                     }
975                     else if (mon->mflee) {
976                         adverb1 = "";
977                         adverb2 = " from behind";
978                     }
979                     You("%sstrike %s%s!", adverb1, adj_monnam(mon, adjective),
980                         adverb2);
981                     if ((wtype = uwep_skill_type()) != P_NONE) {
982                         if (P_SKILL(wtype) == P_ISRESTRICTED) {
983                             cap = 2;
984                         }
985                         else if (P_SKILL(wtype) == P_UNSKILLED) {
986                             cap = 4;
987                         }
988                         else {
989                             cap = (P_SKILL(wtype) - 1) * 10;
990                             bonus = P_SKILL(wtype) - 1;
991                         }
992                     }
993                     if (u.ulevel < cap) {
994                         cap = u.ulevel; /* e.g. Expert but only XL 10 */
995                     }
996                     tmp += rnd(cap) + bonus;
997                     hittxt = TRUE;
998                 } else if (dieroll == 2 && obj == uwep
999                            && obj->oclass == WEAPON_CLASS
1000                            && (bimanual(obj)
1001                                || (Role_if(PM_SAMURAI) && obj->otyp == KATANA
1002                                    && !uarms))
1003                            && ((wtype = uwep_skill_type()) != P_NONE
1004                                && P_SKILL(wtype) >= P_SKILLED)
1005                            && ((monwep = MON_WEP(mon)) != 0
1006                                && !is_flimsy(monwep)
1007                                && !obj_resists(monwep,
1008                                        50 + 15 * (greatest_erosion(obj)
1009                                                   - greatest_erosion(monwep)),
1010                                                100))) {
1011                     /*
1012                      * 2.5% chance of shattering defender's weapon when
1013                      * using a two-handed weapon; less if uwep is rusted.
1014                      * [dieroll == 2 is most successful non-beheading or
1015                      * -bisecting hit, in case of special artifact damage;
1016                      * the percentage chance is (1/20)*(50/100).]
1017                      * If attacker's weapon is rustier than defender's,
1018                      * the obj_resists chance is increased so the shatter
1019                      * chance is decreased; if less rusty, then vice versa.
1020                      */
1021                     setmnotwielded(mon, monwep);
1022                     mon->weapon_check = NEED_WEAPON;
1023                     pline("%s from the force of your blow!",
1024                           Yobjnam2(monwep, "shatter"));
1025                     m_useupall(mon, monwep);
1026                     /* If someone just shattered MY weapon, I'd flee! */
1027                     if (rn2(4)) {
1028                         monflee(mon, d(2, 3), TRUE, TRUE);
1029                     }
1030                     hittxt = TRUE;
1031                 }
1032                 if (obj->oartifact) {
1033                     artimsg = artifact_hit(&g.youmonst, mon, obj, &tmp, dieroll);
1034                     /* artifact_hit updates 'tmp' but doesn't inflict any
1035                        damage; however, it might cause carried items to be
1036                        destroyed and they might do so */
1037                     if (artimsg) {
1038                         if (DEADMONSTER(mon)) /* artifact killed monster */
1039                             return FALSE;
1040 			/* perhaps artifact tried to behead a headless monster */
1041                         if (tmp == 0)
1042                             return TRUE;
1043                         hittxt = TRUE;
1044                     }
1045                 }
1046                 if (mon_hates_material(mon, obj->material)) {
1047                     /* dmgval() already added bonus damage */
1048                     hated_obj = obj;
1049                 }
1050                 if (artifact_light(obj) && obj->lamplit
1051                     && mon_hates_light(mon))
1052                     lightobj = TRUE;
1053                 if (u.usteed && !thrown && tmp > 0
1054                     && weapon_type(obj) == P_LANCE && mon != u.ustuck) {
1055                     jousting = joust(mon, obj);
1056                     /* exercise skill even for minimal damage hits */
1057                     if (jousting)
1058                         train_weapon_skill = TRUE;
1059                 }
1060                 if (thrown == HMON_THROWN
1061                     && (is_ammo(obj) || is_missile(obj))) {
1062                     if (ammo_and_launcher(obj, uwep)) {
1063                         /* elves and samurai do extra damage using their own
1064                            bows with own arrows; they're highly trained */
1065                         if (Role_if(PM_SAMURAI) && obj->otyp == YA
1066                             && uwep->otyp == YUMI)
1067                             tmp++;
1068                         else if (Race_if(PM_ELF) && obj->otyp == ELVEN_ARROW
1069                                  && uwep->otyp == ELVEN_BOW)
1070                             tmp++;
1071                         train_weapon_skill = (tmp > 0);
1072                     }
1073                 }
1074                 if ((obj->opoisoned || permapoisoned(obj))
1075                     && is_poisonable(obj)) {
1076                     /* Since non-thrown poison sources do more damage, they
1077                      * would be way too powerful to poison on each hit - so
1078                      * artificially limit the opportunity for it to score a
1079                      * hit.
1080                      * However, we don't want to only cause alignment penalties
1081                      * for using Grimtooth when it actually causes poison: the
1082                      * intent was to use a poisoned weapon whether or not it
1083                      * actually poisoned anything. */
1084                     if (Role_if(PM_SAMURAI)) {
1085                         You("are in dishonor for using a poisoned weapon.");
1086                         adjalign(-sgn(u.ualign.type));
1087                     } else if (u.ualign.type == A_LAWFUL
1088                                && u.ualign.record > -10) {
1089                         You_feel("like an evil coward for using a poisoned weapon.");
1090                         adjalign(-1);
1091                     }
1092                     /* Grimtooth's 1/4 chance of poisoning is based on dieroll,
1093                      * which provides a consistent way to make sure the poison
1094                      * damage is only dealt when the special message is
1095                      * delivered.
1096                      * For other (currently nonexistent) poisoned melee weapons,
1097                      * using dieroll should still work fine. */
1098                     if (thrown == HMON_THROWN || dieroll <= 5) {
1099                         ispoisoned = TRUE;
1100                     }
1101                 }
1102                 /* maybe break your glass weapon or monster's glass armor; put
1103                  * this at the end so that other stuff doesn't have to check obj
1104                  * && obj->whatever all the time */
1105                 if (hand_to_hand) {
1106                     break_glass_obj(obj);
1107                     break_glass_obj(some_armor(mon));
1108                 }
1109 
1110             }
1111 
1112         /* attacking with non-weapons */
1113         } else if (obj->oclass == POTION_CLASS) {
1114             if (obj->quan > 1L)
1115                 obj = splitobj(obj, 1L);
1116             else
1117                 setuwep((struct obj *) 0);
1118             freeinv(obj);
1119             potionhit(mon, obj,
1120                       hand_to_hand ? POTHIT_HERO_BASH : POTHIT_HERO_THROW);
1121             obj = NULL;
1122             if (DEADMONSTER(mon))
1123                 return FALSE; /* killed */
1124             hittxt = TRUE;
1125             /* in case potion effect causes transformation */
1126             mdat = mon->data;
1127             tmp = (noncorporeal(mdat)) ? 0 : 1;
1128         } else {
1129             if (noncorporeal(mdat) && !shade_aware(obj)) {
1130                 tmp = 0;
1131             } else {
1132                 switch (obj->otyp) {
1133                 case BOULDER:         /* 1d20 */
1134                 case HEAVY_IRON_BALL: /* 1d25 */
1135                 case IRON_CHAIN:      /* 1d4+1 */
1136                     tmp = dmgval(obj, mon);
1137                     if (mon_hates_material(mon, obj->material)) {
1138                         /* dmgval() already added damage, but track hated_obj */
1139                         hated_obj = obj;
1140                     }
1141                     break;
1142                 case MIRROR:
1143                     if (breaktest(obj)) {
1144                         You("break %s.  That's bad luck!", ysimple_name(obj));
1145                         change_luck(-2);
1146                         useup(obj);
1147                         obj = (struct obj *) 0;
1148                         unarmed = FALSE; /* avoid obj==0 confusion */
1149                         get_dmg_bonus = FALSE;
1150                         hittxt = TRUE;
1151                     }
1152                     tmp = 1;
1153                     break;
1154                 case EXPENSIVE_CAMERA:
1155                     You("succeed in destroying %s.  Congratulations!",
1156                         ysimple_name(obj));
1157                     release_camera_demon(obj, u.ux, u.uy);
1158                     useup(obj);
1159                     return TRUE;
1160                 case CORPSE: /* fixed by polder@cs.vu.nl */
1161                     if (touch_petrifies(&mons[obj->corpsenm])) {
1162                         tmp = 1;
1163                         hittxt = TRUE;
1164                         You("hit %s with %s.", mon_nam(mon),
1165                             corpse_xname(obj, (const char *) 0,
1166                                          obj->dknown ? CXN_PFX_THE
1167                                                      : CXN_ARTICLE));
1168                         obj->dknown = 1;
1169                         if (!munstone(mon, TRUE))
1170                             minstapetrify(mon, TRUE);
1171                         if (resists_ston(mon))
1172                             break;
1173                         /* note: hp may be <= 0 even if munstoned==TRUE */
1174                         return (boolean) !DEADMONSTER(mon);
1175 #if 0
1176                     } else if (touch_petrifies(mdat)) {
1177                         ; /* maybe turn the corpse into a statue? */
1178 #endif
1179                     }
1180                     tmp = (obj->corpsenm >= LOW_PM ? mons[obj->corpsenm].msize
1181                                                    : 0) + 1;
1182                     break;
1183 
1184 #define useup_eggs(o)                    \
1185     do {                                 \
1186         if (thrown)                      \
1187             obfree(o, (struct obj *) 0); \
1188         else                             \
1189             useupall(o);                 \
1190         o = (struct obj *) 0;            \
1191     } while (0) /* now gone */
1192                 case EGG: {
1193                     long cnt = obj->quan;
1194 
1195                     tmp = 1; /* nominal physical damage */
1196                     get_dmg_bonus = FALSE;
1197                     hittxt = TRUE; /* message always given */
1198                     /* egg is always either used up or transformed, so next
1199                        hand-to-hand attack should yield a "bashing" mesg */
1200                     if (obj == uwep)
1201                         g.unweapon = TRUE;
1202                     if (obj->spe && obj->corpsenm >= LOW_PM) {
1203                         if (obj->quan < 5L)
1204                             change_luck((schar) - (obj->quan));
1205                         else
1206                             change_luck(-5);
1207                     }
1208 
1209                     if (touch_petrifies(&mons[obj->corpsenm])) {
1210                         /*learn_egg_type(obj->corpsenm);*/
1211                         pline("Splat!  You hit %s with %s %s egg%s!",
1212                               mon_nam(mon),
1213                               obj->known ? "the" : cnt > 1L ? "some" : "a",
1214                               obj->known ? mons[obj->corpsenm].pmnames[NEUTRAL]
1215                                          : "petrifying",
1216                               plur(cnt));
1217                         obj->known = 1; /* (not much point...) */
1218                         useup_eggs(obj);
1219                         if (!munstone(mon, TRUE))
1220                             minstapetrify(mon, TRUE);
1221                         if (resists_ston(mon))
1222                             break;
1223                         return (boolean) (!DEADMONSTER(mon));
1224                     } else { /* ordinary egg(s) */
1225                         const char *eggp = (obj->corpsenm != NON_PM
1226                                             && obj->known)
1227                                     ? the(mons[obj->corpsenm].pmnames[NEUTRAL])
1228                                            : (cnt > 1L) ? "some" : "an";
1229 
1230                         if (strstri(pmname(mon->data, Mgender(mon)), "devil"))
1231                             pline("Deviled egg%s!", plur(cnt));
1232                         else
1233                             You("hit %s with %s egg%s.", mon_nam(mon), eggp,
1234                                 plur(cnt));
1235 
1236                         if (touch_petrifies(mdat) && !stale_egg(obj)) {
1237                             pline_The("egg%s %s alive any more...", plur(cnt),
1238                                       (cnt == 1L) ? "isn't" : "aren't");
1239                             if (obj->timed)
1240                                 obj_stop_timers(obj);
1241                             obj->otyp = ROCK;
1242                             obj->oclass = GEM_CLASS;
1243                             obj->oartifact = 0;
1244                             obj->spe = 0;
1245                             obj->known = obj->dknown = obj->bknown = 0;
1246                             obj->owt = weight(obj);
1247                             if (thrown)
1248                                 place_object(obj, mon->mx, mon->my);
1249                         } else {
1250                             pline("Splat!");
1251                             useup_eggs(obj);
1252                             exercise(A_WIS, FALSE);
1253                         }
1254                     }
1255                     break;
1256 #undef useup_eggs
1257                 }
1258                 case CLOVE_OF_GARLIC: /* no effect against demons */
1259                     if (is_undead(mdat) || is_vampshifter(mon)) {
1260                         monflee(mon, d(2, 4), FALSE, TRUE);
1261                     }
1262                     tmp = 1;
1263                     break;
1264                 case CREAM_PIE:
1265                 case BLINDING_VENOM:
1266                     if (can_blnd(&g.youmonst, mon,
1267                                  (uchar) ((obj->otyp == BLINDING_VENOM)
1268                                              ? AT_SPIT
1269                                              : AT_WEAP),
1270                                  obj)) {
1271                         if (Blind) {
1272                             pline(obj->otyp == CREAM_PIE ? "Splat!"
1273                                                          : "Splash!");
1274                         } else if (obj->otyp == BLINDING_VENOM) {
1275                             pline_The("venom blinds %s%s!", mon_nam(mon),
1276                                       mon->mcansee ? "" : " further");
1277                         } else {
1278                             char *whom = mon_nam(mon);
1279                             char *what = The(xname(obj));
1280 
1281                             if (!thrown && obj->quan > 1L)
1282                                 what = An(singular(obj, xname));
1283                             /* note: s_suffix returns a modifiable buffer */
1284                             if (haseyes(mdat)
1285                                 && mdat != &mons[PM_FLOATING_EYE])
1286                                 whom = strcat(strcat(s_suffix(whom), " "),
1287                                               mbodypart(mon, FACE));
1288                             pline("%s %s over %s!", what,
1289                                   vtense(what, "splash"), whom);
1290                         }
1291                         setmangry(mon, TRUE);
1292                         mon->mcansee = 0;
1293                         tmp = rn1(25, 21);
1294                         if (((int) mon->mblinded + tmp) > 127)
1295                             mon->mblinded = 127;
1296                         else
1297                             mon->mblinded += tmp;
1298                     } else {
1299                         pline(obj->otyp == CREAM_PIE ? "Splat!" : "Splash!");
1300                         setmangry(mon, TRUE);
1301                     }
1302                     wakeup(mon, TRUE, TRUE);
1303                     {
1304                         boolean more_than_1 = (obj->quan > 1L);
1305 
1306                         if (thrown)
1307                             obfree(obj, (struct obj *) 0);
1308                         else
1309                             useup(obj);
1310 
1311                         if (!more_than_1)
1312                             obj = (struct obj *) 0;
1313                     }
1314                     hittxt = TRUE;
1315                     get_dmg_bonus = FALSE;
1316                     tmp = 0;
1317                     break;
1318                 case ACID_VENOM: /* thrown (or spit) */
1319                     if (resists_acid(mon)) {
1320                         Your("venom hits %s harmlessly.", mon_nam(mon));
1321                         tmp = 0;
1322                     } else {
1323                         Your("venom burns %s!", mon_nam(mon));
1324                         tmp = dmgval(obj, mon);
1325                     }
1326                     {
1327                         boolean more_than_1 = (obj->quan > 1L);
1328 
1329                         if (thrown)
1330                             obfree(obj, (struct obj *) 0);
1331                         else
1332                             useup(obj);
1333 
1334                         if (!more_than_1)
1335                             obj = (struct obj *) 0;
1336                     }
1337                     hittxt = TRUE;
1338                     get_dmg_bonus = FALSE;
1339                     break;
1340                 default:
1341                     /* non-weapons can damage because of their weight */
1342                     /* (but not too much) */
1343                     tmp = obj->owt / 100;
1344                     if (is_wet_towel(obj)) {
1345                         /* wielded wet towel should probably use whip skill
1346                            (but not by setting objects[TOWEL].oc_skill==P_WHIP
1347                            because that would turn towel into a weptool) */
1348                         tmp += obj->spe;
1349                         if (rn2(obj->spe + 1)) /* usually lose some wetness */
1350                             dry_a_towel(obj, -1, TRUE);
1351                     }
1352                     if (tmp < 1)
1353                         tmp = 1;
1354                     else
1355                         tmp = rnd(tmp);
1356                     if (tmp > 6)
1357                         tmp = 6;
1358                     /*
1359                      * Things like wands made of harmful materials can arrive here so
1360                      * we need another check for that.
1361                      */
1362                     if (mon_hates_material(mon, obj->material)) {
1363                         /* dmgval() already added damage, but track hated_obj */
1364                         hated_obj = obj;
1365                     }
1366                 }
1367             }
1368         }
1369     }
1370 
1371     /*
1372      ***** NOTE: perhaps obj is undefined! (if !thrown && BOOMERANG)
1373      *      *OR* if attacking bare-handed!
1374      * Note too: the cases where obj might get destroyed do not
1375      *      set 'use_weapon_skill', bare-handed does.
1376      */
1377 
1378     if (tmp > 0) {
1379         int dmgbonus = 0;
1380 
1381         /*
1382          * Potential bonus (or penalty) from worn ring of increase damage
1383          * (or intrinsic bonus from eating same) or from strength.
1384          */
1385         if (get_dmg_bonus) {
1386             dmgbonus = u.udaminc;
1387             /* throwing using a propellor gets an increase-damage bonus
1388                but not a strength one; other attacks get both */
1389             if (thrown != HMON_THROWN
1390                 || !obj || !uwep || !ammo_and_launcher(obj, uwep)
1391 		|| uslinging())
1392                 dmgbonus += dbon();
1393         }
1394 
1395         /*
1396          * Potential bonus (or penalty) from weapon skill.
1397          * 'use_weapon_skill' is True for hand-to-hand ordinary weapon,
1398          * applied or jousting polearm or lance, thrown missile (dart,
1399          * shuriken, boomerang), or shot ammo (arrow, bolt, rock/gem when
1400          * wielding corresponding launcher).
1401          * It is False for hand-to-hand or thrown non-weapon, hand-to-hand
1402          * polearm or lance when not mounted, hand-to-hand missile or ammo
1403          * or launcher, thrown non-missile, or thrown ammo (including rocks)
1404          * when not wielding corresponding launcher.
1405          */
1406         if (use_weapon_skill) {
1407             struct obj *skillwep = obj;
1408 
1409             if (PROJECTILE(obj) && ammo_and_launcher(obj, uwep))
1410                 skillwep = uwep;
1411             dmgbonus += weapon_dam_bonus(skillwep);
1412 
1413             /* hit for more than minimal damage (before being adjusted
1414                for damage or skill bonus) trains the skill toward future
1415                enhancement */
1416             if (train_weapon_skill) {
1417                 /* [this assumes that `!thrown' implies wielded...] */
1418                 wtype = thrown ? weapon_type(skillwep) : uwep_skill_type();
1419                 use_skill(wtype, 1);
1420             }
1421         }
1422 
1423         /* apply combined damage+strength and skill bonuses */
1424         tmp += dmgbonus;
1425         /* don't let penalty, if bonus is negative, turn a hit into a miss */
1426         if (tmp < 1)
1427             tmp = 1;
1428     }
1429 
1430     if (ispoisoned) {
1431         int nopoison = (10 - (obj->owt / 10));
1432 
1433         if (nopoison < 2)
1434             nopoison = 2;
1435         if (obj && !permapoisoned(obj) && !rn2(nopoison)) {
1436             /* remove poison now in case obj ends up in a bones file */
1437             obj->opoisoned = FALSE;
1438             /* defer "obj is no longer poisoned" until after hit message */
1439             unpoisonmsg = TRUE;
1440         }
1441         if (resists_poison(mon)) {
1442             needpoismsg = TRUE;
1443         }
1444         else {
1445             tmp += (thrown == HMON_THROWN ? rnd(6) : rn1(10, 6));
1446         }
1447     }
1448     if (tmp < 1) {
1449         /* make sure that negative damage adjustment can't result
1450            in inadvertently boosting the victim's hit points */
1451         tmp = (get_dmg_bonus && noncorporeal(mdat)) ? 1 : 0;
1452         if (noncorporeal(mdat) && !hittxt
1453             && thrown != HMON_THROWN && thrown != HMON_KICKED)
1454             hittxt = shade_miss(&g.youmonst, mon, obj, FALSE, TRUE);
1455     }
1456 
1457     if (jousting) {
1458         tmp += d(2, (obj == uwep) ? 10 : 2); /* [was in dmgval()] */
1459         You("joust %s%s", mon_nam(mon), canseemon(mon) ? exclam(tmp) : ".");
1460         if (jousting < 0) {
1461             pline("%s shatters on impact!", Yname2(obj));
1462             /* (must be either primary or secondary weapon to get here) */
1463             set_twoweap(FALSE); /* sets u.twoweap = FALSE;
1464                                  * untwoweapon() is too verbose here */
1465             if (obj == uwep)
1466                 uwepgone(); /* set g.unweapon */
1467             /* minor side-effect: broken lance won't split puddings */
1468             useup(obj);
1469             obj = (struct obj *) 0;
1470         }
1471         /* avoid migrating a dead monster */
1472         if (mon->mhp > tmp) {
1473             mhurtle(mon, u.dx, u.dy, 1);
1474             mdat = mon->data; /* in case of a polymorph trap */
1475             if (DEADMONSTER(mon))
1476                 already_killed = TRUE;
1477         }
1478         hittxt = TRUE;
1479     } else if (unarmed && tmp > 1 && !thrown && !obj && !Upolyd) {
1480         /* VERY small chance of stunning opponent if unarmed. */
1481         if (rnd(100) < P_SKILL(P_BARE_HANDED_COMBAT) && !bigmonst(mdat)
1482             && !thick_skinned(mdat)) {
1483             if (canspotmon(mon))
1484                 pline("%s %s from your powerful strike!", Monnam(mon),
1485                       makeplural(stagger(mon->data, "stagger")));
1486             /* avoid migrating a dead monster */
1487             if (mon->mhp > tmp) {
1488                 mhurtle(mon, u.dx, u.dy, 1);
1489                 mdat = mon->data; /* in case of a polymorph trap */
1490                 if (DEADMONSTER(mon))
1491                     already_killed = TRUE;
1492             }
1493             hittxt = TRUE;
1494         }
1495     }
1496 
1497     if (!already_killed)
1498         mon->mhp -= tmp;
1499     /* adjustments might have made tmp become less than what
1500        a level draining artifact has already done to max HP */
1501     if (mon->mhp > mon->mhpmax)
1502         mon->mhp = mon->mhpmax;
1503     if (DEADMONSTER(mon))
1504         destroyed = TRUE;
1505     if (mon->mtame && tmp > 0) {
1506         /* do this even if the pet is being killed (affects revival) */
1507         abuse_dog(mon); /* reduces tameness */
1508         /* flee if still alive and still tame; if already suffering from
1509            untimed fleeing, no effect, otherwise increases timed fleeing */
1510         if (mon->mtame && !destroyed)
1511             monflee(mon, 10 * rnd(tmp), FALSE, FALSE);
1512     }
1513     if ((mdat == &mons[PM_BLACK_PUDDING] || mdat == &mons[PM_BROWN_PUDDING])
1514         /* pudding is alive and healthy enough to split */
1515         && mon->mhp > 1 && !mon->mcan
1516         /* iron weapon using melee or polearm hit [3.6.1: metal weapon too;
1517            also allow either or both weapons to cause split when twoweap] */
1518         && obj && (obj == uwep || (u.twoweap && obj == uswapwep))
1519         && ((obj->material == IRON || obj->material == METAL)
1520             /* allow scalpel and tsurugi to split puddings */
1521             /* but not bashing with darts, arrows or ya */
1522             && !(is_ammo(obj) || is_missile(obj)))
1523         && hand_to_hand) {
1524         struct monst *mclone;
1525         char withwhat[BUFSZ];
1526 
1527         if ((mclone = clone_mon(mon, 0, 0)) != 0) {
1528             withwhat[0] = '\0';
1529             if (u.twoweap && flags.verbose)
1530                 Sprintf(withwhat, " with %s", yname(obj));
1531             pline("%s divides as you hit it%s!", Monnam(mon), withwhat);
1532             hittxt = TRUE;
1533             mintrap(mclone);
1534         }
1535     }
1536 
1537     if (!hittxt /*( thrown => obj exists )*/
1538         && (!destroyed
1539             || (thrown && g.m_shot.n > 1 && g.m_shot.o == obj->otyp))) {
1540         if (thrown)
1541             hit(mshot_xname(obj), mon, exclam(tmp));
1542         else if (!flags.verbose)
1543             You("hit it.");
1544         else
1545             You("%s %s%s",
1546                 (obj && (is_shield(obj) || obj->otyp == HEAVY_IRON_BALL))
1547                   ? "bash" : Role_if(PM_BARBARIAN) ? "smite" : "hit",
1548                 mon_nam(mon), canseemon(mon) ? exclam(tmp) : ".");
1549     }
1550 
1551     if (hated_obj && ((artimsg & ARTIFACTHIT_INSTAKILLMSG) == 0)) {
1552         searmsg(&g.youmonst, mon, hated_obj, FALSE);
1553     }
1554     if (lightobj) {
1555         const char *fmt;
1556         char *whom = mon_nam(mon);
1557         char emitlightobjbuf[BUFSZ];
1558 
1559         if (canspotmon(mon)) {
1560             if (saved_oname[0]) {
1561                 Sprintf(emitlightobjbuf,
1562                         "%s radiance penetrates deep into",
1563                         s_suffix(saved_oname));
1564                 Strcat(emitlightobjbuf, " %s!");
1565                 fmt = emitlightobjbuf;
1566             } else
1567                 fmt = "The light sears %s!";
1568         } else {
1569             *whom = highc(*whom); /* "it" -> "It" */
1570             fmt = "%s is seared!";
1571         }
1572         /* note: s_suffix returns a modifiable buffer */
1573         if (!noncorporeal(mdat) && !amorphous(mdat))
1574             whom = strcat(s_suffix(whom), " flesh");
1575         pline(fmt, whom);
1576     }
1577     /* if a "no longer poisoned" message is coming, it will be last;
1578        obj->opoisoned was cleared above and any message referring to
1579        "poisoned <obj>" has now been given; we want just "<obj>" for
1580        last message, so reformat while obj is still accessible */
1581     if (unpoisonmsg)
1582         Strcpy(saved_oname, cxname(obj));
1583 
1584     /* [note: thrown obj might go away during killed()/xkilled() call
1585        (via 'thrownobj'; if swallowed, it gets added to engulfer's
1586        minvent and might merge with a stack that's already there)] */
1587     /* already_killed and poiskilled won't apply for Trollsbane */
1588 
1589     if (needpoismsg) {
1590         pline_The("poison doesn't seem to affect %s.", mon_nam(mon));
1591     }
1592     if (destroyed) {
1593         if (!already_killed) {
1594             if (troll_baned(mon, obj))
1595                 g.mkcorpstat_norevive = TRUE;
1596             killed(mon); /* takes care of most messages */
1597             g.mkcorpstat_norevive = FALSE;
1598         }
1599     } else if (u.umconf && hand_to_hand) {
1600         nohandglow(mon);
1601         if (!mon->mconf && !resist(mon, SPBOOK_CLASS, 0, NOTELL)) {
1602             mon->mconf = 1;
1603             if (!mon->mstun && mon->mcanmove && !mon->msleeping
1604                 && canseemon(mon))
1605                 pline("%s appears confused.", Monnam(mon));
1606         }
1607     }
1608     if (unpoisonmsg)
1609         Your("%s %s no longer poisoned.", saved_oname,
1610              vtense(saved_oname, "are"));
1611     if (!destroyed) {
1612         print_mon_wounded(mon, saved_mhp);
1613     }
1614 
1615     return destroyed ? FALSE : TRUE;
1616 }
1617 
1618 static boolean
shade_aware(struct obj * obj)1619 shade_aware(struct obj *obj)
1620 {
1621     if (!obj)
1622         return FALSE;
1623     /*
1624      * The things in this list either
1625      * 1) affect shades.
1626      *  OR
1627      * 2) are dealt with properly by other routines
1628      *    when it comes to shades.
1629      */
1630     if (obj->otyp == BOULDER
1631         || obj->otyp == HEAVY_IRON_BALL
1632         || obj->otyp == IRON_CHAIN      /* dmgval handles those first three */
1633         || obj->otyp == MIRROR          /* silver in the reflective surface */
1634         || obj->otyp == CLOVE_OF_GARLIC /* causes shades to flee */
1635         || obj->material == SILVER)
1636         return TRUE;
1637     return FALSE;
1638 }
1639 
1640 /* used for hero vs monster and monster vs monster; also handles
1641    monster vs hero but that won't happen because hero can't be a shade */
1642 boolean
shade_miss(struct monst * magr,struct monst * mdef,struct obj * obj,boolean thrown,boolean verbose)1643 shade_miss(struct monst *magr, struct monst *mdef, struct obj *obj,
1644            boolean thrown, boolean verbose)
1645 {
1646     const char *what, *whose, *target;
1647     boolean youagr = (magr == &g.youmonst), youdef = (mdef == &g.youmonst);
1648 
1649     /* we're using dmgval() for zero/not-zero, not for actual damage amount */
1650     if (!noncorporeal(mdef->data) || (obj && dmgval(obj, mdef)))
1651         return FALSE;
1652 
1653     if (verbose
1654         && ((youdef || cansee(mdef->mx, mdef->my) || sensemon(mdef))
1655             || (magr == &g.youmonst && distu(mdef->mx, mdef->my) <= 2))) {
1656         static const char harmlessly_thru[] = " harmlessly through ";
1657 
1658         what = (!obj || shade_aware(obj)) ? "attack" : cxname(obj);
1659         target = youdef ? "you" : mon_nam(mdef);
1660         if (!thrown) {
1661             whose = youagr ? "Your" : s_suffix(Monnam(magr));
1662             pline("%s %s %s%s%s.", whose, what,
1663                   vtense(what, "pass"), harmlessly_thru, target);
1664         } else {
1665             pline("%s %s%s%s.", The(what), /* note: not pline_The() */
1666                   vtense(what, "pass"), harmlessly_thru, target);
1667         }
1668         if (!youdef && !canspotmon(mdef))
1669             map_invisible(mdef->mx, mdef->my);
1670     }
1671     if (!youdef)
1672         wakeup(mdef, FALSE, TRUE);
1673     return TRUE;
1674 }
1675 
1676 /* check whether slippery clothing protects from hug or wrap attack */
1677 /* [currently assumes that you are the attacker] */
1678 static boolean
m_slips_free(struct monst * mdef,struct attack * mattk)1679 m_slips_free(struct monst *mdef, struct attack *mattk)
1680 {
1681     struct obj *obj;
1682 
1683     if (mattk->adtyp == AD_DRIN) {
1684         /* intelligence drain attacks the head */
1685         obj = which_armor(mdef, W_ARMH);
1686     } else {
1687         /* grabbing attacks the body */
1688         obj = which_armor(mdef, W_ARMC); /* cloak */
1689         if (!obj)
1690             obj = which_armor(mdef, W_ARM); /* suit */
1691         if (!obj)
1692             obj = which_armor(mdef, W_ARMU); /* shirt */
1693     }
1694 
1695     /* if monster's cloak/armor is greased, your grab slips off; this
1696        protection might fail (33% chance) when the armor is cursed */
1697     if (obj && (obj->greased || obj->otyp == OILSKIN_CLOAK)
1698         && (!obj->cursed || rn2(3))) {
1699         You("%s %s %s %s!",
1700             mattk->adtyp == AD_WRAP ? "slip off of"
1701                                     : "grab, but cannot hold onto",
1702             s_suffix(mon_nam(mdef)), obj->greased ? "greased" : "slippery",
1703             /* avoid "slippery slippery cloak"
1704                for undiscovered oilskin cloak */
1705             (obj->greased || objects[obj->otyp].oc_name_known)
1706                 ? xname(obj)
1707                 : cloak_simple_name(obj));
1708 
1709         if (obj->greased && !rn2(2)) {
1710             pline_The("grease wears off.");
1711             obj->greased = 0;
1712         }
1713         return TRUE;
1714     }
1715     return FALSE;
1716 }
1717 
1718 /* used when hitting a monster with a lance while mounted;
1719    1: joust hit; 0: ordinary hit; -1: joust but break lance */
1720 static int
joust(struct monst * mon,struct obj * obj)1721 joust(struct monst *mon, /* target */
1722       struct obj *obj)   /* weapon */
1723 {
1724     int skill_rating, joust_dieroll;
1725 
1726     if (Fumbling || Stunned)
1727         return 0;
1728     /* sanity check; lance must be wielded in order to joust */
1729     if (obj != uwep && (obj != uswapwep || !u.twoweap))
1730         return 0;
1731 
1732     /* if using two weapons, use worse of lance and two-weapon skills */
1733     skill_rating = P_SKILL(weapon_type(obj)); /* lance skill */
1734     if (u.twoweap && P_SKILL(P_TWO_WEAPON_COMBAT) < skill_rating)
1735         skill_rating = P_SKILL(P_TWO_WEAPON_COMBAT);
1736     if (skill_rating == P_ISRESTRICTED)
1737         skill_rating = P_UNSKILLED; /* 0=>1 */
1738 
1739     /* odds to joust are expert:80%, skilled:60%, basic:40%, unskilled:20% */
1740     if ((joust_dieroll = rn2(5)) < skill_rating) {
1741         if (joust_dieroll == 0 && rnl(50) == (50 - 1) && !unsolid(mon->data)
1742             && !obj_resists(obj, 0, 100))
1743             return -1; /* hit that breaks lance */
1744         return 1;      /* successful joust */
1745     }
1746     return 0; /* no joust bonus; revert to ordinary attack */
1747 }
1748 
1749 /*
1750  * Send in a demon pet for the hero.  Exercise wisdom.
1751  *
1752  * This function used to be inline to damageum(), but the Metrowerks compiler
1753  * (DR4 and DR4.5) screws up with an internal error 5 "Expression Too
1754  * Complex."
1755  * Pulling it out makes it work.
1756  */
1757 static void
demonpet(void)1758 demonpet(void)
1759 {
1760     int i;
1761     struct permonst *pm;
1762     struct monst *dtmp;
1763 
1764     pline("Some hell-p has arrived!");
1765     i = !rn2(6) ? ndemon(u.ualign.type) : NON_PM;
1766     pm = i != NON_PM ? &mons[i] : g.youmonst.data;
1767     if ((dtmp = makemon(pm, u.ux, u.uy, NO_MM_FLAGS)) != 0)
1768         (void) tamedog(dtmp, (struct obj *) 0, FALSE);
1769     exercise(A_WIS, TRUE);
1770 }
1771 
1772 static boolean
theft_petrifies(struct obj * otmp)1773 theft_petrifies(struct obj *otmp)
1774 {
1775     if (uarmg || otmp->otyp != CORPSE
1776         || !touch_petrifies(&mons[otmp->corpsenm]) || Stone_resistance)
1777         return FALSE;
1778 
1779 #if 0   /* no poly_when_stoned() critter has theft capability */
1780     if (poly_when_stoned(g.youmonst.data)
1781         && polymon(PM_STONE_GOLEM, POLYMON_ALL_MSGS) {
1782         display_nhwindow(WIN_MESSAGE, FALSE);   /* --More-- */
1783         return TRUE;
1784     }
1785 #endif
1786 
1787     /* stealing this corpse is fatal... */
1788     instapetrify(corpse_xname(otmp, "stolen", CXN_ARTICLE));
1789     /* apparently wasn't fatal after all... */
1790     return TRUE;
1791 }
1792 
1793 /*
1794  * Player uses theft attack against monster.
1795  *
1796  * If the target is wearing body armor, take all of its possessions;
1797  * otherwise, take one object.  [Is this really the behavior we want?]
1798  */
1799 static void
steal_it(struct monst * mdef,struct attack * mattk)1800 steal_it(struct monst *mdef, struct attack *mattk)
1801 {
1802     struct obj *otmp, *gold = 0, *ustealo, **minvent_ptr;
1803 
1804     otmp = mdef->minvent;
1805     if (!otmp || (otmp->oclass == COIN_CLASS && !otmp->nobj))
1806         return; /* nothing to take */
1807 
1808     /* look for worn body armor */
1809     ustealo = (struct obj *) 0;
1810     if (could_seduce(&g.youmonst, mdef, mattk)) {
1811         /* find armor, and move it to end of inventory in the process */
1812         minvent_ptr = &mdef->minvent;
1813         while ((otmp = *minvent_ptr) != 0)
1814             if (otmp->owornmask & W_ARM) {
1815                 if (ustealo)
1816                     panic("steal_it: multiple worn suits");
1817                 *minvent_ptr = otmp->nobj; /* take armor out of minvent */
1818                 ustealo = otmp;
1819                 ustealo->nobj = (struct obj *) 0;
1820             } else {
1821                 minvent_ptr = &otmp->nobj;
1822             }
1823         *minvent_ptr = ustealo; /* put armor back into minvent */
1824     }
1825     gold = findgold(mdef->minvent, TRUE);
1826 
1827     if (ustealo) { /* we will be taking everything */
1828         char heshe[20];
1829 
1830         /* 3.7: this uses hero's base gender rather than nymph feminimity
1831            but was using hardcoded pronouns She/her for target monster;
1832            switch to dynamic pronoun */
1833         if (gender(mdef) == (int) u.mfemale
1834             && g.youmonst.data->mlet == S_NYMPH)
1835             You("charm %s.  %s gladly hands over %s%s possessions.",
1836                 mon_nam(mdef), upstart(strcpy(heshe, mhe(mdef))),
1837                 !gold ? "" : "most of ", mhis(mdef));
1838         else
1839             You("seduce %s and %s starts to take off %s clothes.",
1840                 mon_nam(mdef), mhe(mdef), mhis(mdef));
1841     }
1842 
1843     /* prevent gold from being stolen so that steal-item isn't a superset
1844        of steal-gold; shuffling it out of minvent before selecting next
1845        item, and then back in case hero or monster dies (hero touching
1846        stolen c'trice corpse or monster wielding one and having gloves
1847        stolen) is less bookkeeping than skipping it within the loop or
1848        taking it out once and then trying to figure out how to put it back */
1849     if (gold)
1850         obj_extract_self(gold);
1851 
1852     while ((otmp = mdef->minvent) != 0) {
1853         int stealresult;
1854         if (gold) /* put 'mdef's gold back after remembering mdef->minvent */
1855             mpickobj(mdef, gold), gold = 0;
1856         if (!Upolyd)
1857             break; /* no longer have ability to steal */
1858 
1859         /* special message for final item; no need to check owornmask because
1860          * ustealo is only set on objects with (owornmask & W_ARM) */
1861         if (otmp == ustealo)
1862             pline("%s finishes taking off %s suit.", Monnam(mdef),
1863                     mhis(mdef));
1864         stealresult = really_steal(otmp, mdef);
1865         if (stealresult == STEAL_ABORT) /* hero got interrupted... */
1866             break;
1867         if (stealresult == STEAL_DROPPED)
1868             continue;
1869         if (!ustealo)
1870             break; /* only taking one item */
1871 
1872         /* take gold out of minvent before making next selection; if it
1873            is the only thing left, the loop will terminate and it will be
1874            put back below */
1875         if ((gold = findgold(mdef->minvent, TRUE)) != 0)
1876             obj_extract_self(gold);
1877     }
1878 
1879     /* put gold back; won't happen if either hero or 'mdef' dies because
1880        gold will be back in monster's inventory at either of those times
1881        (so will be present in mdef's minvent for bones, or in its statue
1882        now if it has just been turned into one) */
1883     if (gold)
1884         mpickobj(mdef, gold);
1885 }
1886 
1887 /* Actual mechanics of stealing obj from mdef. This is now its own function
1888  * because player-as-leprechaun can steal gold items, including gold weapons and
1889  * armor, etc.
1890  * Assumes caller handles whatever other messages are necessary; this takes care
1891  * of the "You steal e - an imaginary widget" message.
1892  * Returns one of the STEAL_* values. */
1893 static int
really_steal(struct obj * obj,struct monst * mdef)1894 really_steal(struct obj *obj, struct monst *mdef)
1895 {
1896     long unwornmask = obj->owornmask;
1897     /* take the object away from the monster */
1898     extract_from_minvent(mdef, obj, TRUE, FALSE);
1899     /* give the object to the character */
1900     obj = hold_another_object(obj, "You snatched but dropped %s.",
1901                               doname(obj), "You steal: ");
1902     /* might have dropped obj, and it might have broken or left level */
1903     if (!obj || obj->where != OBJ_INVENT)
1904         return STEAL_DROPPED;
1905     if (theft_petrifies(obj))
1906         return STEAL_ABORT; /* stop thieving even though hero survived */
1907     /* more take-away handling, after theft message */
1908     if (unwornmask & W_WEP) { /* stole wielded weapon */
1909         possibly_unwield(mdef, FALSE);
1910     } else if (unwornmask & W_ARMG) { /* stole worn gloves */
1911         mselftouch(mdef, (const char *) 0, TRUE);
1912         if (DEADMONSTER(mdef)) /* it's now a statue */
1913             return STEAL_ABORT;       /* can't continue stealing */
1914     }
1915     return STEAL_SUCCESS;
1916 }
1917 
1918 void
mhitm_ad_rust(struct monst * magr,struct attack * mattk,struct monst * mdef,struct mhitm_data * mhm)1919 mhitm_ad_rust(struct monst *magr, struct attack *mattk, struct monst *mdef,
1920               struct mhitm_data *mhm)
1921 {
1922     struct permonst *pd = mdef->data;
1923 
1924     if (magr == &g.youmonst) {
1925         /* uhitm */
1926         if (completelyrusts(pd)) { /* iron golem */
1927             /* note: the life-saved case is hypothetical because
1928                life-saving doesn't work for golems */
1929             pline("%s %s to pieces!", Monnam(mdef),
1930                   !mlifesaver(mdef) ? "falls" : "starts to fall");
1931             xkilled(mdef, XKILL_NOMSG);
1932             mhm->hitflags |= MM_DEF_DIED;
1933         }
1934         erode_armor(mdef, ERODE_RUST);
1935         mhm->damage = 0; /* damageum(), int tmp */
1936     } else if (mdef == &g.youmonst) {
1937         /* mhitu */
1938         hitmsg(magr, mattk);
1939         if (magr->mcan) {
1940             return;
1941         }
1942         if (completelyrusts(pd)) {
1943             You("rust!");
1944             /* KMH -- this is okay with unchanging */
1945             Strcpy(g.killer.name, "rusted away");
1946             g.killer.format = NO_KILLER_PREFIX;
1947             rehumanize();
1948             return;
1949         }
1950         erode_armor(&g.youmonst, ERODE_RUST);
1951     } else {
1952         /* mhitm */
1953         if (magr->mcan)
1954             return;
1955         if (completelyrusts(pd)) { /* PM_IRON_GOLEM */
1956             if (g.vis && canseemon(mdef))
1957                 pline("%s %s to pieces!", Monnam(mdef),
1958                       !mlifesaver(mdef) ? "falls" : "starts to fall");
1959             monkilled(mdef, (char *) 0, AD_RUST);
1960             if (!DEADMONSTER(mdef)) {
1961                 mhm->hitflags = MM_MISS;
1962                 mhm->done = TRUE;
1963                 return;
1964             }
1965             mhm->hitflags = (MM_DEF_DIED | (grow_up(magr, mdef) ? 0 : MM_AGR_DIED));
1966             mhm->done = TRUE;
1967             return;
1968         }
1969         erode_armor(mdef, ERODE_RUST);
1970         mdef->mstrategy &= ~STRAT_WAITFORU;
1971         mhm->damage = 0; /* mdamagem(), int tmp */
1972     }
1973 }
1974 
1975 void
mhitm_ad_corr(struct monst * magr,struct attack * mattk,struct monst * mdef,struct mhitm_data * mhm)1976 mhitm_ad_corr(struct monst *magr, struct attack *mattk, struct monst *mdef,
1977               struct mhitm_data *mhm)
1978 {
1979     if (magr == &g.youmonst) {
1980         /* uhitm */
1981         erode_armor(mdef, ERODE_CORRODE);
1982         mhm->damage = 0;
1983     } else if (mdef == &g.youmonst) {
1984         /* mhitu */
1985         hitmsg(magr, mattk);
1986         if (magr->mcan)
1987             return;
1988         erode_armor(mdef, ERODE_CORRODE);
1989     } else {
1990         /* mhitm */
1991         if (magr->mcan)
1992             return;
1993         erode_armor(mdef, ERODE_CORRODE);
1994         mdef->mstrategy &= ~STRAT_WAITFORU;
1995         mhm->damage = 0;
1996     }
1997 }
1998 
1999 void
mhitm_ad_dcay(struct monst * magr,struct attack * mattk,struct monst * mdef,struct mhitm_data * mhm)2000 mhitm_ad_dcay(struct monst *magr, struct attack *mattk, struct monst *mdef,
2001               struct mhitm_data *mhm)
2002 {
2003     struct permonst *pd = mdef->data;
2004 
2005     if (magr == &g.youmonst) {
2006         /* uhitm */
2007         if (completelyrots(pd)) {
2008             pline("%s %s to pieces!", Monnam(mdef),
2009                   !mlifesaver(mdef) ? "falls" : "starts to fall");
2010             xkilled(mdef, XKILL_NOMSG);
2011         }
2012         erode_armor(mdef, ERODE_ROT);
2013         mhm->damage = 0;
2014     } else if (mdef == &g.youmonst) {
2015         /* mhitu */
2016         hitmsg(magr, mattk);
2017         if (magr->mcan)
2018             return;
2019         if (completelyrots(pd)) {
2020             You("rot!");
2021             /* KMH -- this is okay with unchanging */
2022             strcpy(g.killer.name, "rotted away");
2023             g.killer.format = NO_KILLER_PREFIX;
2024             rehumanize();
2025             return;
2026         }
2027         erode_armor(mdef, ERODE_ROT);
2028     } else {
2029         /* mhitm */
2030         if (magr->mcan)
2031             return;
2032         if (completelyrots(pd)) {
2033             /* note: the life-saved case is hypothetical because
2034                life-saving doesn't work for golems */
2035             if (g.vis && canseemon(mdef))
2036                 pline("%s %s to pieces!", Monnam(mdef),
2037                       !mlifesaver(mdef) ? "falls" : "starts to fall");
2038             monkilled(mdef, (char *) 0, AD_DCAY);
2039             if (!DEADMONSTER(mdef)) {
2040                 mhm->done = TRUE;
2041                 mhm->hitflags = MM_MISS;
2042                 return;
2043             }
2044             mhm->done = TRUE;
2045             mhm->hitflags = (MM_DEF_DIED | (grow_up(magr, mdef) ? 0 : MM_AGR_DIED));
2046             return;
2047         }
2048         erode_armor(mdef, ERODE_ROT);
2049         mdef->mstrategy &= ~STRAT_WAITFORU;
2050         mhm->damage = 0;
2051     }
2052 }
2053 
2054 void
mhitm_ad_dren(struct monst * magr,struct attack * mattk,struct monst * mdef,struct mhitm_data * mhm)2055 mhitm_ad_dren(struct monst *magr, struct attack *mattk, struct monst *mdef,
2056               struct mhitm_data *mhm)
2057 {
2058     if (magr == &g.youmonst) {
2059         /* uhitm */
2060         int armpro = magic_negation(mdef);
2061         /* since hero can't be cancelled, only defender's armor applies */
2062         boolean negated = !(rn2(10) >= 3 * armpro);
2063 
2064         if (!negated && !rn2(4))
2065             xdrainenergym(mdef, TRUE);
2066         mhm->damage = 0;
2067     } else if (mdef == &g.youmonst) {
2068         /* mhitu */
2069         int armpro = magic_negation(mdef);
2070         boolean uncancelled = !magr->mcan && (rn2(10) >= 3 * armpro);
2071 
2072         hitmsg(magr, mattk);
2073         if (uncancelled && !rn2(4)) /* 25% chance */
2074             drain_en(mhm->damage);
2075         mhm->damage = 0;
2076     } else {
2077         /* mhitm */
2078         /* cancellation factor is the same as when attacking the hero */
2079         int armpro = magic_negation(mdef);
2080         boolean cancelled = magr->mcan || !(rn2(10) >= 3 * armpro);
2081 
2082         if (!cancelled && !rn2(4))
2083             xdrainenergym(mdef, (boolean) (g.vis && canspotmon(mdef)
2084                                            && mattk->aatyp != AT_ENGL));
2085         mhm->damage = 0;
2086     }
2087 }
2088 
2089 void
mhitm_ad_drli(struct monst * magr,struct attack * mattk,struct monst * mdef,struct mhitm_data * mhm)2090 mhitm_ad_drli(struct monst *magr, struct attack *mattk, struct monst *mdef,
2091               struct mhitm_data *mhm)
2092 {
2093     if (magr == &g.youmonst) {
2094         /* uhitm */
2095         int armpro = magic_negation(mdef);
2096         /* since hero can't be cancelled, only defender's armor applies */
2097         boolean negated = !(rn2(10) >= 3 * armpro);
2098 
2099         if (!negated && !rn2(3) && !resists_drli(mdef)
2100             && !item_catches_drain(mdef)) {
2101             mhm->damage = d(2, 6); /* Stormbringer uses monhp_per_lvl
2102                                     * (usually 1d8) */
2103             pline("%s becomes weaker!", Monnam(mdef));
2104             if (mdef->mhpmax - mhm->damage > (int) mdef->m_lev) {
2105                 mdef->mhpmax -= mhm->damage;
2106             } else {
2107                 /* limit floor of mhpmax reduction to current m_lev + 1;
2108                    avoid increasing it if somehow already less than that */
2109                 if (mdef->mhpmax > (int) mdef->m_lev)
2110                     mdef->mhpmax = (int) mdef->m_lev + 1;
2111             }
2112             mdef->mhp -= mhm->damage;
2113             /* !m_lev: level 0 monster is killed regardless of hit points
2114                rather than drop to level -1; note: some non-living creatures
2115                (golems, vortices) are subject to life-drain */
2116             if (DEADMONSTER(mdef) || !mdef->m_lev) {
2117                 pline("%s %s!", Monnam(mdef),
2118                       nonliving(mdef->data) ? "expires" : "dies");
2119                 xkilled(mdef, XKILL_NOMSG);
2120             } else
2121                 mdef->m_lev--;
2122             mhm->damage = 0; /* damage has already been inflicted */
2123 
2124             /* unlike hitting with Stormbringer, wounded hero doesn't
2125                heal any from the drained life */
2126         }
2127     } else if (mdef == &g.youmonst) {
2128         /* mhitu */
2129         int armpro = magic_negation(mdef);
2130         boolean uncancelled = !magr->mcan && (rn2(10) >= 3 * armpro);
2131 
2132         hitmsg(magr, mattk);
2133         if (uncancelled && !rn2(3) && !Drain_resistance
2134             && !item_catches_drain(mdef)) {
2135             losexp("life drainage");
2136 
2137             /* unlike hitting with Stormbringer, wounded attacker doesn't
2138                heal any from the drained life */
2139         }
2140     } else {
2141         /* mhitm */
2142         int armpro = magic_negation(mdef);
2143         boolean cancelled = magr->mcan || !(rn2(10) >= 3 * armpro);
2144 
2145         if (!cancelled && !rn2(3) && !resists_drli(mdef)
2146             && !item_catches_drain(mdef)) {
2147             mhm->damage = d(2, 6); /* Stormbringer uses monhp_per_lvl(usually 1d8) */
2148             if (g.vis && canspotmon(mdef))
2149                 pline("%s becomes weaker!", Monnam(mdef));
2150             if (mdef->mhpmax - mhm->damage > (int) mdef->m_lev) {
2151                 mdef->mhpmax -= mhm->damage;
2152             } else {
2153                 /* limit floor of mhpmax reduction to current m_lev + 1;
2154                    avoid increasing it if somehow already less than that */
2155                 if (mdef->mhpmax > (int) mdef->m_lev)
2156                     mdef->mhpmax = (int) mdef->m_lev + 1;
2157             }
2158             if (mdef->m_lev == 0) /* automatic kill if drained past level 0 */
2159                 mhm->damage = mdef->mhp;
2160             else
2161                 mdef->m_lev--;
2162 
2163             /* unlike hitting with Stormbringer, wounded attacker doesn't
2164                heal any from the drained life */
2165         }
2166     }
2167 }
2168 
2169 void
mhitm_ad_fire(struct monst * magr,struct attack * mattk,struct monst * mdef,struct mhitm_data * mhm)2170 mhitm_ad_fire(struct monst *magr, struct attack *mattk, struct monst *mdef,
2171               struct mhitm_data *mhm)
2172 {
2173     struct permonst *pd = mdef->data;
2174     const int orig_dmg = mhm->damage; /* damage coming into the function */
2175 
2176     if (magr == &g.youmonst) {
2177         /* uhitm */
2178         int armpro = magic_negation(mdef);
2179         /* since hero can't be cancelled, only defender's armor applies */
2180         boolean negated = !(rn2(10) >= 3 * armpro);
2181 
2182         if (negated) {
2183             mhm->damage = 0;
2184             return;
2185         }
2186         if (!Blind)
2187             pline("%s is %s!", Monnam(mdef), on_fire(pd, mattk));
2188         if (completelyburns(pd)) { /* paper golem or straw golem */
2189             if (!Blind)
2190                 /* note: the life-saved case is hypothetical because
2191                    life-saving doesn't work for golems */
2192                 pline("%s %s!", Monnam(mdef),
2193                       !mlifesaver(mdef) ? "burns completely"
2194                                         : "is totally engulfed in flames");
2195             else
2196                 You("smell burning%s.",
2197                     (pd == &mons[PM_PAPER_GOLEM]) ? " paper"
2198                       : (pd == &mons[PM_STRAW_GOLEM]) ? " straw" : "");
2199             xkilled(mdef, XKILL_NOMSG | XKILL_NOCORPSE);
2200             mhm->damage = 0;
2201             return;
2202             /* Don't return yet; keep hp<1 and mhm.damage=0 for pet msg */
2203         }
2204         if (resists_fire(mdef)) {
2205             if (!Blind)
2206                 pline_The("fire doesn't heat %s!", mon_nam(mdef));
2207             golemeffects(mdef, AD_FIRE, mhm->damage);
2208             shieldeff(mdef->mx, mdef->my);
2209             mhm->damage = 0;
2210         }
2211         mhm->damage += destroy_items(mdef, AD_FIRE, orig_dmg);
2212         ignite_items(mdef->minvent);
2213     } else if (mdef == &g.youmonst) {
2214         /* mhitu */
2215         int armpro = magic_negation(mdef);
2216         boolean uncancelled = !magr->mcan && (rn2(10) >= 3 * armpro);
2217 
2218         hitmsg(magr, mattk);
2219         if (uncancelled) {
2220             pline("You're %s!", on_fire(pd, mattk));
2221             if (completelyburns(pd)) { /* paper or straw golem */
2222                 You("go up in flames!");
2223                 /* KMH -- this is okay with unchanging */
2224                 Strcpy(g.killer.name, "immolated");
2225                 g.killer.format = NO_KILLER_PREFIX;
2226                 rehumanize();
2227                 return;
2228             } else if (Fire_resistance) {
2229                 pline_The("fire doesn't feel hot!");
2230                 mhm->damage = 0;
2231             }
2232             if ((int) magr->m_lev > rn2(20)) {
2233                 (void) destroy_items(&g.youmonst, AD_FIRE, orig_dmg);
2234                 ignite_items(g.invent);
2235             }
2236             burn_away_slime();
2237         } else
2238             mhm->damage = 0;
2239     } else {
2240         /* mhitm */
2241         int armpro = magic_negation(mdef);
2242         boolean cancelled = magr->mcan || !(rn2(10) >= 3 * armpro);
2243 
2244         if (cancelled) {
2245             mhm->damage = 0;
2246             return;
2247         }
2248         if (g.vis && canseemon(mdef))
2249             pline("%s is %s!", Monnam(mdef), on_fire(pd, mattk));
2250         if (completelyburns(pd)) { /* paper golem or straw golem */
2251             /* note: the life-saved case is hypothetical because
2252                life-saving doesn't work for golems */
2253             if (g.vis && canseemon(mdef))
2254                 pline("%s %s!", Monnam(mdef),
2255                       !mlifesaver(mdef) ? "burns completely"
2256                                         : "is totally engulfed in flames");
2257             monkilled(mdef, (char *) 0, AD_FIRE);
2258             if (!DEADMONSTER(mdef)) {
2259                 mhm->hitflags = MM_MISS;
2260                 mhm->done = TRUE;
2261                 return;
2262             }
2263             mhm->hitflags = (MM_DEF_DIED | (grow_up(magr, mdef) ? 0 : MM_AGR_DIED));
2264             mhm->done = TRUE;
2265             return;
2266         }
2267         if (resists_fire(mdef)) {
2268             if (g.vis && canseemon(mdef))
2269                 pline_The("fire doesn't seem to burn %s!", mon_nam(mdef));
2270             shieldeff(mdef->mx, mdef->my);
2271             golemeffects(mdef, AD_FIRE, mhm->damage);
2272             mhm->damage = 0;
2273         }
2274         mhm->damage += destroy_items(mdef, AD_FIRE, orig_dmg);
2275         ignite_items(mdef->minvent);
2276     }
2277 }
2278 
2279 void
mhitm_ad_cold(struct monst * magr,struct attack * mattk,struct monst * mdef,struct mhitm_data * mhm)2280 mhitm_ad_cold(struct monst *magr, struct attack *mattk, struct monst *mdef,
2281               struct mhitm_data *mhm)
2282 {
2283     const int orig_dmg = mhm->damage;
2284 
2285     if (magr == &g.youmonst) {
2286         /* uhitm */
2287         int armpro = magic_negation(mdef);
2288         /* since hero can't be cancelled, only defender's armor applies */
2289         boolean negated = !(rn2(10) >= 3 * armpro);
2290 
2291         if (negated) {
2292             mhm->damage = 0;
2293             return;
2294         }
2295         if (!Blind)
2296             pline("%s is covered in frost!", Monnam(mdef));
2297         if (resists_cold(mdef)) {
2298             shieldeff(mdef->mx, mdef->my);
2299             if (!Blind)
2300                 pline_The("frost doesn't chill %s!", mon_nam(mdef));
2301             golemeffects(mdef, AD_COLD, mhm->damage);
2302             mhm->damage = 0;
2303         }
2304         mhm->damage += destroy_items(mdef, AD_COLD, orig_dmg);
2305     } else if (mdef == &g.youmonst) {
2306         /* mhitu */
2307         int armpro = magic_negation(mdef);
2308         boolean uncancelled = !magr->mcan && (rn2(10) >= 3 * armpro);
2309 
2310         hitmsg(magr, mattk);
2311         if (uncancelled) {
2312             pline("You're covered in frost!");
2313             if (Cold_resistance) {
2314                 pline_The("frost doesn't seem cold!");
2315                 mhm->damage = 0;
2316             }
2317             if ((int) magr->m_lev > rn2(20))
2318                 (void) destroy_items(&g.youmonst, AD_COLD, orig_dmg);
2319         } else
2320             mhm->damage = 0;
2321     } else {
2322         /* mhitm */
2323         int armpro = magic_negation(mdef);
2324         boolean cancelled = magr->mcan || !(rn2(10) >= 3 * armpro);
2325 
2326         if (cancelled) {
2327             mhm->damage = 0;
2328             return;
2329         }
2330         if (g.vis && canseemon(mdef))
2331             pline("%s is covered in frost!", Monnam(mdef));
2332         if (resists_cold(mdef)) {
2333             if (g.vis && canseemon(mdef))
2334                 pline_The("frost doesn't seem to chill %s!", mon_nam(mdef));
2335             shieldeff(mdef->mx, mdef->my);
2336             golemeffects(mdef, AD_COLD, mhm->damage);
2337             mhm->damage = 0;
2338         }
2339         mhm->damage += destroy_items(mdef, AD_COLD, orig_dmg);
2340     }
2341 }
2342 
2343 void
mhitm_ad_elec(struct monst * magr,struct attack * mattk,struct monst * mdef,struct mhitm_data * mhm)2344 mhitm_ad_elec(struct monst *magr, struct attack *mattk, struct monst *mdef,
2345               struct mhitm_data *mhm)
2346 {
2347     const int orig_dmg = mhm->damage;
2348 
2349     if (magr == &g.youmonst) {
2350         /* uhitm */
2351         int armpro = magic_negation(mdef);
2352         /* since hero can't be cancelled, only defender's armor applies */
2353         boolean negated = !(rn2(10) >= 3 * armpro);
2354 
2355         if (negated) {
2356             mhm->damage = 0;
2357             return;
2358         }
2359         if (!Blind)
2360             pline("%s is zapped!", Monnam(mdef));
2361         if (resists_elec(mdef)) {
2362             if (!Blind)
2363                 pline_The("zap doesn't shock %s!", mon_nam(mdef));
2364             golemeffects(mdef, AD_ELEC, mhm->damage);
2365             shieldeff(mdef->mx, mdef->my);
2366             mhm->damage = 0;
2367         }
2368         mhm->damage += destroy_items(mdef, AD_ELEC, orig_dmg);
2369     } else if (mdef == &g.youmonst) {
2370         /* mhitu */
2371         int armpro = magic_negation(mdef);
2372         boolean uncancelled = !magr->mcan && (rn2(10) >= 3 * armpro);
2373 
2374         hitmsg(magr, mattk);
2375         if (uncancelled) {
2376             You("get zapped!");
2377             if (Shock_resistance) {
2378                 pline_The("zap doesn't shock you!");
2379                 mhm->damage = 0;
2380             }
2381             if ((int) magr->m_lev > rn2(20))
2382                 (void) destroy_items(&g.youmonst, AD_ELEC, orig_dmg);
2383         } else
2384             mhm->damage = 0;
2385     } else {
2386         /* mhitm */
2387         int armpro = magic_negation(mdef);
2388         boolean cancelled = magr->mcan || !(rn2(10) >= 3 * armpro);
2389 
2390         if (cancelled) {
2391             mhm->damage = 0;
2392             return;
2393         }
2394         if (g.vis && canseemon(mdef))
2395             pline("%s gets zapped!", Monnam(mdef));
2396         if (resists_elec(mdef)) {
2397             if (g.vis && canseemon(mdef))
2398                 pline_The("zap doesn't shock %s!", mon_nam(mdef));
2399             shieldeff(mdef->mx, mdef->my);
2400             golemeffects(mdef, AD_ELEC, mhm->damage);
2401             mhm->damage = 0;
2402         }
2403         mhm->damage += destroy_items(mdef, AD_ELEC, orig_dmg);
2404     }
2405 }
2406 
2407 void
mhitm_ad_acid(struct monst * magr,struct attack * mattk,struct monst * mdef,struct mhitm_data * mhm)2408 mhitm_ad_acid(struct monst *magr, struct attack *mattk, struct monst *mdef,
2409               struct mhitm_data *mhm)
2410 {
2411     if (magr == &g.youmonst) {
2412         /* uhitm */
2413         if (resists_acid(mdef))
2414             mhm->damage = 0;
2415     } else if (mdef == &g.youmonst) {
2416         /* mhitu */
2417         hitmsg(magr, mattk);
2418         if (!magr->mcan && !rn2(3))
2419             if (Acid_resistance) {
2420                 pline("You're covered in %s, but it seems harmless.",
2421                       hliquid("acid"));
2422                 mhm->damage = 0;
2423             } else {
2424                 pline("You're covered in %s!  It burns!", hliquid("acid"));
2425                 exercise(A_STR, FALSE);
2426             }
2427         else
2428             mhm->damage = 0;
2429     } else {
2430         /* mhitm */
2431         if (magr->mcan) {
2432             mhm->damage = 0;
2433             return;
2434         }
2435         if (resists_acid(mdef)) {
2436             if (g.vis && canseemon(mdef))
2437                 pline("%s is covered in %s, but it seems harmless.",
2438                       Monnam(mdef), hliquid("acid"));
2439             mhm->damage = 0;
2440         } else if (g.vis && canseemon(mdef)) {
2441             pline("%s is covered in %s!", Monnam(mdef), hliquid("acid"));
2442             pline("It burns %s!", mon_nam(mdef));
2443         }
2444         if (!rn2(30))
2445             erode_armor(mdef, ERODE_CORRODE);
2446         if (!rn2(6))
2447             acid_damage(MON_WEP(mdef));
2448     }
2449 }
2450 
2451 void
mhitm_ad_sgld(struct monst * magr,struct attack * mattk,struct monst * mdef,struct mhitm_data * mhm)2452 mhitm_ad_sgld(struct monst *magr, struct attack *mattk, struct monst *mdef,
2453               struct mhitm_data *mhm)
2454 {
2455     struct permonst *pa = magr->data;
2456     struct permonst *pd = mdef->data;
2457 
2458     if (magr == &g.youmonst) {
2459         /* uhitm */
2460         struct obj *mongold = findgold(mdef->minvent, FALSE);
2461 
2462         if (mongold) {
2463             if (mongold->otyp != GOLD_PIECE) {
2464                 /* stole a gold non-coin object */
2465                 really_steal(mongold, mdef);
2466             }
2467             else if (merge_choice(g.invent, mongold) || inv_cnt(FALSE) < 52) {
2468                 obj_extract_self(mongold);
2469                 addinv(mongold);
2470                 Your("purse feels heavier.");
2471             } else {
2472                 You("grab %s's gold, but find no room in your knapsack.",
2473                     mon_nam(mdef));
2474                 obj_extract_self(mongold);
2475                 dropy(mongold);
2476             }
2477         }
2478         exercise(A_DEX, TRUE);
2479         mhm->damage = 0;
2480     } else if (mdef == &g.youmonst) {
2481         /* mhitu */
2482         hitmsg(magr, mattk);
2483         if (pd->mlet == pa->mlet)
2484             return;
2485         if (!magr->mcan)
2486             stealgold(magr);
2487     } else {
2488         /* mhitm */
2489         char buf[BUFSZ];
2490 
2491         mhm->damage = 0;
2492         if (magr->mcan)
2493             return;
2494         /* technically incorrect; no check for stealing gold from
2495          * between mdef's feet...
2496          */
2497         {
2498             struct obj *gold = findgold(mdef->minvent, FALSE);
2499 
2500             if (!gold)
2501                 return;
2502             if (g.vis && canseemon(mdef)) {
2503                 Strcpy(buf, Monnam(magr));
2504                 if (gold->otyp == GOLD_PIECE) {
2505                     pline("%s steals some gold from %s.", buf, mon_nam(mdef));
2506                 }
2507                 else {
2508                     pline("%s steals %s.", Monnam(magr),
2509                         distant_name(gold, yname));
2510                 }
2511             }
2512             obj_extract_self(gold);
2513             add_to_minv(magr, gold);
2514         }
2515         mdef->mstrategy &= ~STRAT_WAITFORU;
2516         if (!tele_restrict(magr)) {
2517             boolean couldspot = canspotmon(magr);
2518 
2519             (void) rloc(magr, TRUE);
2520             if (g.vis && couldspot && !canspotmon(magr))
2521                 pline("%s suddenly disappears!", buf);
2522         }
2523     }
2524 }
2525 
2526 
2527 void
mhitm_ad_tlpt(struct monst * magr,struct attack * mattk,struct monst * mdef,struct mhitm_data * mhm)2528 mhitm_ad_tlpt(struct monst *magr, struct attack *mattk, struct monst *mdef,
2529               struct mhitm_data *mhm)
2530 {
2531     if (magr == &g.youmonst) {
2532         /* uhitm */
2533         int armpro = magic_negation(mdef);
2534         /* since hero can't be cancelled, only defender's armor applies */
2535         boolean negated = !(rn2(10) >= 3 * armpro);
2536 
2537         if (mhm->damage <= 0)
2538             mhm->damage = 1;
2539         if (!negated) {
2540             char nambuf[BUFSZ];
2541             boolean u_saw_mon = (canseemon(mdef)
2542                                  || (u.uswallow && u.ustuck == mdef));
2543 
2544             /* record the name before losing sight of monster */
2545             Strcpy(nambuf, Monnam(mdef));
2546             if (u_teleport_mon(mdef, FALSE) && u_saw_mon
2547                 && !(canseemon(mdef) || (u.uswallow && u.ustuck == mdef)))
2548                 pline("%s suddenly disappears!", nambuf);
2549             if (mhm->damage >= mdef->mhp) { /* see hitmu(mhitu.c) */
2550                 if (mdef->mhp == 1)
2551                     ++mdef->mhp;
2552                 mhm->damage = mdef->mhp - 1;
2553             }
2554         }
2555     } else if (mdef == &g.youmonst) {
2556         /* mhitu */
2557         int armpro = magic_negation(mdef);
2558         boolean uncancelled = !magr->mcan && (rn2(10) >= 3 * armpro);
2559         int tmphp;
2560 
2561         hitmsg(magr, mattk);
2562         if (uncancelled) {
2563             if (flags.verbose)
2564                 Your("position suddenly seems %suncertain!",
2565                      (Teleport_control && !Stunned && !unconscious()) ? ""
2566                      : "very ");
2567             tele();
2568             /* As of 3.6.2:  make sure damage isn't fatal; previously, it
2569                was possible to be teleported and then drop dead at
2570                the destination when QM's 1d4 damage gets applied below;
2571                even though that wasn't "wrong", it seemed strange,
2572                particularly if the teleportation had been controlled
2573                [applying the damage first and not teleporting if fatal
2574                is another alternative but it has its own complications] */
2575             if ((Half_physical_damage ? (mhm->damage - 1) / 2 : mhm->damage)
2576                 >= (tmphp = (Upolyd ? u.mh : u.uhp))) {
2577                 mhm->damage = tmphp - 1;
2578                 if (Half_physical_damage)
2579                     mhm->damage *= 2; /* doesn't actually increase damage;
2580                                        * we only get here if half the
2581                                        * original damage would would have
2582                                        * been fatal, so double reduced
2583                                        * damage will be less than original */
2584                 if (mhm->damage < 1) { /* implies (tmphp <= 1) */
2585                     mhm->damage = 1;
2586                     /* this might increase current HP beyond maximum HP but it
2587                        will be immediately reduced by caller, so that should
2588                        be indistinguishable from zero damage; we don't drop
2589                        damage all the way to zero because that inhibits any
2590                        passive counterattack if poly'd hero has one */
2591                     if (Upolyd && u.mh == 1)
2592                         ++u.mh;
2593                     else if (!Upolyd && u.uhp == 1)
2594                         ++u.uhp;
2595                     /* [don't set context.botl here] */
2596                 }
2597             }
2598         }
2599     } else {
2600         /* mhitm */
2601         int armpro = magic_negation(mdef);
2602         boolean cancelled = magr->mcan || !(rn2(10) >= 3 * armpro);
2603 
2604         if (!cancelled && mhm->damage < mdef->mhp && !tele_restrict(mdef)) {
2605             char mdef_Monnam[BUFSZ];
2606             boolean wasseen = canspotmon(mdef);
2607 
2608             /* save the name before monster teleports, otherwise
2609                we'll get "it" in the suddenly disappears message */
2610             if (g.vis && wasseen)
2611                 Strcpy(mdef_Monnam, Monnam(mdef));
2612             mdef->mstrategy &= ~STRAT_WAITFORU;
2613             (void) rloc(mdef, TRUE);
2614             if (g.vis && wasseen && !canspotmon(mdef) && mdef != u.usteed)
2615                 pline("%s suddenly disappears!", mdef_Monnam);
2616             if (mhm->damage >= mdef->mhp) { /* see hitmu(mhitu.c) */
2617                 if (mdef->mhp == 1)
2618                     ++mdef->mhp;
2619                 mhm->damage = mdef->mhp - 1;
2620             }
2621         }
2622     }
2623 }
2624 
2625 void
mhitm_ad_blnd(struct monst * magr,struct attack * mattk,struct monst * mdef,struct mhitm_data * mhm)2626 mhitm_ad_blnd(
2627     struct monst *magr,     /* attacker */
2628     struct attack *mattk,   /* magr's attack */
2629     struct monst *mdef,     /* defender */
2630     struct mhitm_data *mhm) /* optional for monster vs monster */
2631 {
2632     if (magr == &g.youmonst) {
2633         /* uhitm */
2634         if (can_blnd(magr, mdef, mattk->aatyp, (struct obj *) 0)) {
2635             if (!Blind && mdef->mcansee)
2636                 pline("%s is blinded.", Monnam(mdef));
2637             mdef->mcansee = 0;
2638             mhm->damage += mdef->mblinded;
2639             if (mhm->damage > 127)
2640                 mhm->damage = 127;
2641             mdef->mblinded = mhm->damage;
2642         }
2643         mhm->damage = 0;
2644     } else if (mdef == &g.youmonst) {
2645         /* mhitu */
2646         if (can_blnd(magr, mdef, mattk->aatyp, (struct obj *) 0)) {
2647             if (!Blind)
2648                 pline("%s blinds you!", Monnam(magr));
2649             make_blinded(Blinded + (long) mhm->damage, FALSE);
2650             if (!Blind) /* => Eyes of the Overworld */
2651                 Your1(vision_clears);
2652         }
2653         mhm->damage = 0;
2654     } else {
2655         /* mhitm */
2656         if (can_blnd(magr, mdef, mattk->aatyp, (struct obj *) 0)) {
2657             char buf[BUFSZ];
2658             unsigned rnd_tmp;
2659 
2660             if (g.vis && mdef->mcansee && canspotmon(mdef)) {
2661                 /* feedback for becoming blinded is given if observed
2662                    telepathically (canspotmon suffices) but additional
2663                    info about archon's glow is only given if seen */
2664                 Snprintf(buf, sizeof buf, "%s is blinded", Monnam(mdef));
2665                 if (mdef->data == &mons[PM_ARCHON] && canseemon(mdef))
2666                     Snprintf(eos(buf), sizeof buf - strlen(buf),
2667                              " by %s radiance", s_suffix(mon_nam(magr)));
2668                 pline("%s.", buf);
2669             }
2670             rnd_tmp = d((int) mattk->damn, (int) mattk->damd);
2671             if ((rnd_tmp += mdef->mblinded) > 127)
2672                 rnd_tmp = 127;
2673             mdef->mblinded = rnd_tmp;
2674             mdef->mcansee = 0;
2675             mdef->mstrategy &= ~STRAT_WAITFORU;
2676         }
2677         if (mhm)
2678             mhm->damage = 0;
2679     }
2680 }
2681 
2682 void
mhitm_ad_curs(struct monst * magr,struct attack * mattk,struct monst * mdef,struct mhitm_data * mhm)2683 mhitm_ad_curs(struct monst *magr, struct attack *mattk, struct monst *mdef,
2684               struct mhitm_data *mhm)
2685 {
2686     struct permonst *pa = magr->data;
2687     struct permonst *pd = mdef->data;
2688 
2689     if (magr == &g.youmonst) {
2690         /* uhitm */
2691         if (night() && !rn2(10) && !mdef->mcan) {
2692             if (pd == &mons[PM_CLAY_GOLEM]) {
2693                 if (!Blind)
2694                     pline("Some writing vanishes from %s head!",
2695                           s_suffix(mon_nam(mdef)));
2696                 xkilled(mdef, XKILL_NOMSG);
2697                 /* Don't return yet; keep hp<1 and mhm.damage=0 for pet msg */
2698             } else {
2699                 mdef->mcan = 1;
2700                 You("chuckle.");
2701             }
2702         }
2703         mhm->damage = 0;
2704     } else if (mdef == &g.youmonst) {
2705         /* mhitu */
2706         hitmsg(magr, mattk);
2707         if (!night() && pa == &mons[PM_GREMLIN])
2708             return;
2709         if (!magr->mcan && !rn2(10)) {
2710             if (!Deaf) {
2711                 if (Blind)
2712                     You_hear("laughter.");
2713                 else
2714                     pline("%s chuckles.", Monnam(magr));
2715             }
2716             if (u.umonnum == PM_CLAY_GOLEM) {
2717                 pline("Some writing vanishes from your head!");
2718                 /* KMH -- this is okay with unchanging */
2719                 Sprintf(g.killer.name, "identity theft");
2720                 g.killer.format = KILLED_BY;
2721                 rehumanize();
2722                 return;
2723             }
2724             attrcurse();
2725         }
2726     } else {
2727         /* mhitm */
2728         if (!night() && (pa == &mons[PM_GREMLIN]))
2729             return;
2730         if (!magr->mcan && !rn2(10)) {
2731             mdef->mcan = 1; /* cancelled regardless of lifesave */
2732             mdef->mstrategy &= ~STRAT_WAITFORU;
2733             if (is_were(pd) && pd->mlet != S_HUMAN)
2734                 were_change(mdef);
2735             if (pd == &mons[PM_CLAY_GOLEM]) {
2736                 if (g.vis && canseemon(mdef)) {
2737                     pline("Some writing vanishes from %s head!",
2738                           s_suffix(mon_nam(mdef)));
2739                     pline("%s is destroyed!", Monnam(mdef));
2740                 }
2741                 mondied(mdef);
2742                 if (!DEADMONSTER(mdef)) {
2743                     mhm->hitflags = MM_MISS;
2744                     mhm->done = TRUE;
2745                     return;
2746                 } else if (mdef->mtame && !g.vis) {
2747                     You(brief_feeling, "strangely sad");
2748                 }
2749                 mhm->hitflags = (MM_DEF_DIED
2750                                  | (grow_up(magr, mdef) ? 0 : MM_AGR_DIED));
2751                 mhm->done = TRUE;
2752                 return;
2753             }
2754             if (!Deaf) {
2755                 if (!g.vis)
2756                     You_hear("laughter.");
2757                 else if (canseemon(magr))
2758                     pline("%s chuckles.", Monnam(magr));
2759             }
2760         }
2761     }
2762 }
2763 
2764 /* Helper for mhitm_ad_drst(), containing some code that is also called from
2765  * mhitm_ad_phys (for poisoned weapons) and shouldn't be subject to magic
2766  * cancellation or a 1/8 chance roll.
2767  * In this specific case, the "mhitm" in the name ACTUALLY means just that -
2768  * this should be called only for monster versus monster situations. */
2769 static void
mhitm_really_poison(struct monst * magr,struct attack * mattk,struct monst * mdef,struct mhitm_data * mhm)2770 mhitm_really_poison(struct monst *magr, struct attack *mattk,
2771                     struct monst *mdef, struct mhitm_data *mhm)
2772 {
2773     if (g.vis && canspotmon(magr))
2774         pline("%s %s was poisoned!", s_suffix(Monnam(magr)),
2775               mpoisons_subj(magr, mattk));
2776     if (resists_poison(mdef)) {
2777         if (g.vis && canspotmon(mdef) && canspotmon(magr))
2778             pline_The("poison doesn't seem to affect %s.",
2779                         mon_nam(mdef));
2780     } else {
2781         mhm->damage += rn1(10, 6);
2782     }
2783 }
2784 
2785 void
mhitm_ad_drst(struct monst * magr,struct attack * mattk,struct monst * mdef,struct mhitm_data * mhm)2786 mhitm_ad_drst(struct monst *magr, struct attack *mattk, struct monst *mdef,
2787               struct mhitm_data *mhm)
2788 {
2789     struct permonst *pa = magr->data;
2790 
2791     if (magr == &g.youmonst) {
2792         /* uhitm */
2793         int armpro = magic_negation(mdef);
2794         /* since hero can't be cancelled, only defender's armor applies */
2795         boolean negated = !(rn2(10) >= 3 * armpro);
2796 
2797         if (!negated && !rn2(8)) {
2798             Your("%s was poisoned!", mpoisons_subj(magr, mattk));
2799             if (resists_poison(mdef)) {
2800                 pline_The("poison doesn't seem to affect %s.", mon_nam(mdef));
2801             }
2802             else {
2803                 mhm->damage += rn1(10, 6);
2804             }
2805         }
2806     } else if (mdef == &g.youmonst) {
2807         /* mhitu */
2808         int armpro = magic_negation(mdef);
2809         boolean uncancelled = !magr->mcan && (rn2(10) >= 3 * armpro);
2810         int ptmp = A_STR;  /* A_STR == 0 */
2811         char buf[BUFSZ];
2812 
2813         switch (mattk->adtyp) {
2814         case AD_DRST: ptmp = A_STR; break;
2815         case AD_DRDX: ptmp = A_DEX; break;
2816         case AD_DRCO: ptmp = A_CON; break;
2817         }
2818         hitmsg(magr, mattk);
2819         if (uncancelled && !rn2(8)) {
2820             Sprintf(buf, "%s %s", s_suffix(Monnam(magr)),
2821                     mpoisons_subj(magr, mattk));
2822             poisoned(buf, ptmp, pmname(pa, Mgender(magr)), 30, FALSE);
2823         }
2824     } else {
2825         /* mhitm */
2826         int armpro = magic_negation(mdef);
2827         boolean cancelled = magr->mcan || !(rn2(10) >= 3 * armpro);
2828 
2829         if (!cancelled && !rn2(8)) {
2830             mhitm_really_poison(magr, mattk, mdef, mhm);
2831         }
2832     }
2833 }
2834 
2835 void
mhitm_ad_drin(struct monst * magr,struct attack * mattk,struct monst * mdef,struct mhitm_data * mhm)2836 mhitm_ad_drin(struct monst *magr, struct attack *mattk, struct monst *mdef,
2837               struct mhitm_data *mhm)
2838 {
2839     struct permonst *pd = mdef->data;
2840 
2841     if (magr == &g.youmonst) {
2842         /* uhitm */
2843         struct obj *helmet;
2844 
2845         if (g.notonhead || !has_head(pd)) {
2846             pline("%s doesn't seem harmed.", Monnam(mdef));
2847             /* hero should skip remaining AT_TENT+AD_DRIN attacks
2848                because they'll be just as harmless as this one (and also
2849                to reduce verbosity) */
2850             g.skipdrin = TRUE;
2851             mhm->damage = 0;
2852             if (!Unchanging && pd == &mons[PM_GREEN_SLIME]) {
2853                 if (!Slimed) {
2854                     You("suck in some slime and don't feel very well.");
2855                     make_slimed(10L, (char *) 0);
2856                 }
2857             }
2858             return;
2859         }
2860         if (m_slips_free(mdef, mattk))
2861             return;
2862 
2863         if ((helmet = which_armor(mdef, W_ARMH)) != 0 && rn2(8)) {
2864             pline("%s %s blocks your attack to %s head.",
2865                   s_suffix(Monnam(mdef)), helm_simple_name(helmet),
2866                   mhis(mdef));
2867             return;
2868         }
2869 
2870         (void) eat_brains(&g.youmonst, mdef, TRUE, &mhm->damage);
2871     } else if (mdef == &g.youmonst) {
2872         /* mhitu */
2873         hitmsg(magr, mattk);
2874         if (defends(AD_DRIN, uwep) || !has_head(pd)) {
2875             You("don't seem harmed.");
2876             /* attacker should skip remaining AT_TENT+AD_DRIN attacks */
2877             g.skipdrin = TRUE;
2878             /* Not clear what to do for green slimes */
2879             return;
2880         }
2881         if (u_slip_free(magr, mattk))
2882             return;
2883 
2884         if (uarmh && rn2(8)) {
2885             /* not body_part(HEAD) */
2886             Your("%s blocks the attack to your head.",
2887                  helm_simple_name(uarmh));
2888             return;
2889         }
2890         /* negative armor class doesn't reduce this damage */
2891         if (Half_physical_damage)
2892             mhm->damage = (mhm->damage + 1) / 2;
2893         mdamageu(magr, mhm->damage);
2894         mhm->damage = 0; /* don't inflict a second dose below */
2895 
2896         if (!uarmh || uarmh->otyp != DUNCE_CAP) {
2897             /* eat_brains() will miss if target is mindless (won't
2898                happen here; hero is considered to retain his mind
2899                regardless of current shape) or is noncorporeal
2900                (can't happen here; no one can poly into a ghost
2901                or shade) so this check for missing is academic */
2902             if (eat_brains(magr, mdef, TRUE, (int *) 0) == MM_MISS)
2903                 return;
2904         }
2905         /* adjattrib gives dunce cap message when appropriate */
2906         (void) adjattrib(A_INT, -rnd(2), AA_YESMSG);
2907     } else {
2908         /* mhitm */
2909         char buf[BUFSZ];
2910 
2911         if (g.notonhead || !has_head(pd)) {
2912             if (g.vis && canspotmon(mdef))
2913                 pline("%s doesn't seem harmed.", Monnam(mdef));
2914             /* Not clear what to do for green slimes */
2915             mhm->damage = 0;
2916             /* don't bother with additional DRIN attacks since they wouldn't
2917                be able to hit target on head either */
2918             g.skipdrin = TRUE; /* affects mattackm()'s attack loop */
2919             return;
2920         }
2921         if ((mdef->misc_worn_check & W_ARMH) && rn2(8)) {
2922             if (g.vis && canspotmon(magr) && canseemon(mdef)) {
2923                 Strcpy(buf, s_suffix(Monnam(mdef)));
2924                 pline("%s helmet blocks %s attack to %s head.", buf,
2925                       s_suffix(mon_nam(magr)), mhis(mdef));
2926             }
2927             return;
2928         }
2929         mhm->hitflags = eat_brains(magr, mdef, g.vis, &mhm->damage);
2930     }
2931 }
2932 
2933 void
mhitm_ad_stck(struct monst * magr,struct attack * mattk,struct monst * mdef,struct mhitm_data * mhm)2934 mhitm_ad_stck(struct monst *magr, struct attack *mattk, struct monst *mdef,
2935               struct mhitm_data *mhm)
2936 {
2937     struct permonst *pd = mdef->data;
2938 
2939     if (magr == &g.youmonst) {
2940         /* uhitm */
2941         int armpro = magic_negation(mdef);
2942         /* since hero can't be cancelled, only defender's armor applies */
2943         boolean negated = !(rn2(10) >= 3 * armpro);
2944 
2945         if (!negated && !sticks(pd) && distu(mdef->mx, mdef->my) <= 2)
2946             set_ustuck(mdef); /* it's now stuck to you */
2947     } else if (mdef == &g.youmonst) {
2948         /* mhitu */
2949         int armpro = magic_negation(mdef);
2950         boolean uncancelled = !magr->mcan && (rn2(10) >= 3 * armpro);
2951 
2952         hitmsg(magr, mattk);
2953         if (uncancelled && !u.ustuck && !sticks(pd)) {
2954             set_ustuck(magr);
2955         }
2956     } else {
2957         /* mhitm */
2958         int armpro = magic_negation(mdef);
2959         boolean cancelled = magr->mcan || !(rn2(10) >= 3 * armpro);
2960 
2961         if (cancelled)
2962             mhm->damage = 0;
2963     }
2964 }
2965 
2966 void
mhitm_ad_wrap(struct monst * magr,struct attack * mattk,struct monst * mdef,struct mhitm_data * mhm)2967 mhitm_ad_wrap(struct monst *magr, struct attack *mattk, struct monst *mdef,
2968               struct mhitm_data *mhm)
2969 {
2970     struct permonst *pd = mdef->data;
2971 
2972     if (magr == &g.youmonst) {
2973         /* uhitm */
2974         if (!sticks(pd)) {
2975             if (!u.ustuck && !rn2(10)) {
2976                 if (m_slips_free(mdef, mattk)) {
2977                     mhm->damage = 0;
2978                 } else {
2979                     You("swing yourself around %s!", mon_nam(mdef));
2980                     set_ustuck(mdef);
2981                 }
2982             } else if (u.ustuck == mdef) {
2983                 /* Monsters don't wear amulets of magical breathing */
2984                 if (is_pool(u.ux, u.uy) && !is_swimmer(pd)
2985                     && !amphibious(pd)) {
2986                     You("drown %s...", mon_nam(mdef));
2987                     mhm->damage = mdef->mhp;
2988                 } else if (mattk->aatyp == AT_HUGS)
2989                     pline("%s is being crushed.", Monnam(mdef));
2990             } else {
2991                 mhm->damage = 0;
2992                 if (flags.verbose)
2993                     You("brush against %s %s.", s_suffix(mon_nam(mdef)),
2994                         mbodypart(mdef, LEG));
2995             }
2996         } else
2997             mhm->damage = 0;
2998     } else if (mdef == &g.youmonst) {
2999         /* mhitu */
3000         if ((!magr->mcan || u.ustuck == magr) && !sticks(pd)) {
3001             if (!u.ustuck && !rn2(10)) {
3002                 if (u_slip_free(magr, mattk)) {
3003                     mhm->damage = 0;
3004                 } else {
3005                     set_ustuck(magr); /* before message, for botl update */
3006                     pline("%s swings itself around you!", Monnam(magr));
3007                 }
3008             } else if (u.ustuck == magr) {
3009                 if (is_pool(magr->mx, magr->my) && !Swimming && !Amphibious) {
3010                     boolean moat = (levl[magr->mx][magr->my].typ != POOL)
3011                                    && (levl[magr->mx][magr->my].typ != WATER)
3012                                    && !Is_medusa_level(&u.uz)
3013                                    && !Is_waterlevel(&u.uz);
3014 
3015                     pline("%s drowns you...", Monnam(magr));
3016                     g.killer.format = KILLED_BY_AN;
3017                     Sprintf(g.killer.name, "%s by %s",
3018                             moat ? "moat" : "pool of water",
3019                             an(pmname(magr->data, Mgender(magr))));
3020                     done(DROWNING);
3021                 } else if (mattk->aatyp == AT_HUGS) {
3022                     You("are being crushed.");
3023                 }
3024             } else {
3025                 mhm->damage = 0;
3026                 if (flags.verbose)
3027                     pline("%s brushes against your %s.", Monnam(magr),
3028                           body_part(LEG));
3029             }
3030         } else
3031             mhm->damage = 0;
3032     } else {
3033         /* mhitm */
3034         if (magr->mcan)
3035             mhm->damage = 0;
3036     }
3037 }
3038 
3039 void
mhitm_ad_plys(struct monst * magr,struct attack * mattk,struct monst * mdef,struct mhitm_data * mhm)3040 mhitm_ad_plys(struct monst *magr, struct attack *mattk, struct monst *mdef,
3041               struct mhitm_data *mhm)
3042 {
3043     if (magr == &g.youmonst) {
3044         /* uhitm */
3045         int armpro = magic_negation(mdef);
3046         /* since hero can't be cancelled, only defender's armor applies */
3047         boolean negated = !(rn2(10) >= 3 * armpro);
3048 
3049         if (!negated && mdef->mcanmove && !rn2(3) && mhm->damage < mdef->mhp) {
3050             if (!Blind)
3051                 pline("%s is frozen by you!", Monnam(mdef));
3052             paralyze_monst(mdef, rnd(10));
3053         }
3054     } else if (mdef == &g.youmonst) {
3055         /* mhitu */
3056         int armpro = magic_negation(mdef);
3057         boolean uncancelled = !magr->mcan && (rn2(10) >= 3 * armpro);
3058 
3059         /* Ghosts don't have a "paralyzing touch"; this is simply the most
3060          * convenient place to put this code. What they actually do is try to
3061          * pop up out of nowhere right next to you, frightening you to death
3062          * (which of course paralyzes you). */
3063         if (magr->data == &mons[PM_GHOST]) {
3064             mhm->damage = 0;
3065             boolean couldspot = canspotmon(magr);
3066             if (g.multi < 0) { /* already paralyzed; ghost won't appear */
3067                 return;
3068             }
3069             if (magr->minvis) {
3070                 magr->minvis = 0;
3071                 magr->mspec_used = d(2, 8);
3072                 /* canseemon rather than canspotmon; if you can spot but not see
3073                  * the ghost, you won't notice that it became visible */
3074                 if (canseemon(magr) && !Unaware) {
3075                     newsym(magr->mx, magr->my);
3076                     if (!couldspot) {
3077                         /* only works if you didn't know it was there before it
3078                         * turned visible */
3079                         if (Hallucination)
3080                             verbalize("Boo!");
3081                         else
3082                             pline("A ghost appears out of nowhere!");
3083                         scary_ghost(magr);
3084                     }
3085                     else {
3086                         pline("%s becomes visible!", Monnam(magr));
3087                         You("aren't scared.");
3088                     }
3089                 }
3090             }
3091             return;
3092         }
3093         hitmsg(magr, mattk);
3094         if (uncancelled && g.multi >= 0 && !rn2(3)) {
3095             boolean msg_given = FALSE;
3096             if (!Free_action) {
3097                 if (!canspotmon(magr)) {
3098                     You("are frozen!");
3099                 }
3100                 else {
3101                     You("are frozen by %s!", mon_nam(magr));
3102                 }
3103                 msg_given = TRUE;
3104                 /* set g.multi_reason;
3105                    3.6.x used "paralyzed by a monster"; be more specific */
3106                 dynamic_multi_reason(magr, "paralyzed", FALSE);
3107             }
3108             make_paralyzed(rnd(10), !msg_given, (const char *) 0);
3109         }
3110     } else {
3111         /* mhitm */
3112         int armpro = magic_negation(mdef);
3113         boolean cancelled = magr->mcan || !(rn2(10) >= 3 * armpro);
3114 
3115         if (!cancelled && mdef->mcanmove) {
3116             if (g.vis && canspotmon(mdef)) {
3117                 char buf[BUFSZ];
3118                 Strcpy(buf, Monnam(mdef));
3119                 pline("%s is frozen by %s.", buf, mon_nam(magr));
3120             }
3121             paralyze_monst(mdef, rnd(10));
3122         }
3123     }
3124 }
3125 
3126 void
mhitm_ad_slee(struct monst * magr,struct attack * mattk,struct monst * mdef,struct mhitm_data * mhm UNUSED)3127 mhitm_ad_slee(struct monst *magr, struct attack *mattk, struct monst *mdef,
3128               struct mhitm_data *mhm UNUSED)
3129 {
3130     if (magr == &g.youmonst) {
3131         /* uhitm */
3132         int armpro = magic_negation(mdef);
3133         /* since hero can't be cancelled, only defender's armor applies */
3134         boolean negated = !(rn2(10) >= 3 * armpro);
3135 
3136         if (!negated && !mdef->msleeping && sleep_monst(mdef, rnd(10), -1)) {
3137             if (!Blind)
3138                 pline("%s is put to sleep by you!", Monnam(mdef));
3139             slept_monst(mdef);
3140         }
3141     } else if (mdef == &g.youmonst) {
3142         /* mhitu */
3143         int armpro = magic_negation(mdef);
3144         boolean uncancelled = !magr->mcan && (rn2(10) >= 3 * armpro);
3145 
3146         hitmsg(magr, mattk);
3147         if (uncancelled && g.multi >= 0 && !rn2(5)) {
3148             if (Sleep_resistance)
3149                 return;
3150             fall_asleep(-rnd(10), TRUE);
3151             if (Blind)
3152                 You("are put to sleep!");
3153             else
3154                 You("are put to sleep by %s!", mon_nam(magr));
3155         }
3156     } else {
3157         /* mhitm */
3158         int armpro = magic_negation(mdef);
3159         boolean cancelled = magr->mcan || !(rn2(10) >= 3 * armpro);
3160 
3161         if (!cancelled && !mdef->msleeping
3162             && sleep_monst(mdef, rnd(10), -1)) {
3163             if (g.vis && canspotmon(mdef)) {
3164                 char buf[BUFSZ];
3165                 Strcpy(buf, Monnam(mdef));
3166                 pline("%s is put to sleep by %s.", buf, mon_nam(magr));
3167             }
3168             mdef->mstrategy &= ~STRAT_WAITFORU;
3169             slept_monst(mdef);
3170         }
3171     }
3172 }
3173 
3174 void
mhitm_ad_slim(struct monst * magr,struct attack * mattk,struct monst * mdef,struct mhitm_data * mhm)3175 mhitm_ad_slim(struct monst *magr, struct attack *mattk, struct monst *mdef,
3176               struct mhitm_data *mhm)
3177 {
3178     struct permonst *pd = mdef->data;
3179 
3180     if (magr == &g.youmonst) {
3181         /* uhitm */
3182         int armpro = magic_negation(mdef);
3183         /* since hero can't be cancelled, only defender's armor applies */
3184         boolean negated = !(rn2(10) >= 3 * armpro);
3185 
3186         if (negated)
3187             return; /* physical damage only */
3188         if (!rn2(4) && !slimeproof(pd)) {
3189             if (!munslime(mdef, TRUE) && !DEADMONSTER(mdef)) {
3190                 /* this assumes newcham() won't fail; since hero has
3191                    a slime attack, green slimes haven't been geno'd */
3192                 You("turn %s into slime.", mon_nam(mdef));
3193                 if (newcham(mdef, &mons[PM_GREEN_SLIME], FALSE, FALSE))
3194                     pd = mdef->data;
3195             }
3196             /* munslime attempt could have been fatal */
3197             if (DEADMONSTER(mdef)) {
3198                 mhm->hitflags = MM_DEF_DIED; /* skip death message */
3199                 mhm->done = TRUE;
3200                 return;
3201             }
3202             mhm->damage = 0;
3203         }
3204     } else if (mdef == &g.youmonst) {
3205         /* mhitu */
3206         int armpro = magic_negation(mdef);
3207         boolean uncancelled = !magr->mcan && (rn2(10) >= 3 * armpro);
3208 
3209         hitmsg(magr, mattk);
3210         if (!uncancelled)
3211             return;
3212         if (flaming(pd)) {
3213             pline_The("slime burns away!");
3214             mhm->damage = 0;
3215         } else if ((Unchanging && !can_slime_with_unchanging())
3216                    || noncorporeal(pd)
3217                    || pd == &mons[PM_GREEN_SLIME]) {
3218             You("are unaffected.");
3219             mhm->damage = 0;
3220         } else if (!Slimed) {
3221             You("don't feel very well.");
3222             make_slimed(10L, (char *) 0);
3223             delayed_killer(SLIMED, KILLED_BY_AN,
3224                            pmname(magr->data, Mgender(magr)));
3225         } else
3226             pline("Yuck!");
3227     } else {
3228         /* mhitm */
3229         int armpro = magic_negation(mdef);
3230         boolean cancelled = magr->mcan || !(rn2(10) >= 3 * armpro);
3231 
3232         if (cancelled)
3233             return; /* physical damage only */
3234         if (!rn2(4) && !slimeproof(pd)) {
3235             if (!munslime(mdef, FALSE) && !DEADMONSTER(mdef)) {
3236                 if (newcham(mdef, &mons[PM_GREEN_SLIME], FALSE,
3237                             (boolean) (g.vis && canseemon(mdef))))
3238                     pd = mdef->data;
3239                 mdef->mstrategy &= ~STRAT_WAITFORU;
3240                 mhm->hitflags = MM_HIT;
3241             }
3242             /* munslime attempt could have been fatal,
3243                potentially to multiple monsters (SCR_FIRE) */
3244             if (DEADMONSTER(magr))
3245                 mhm->hitflags |= MM_AGR_DIED;
3246             if (DEADMONSTER(mdef))
3247                 mhm->hitflags |= MM_DEF_DIED;
3248             mhm->damage = 0;
3249         }
3250     }
3251 }
3252 
3253 void
mhitm_ad_ench(struct monst * magr,struct attack * mattk,struct monst * mdef,struct mhitm_data * mhm UNUSED)3254 mhitm_ad_ench(struct monst *magr, struct attack *mattk, struct monst *mdef,
3255               struct mhitm_data *mhm UNUSED)
3256 {
3257     if (magr == &g.youmonst) {
3258         /* uhitm */
3259         /* there's no msomearmor() function, so just do damage */
3260     } else if (mdef == &g.youmonst) {
3261         /* mhitu */
3262         int armpro = magic_negation(mdef);
3263         boolean uncancelled = !magr->mcan && (rn2(10) >= 3 * armpro);
3264 
3265         hitmsg(magr, mattk);
3266         /* uncancelled is sufficient enough; please
3267            don't make this attack less frequent */
3268         if (uncancelled) {
3269             struct obj *obj = some_armor(mdef);
3270 
3271             if (!obj) {
3272                 /* some rings are susceptible;
3273                    amulets and blindfolds aren't (at present) */
3274                 switch (rn2(5)) {
3275                 case 0:
3276                     break;
3277                 case 1:
3278                     obj = uright;
3279                     break;
3280                 case 2:
3281                     obj = uleft;
3282                     break;
3283                 case 3:
3284                     obj = uamul;
3285                     break;
3286                 case 4:
3287                     obj = ublindf;
3288                     break;
3289                 }
3290             }
3291             if (drain_item(obj, FALSE)) {
3292                 pline("%s less effective.", Yobjnam2(obj, "seem"));
3293             }
3294         }
3295     } else {
3296         /* mhitm */
3297         /* there's no msomearmor() function, so just do damage */
3298     }
3299 }
3300 
3301 void
mhitm_ad_slow(struct monst * magr,struct attack * mattk,struct monst * mdef,struct mhitm_data * mhm UNUSED)3302 mhitm_ad_slow(struct monst *magr, struct attack *mattk, struct monst *mdef,
3303               struct mhitm_data *mhm UNUSED)
3304 {
3305     if (magr == &g.youmonst) {
3306         /* uhitm */
3307         int armpro = magic_negation(mdef);
3308         /* since hero can't be cancelled, only defender's armor applies */
3309         boolean negated = !(rn2(10) >= 3 * armpro);
3310 
3311         if (!negated && mdef->mspeed != MSLOW) {
3312             unsigned int oldspeed = mdef->mspeed;
3313 
3314             mon_adjust_speed(mdef, -1, (struct obj *) 0);
3315             if (mdef->mspeed != oldspeed && canseemon(mdef))
3316                 pline("%s slows down.", Monnam(mdef));
3317         }
3318     } else if (mdef == &g.youmonst) {
3319         /* mhitu */
3320         int armpro = magic_negation(mdef);
3321         boolean uncancelled = !magr->mcan && (rn2(10) >= 3 * armpro);
3322 
3323         hitmsg(magr, mattk);
3324         if (uncancelled && HFast && !defends(AD_SLOW, uwep) && !rn2(4))
3325             u_slow_down();
3326     } else {
3327         /* mhitm */
3328         int armpro = magic_negation(mdef);
3329         boolean cancelled = magr->mcan || !(rn2(10) >= 3 * armpro);
3330 
3331         if (!cancelled && mdef->mspeed != MSLOW) {
3332             unsigned int oldspeed = mdef->mspeed;
3333 
3334             mon_adjust_speed(mdef, -1, (struct obj *) 0);
3335             mdef->mstrategy &= ~STRAT_WAITFORU;
3336             if (mdef->mspeed != oldspeed && g.vis && canspotmon(mdef))
3337                 pline("%s slows down.", Monnam(mdef));
3338         }
3339     }
3340 }
3341 
3342 void
mhitm_ad_conf(struct monst * magr,struct attack * mattk,struct monst * mdef,struct mhitm_data * mhm)3343 mhitm_ad_conf(struct monst *magr, struct attack *mattk, struct monst *mdef,
3344               struct mhitm_data *mhm)
3345 {
3346     if (magr == &g.youmonst) {
3347         /* uhitm */
3348         if (!mdef->mconf) {
3349             if (canseemon(mdef))
3350                 pline("%s looks confused.", Monnam(mdef));
3351             mdef->mconf = 1;
3352         }
3353     } else if (mdef == &g.youmonst) {
3354         /* mhitu */
3355         hitmsg(magr, mattk);
3356         if (!magr->mcan && !rn2(4) && !magr->mspec_used) {
3357             magr->mspec_used = magr->mspec_used + (mhm->damage + rn2(6));
3358             if (Confusion)
3359                 You("are getting even more confused.");
3360             else
3361                 You("are getting confused.");
3362             make_confused(HConfusion + mhm->damage, FALSE);
3363         }
3364         mhm->damage = 0;
3365     } else {
3366         /* mhitm */
3367         /* Since confusing another monster doesn't have a real time
3368          * limit, setting spec_used would not really be right (though
3369          * we still should check for it).
3370          */
3371         if (!magr->mcan && !mdef->mconf && !magr->mspec_used) {
3372             if (g.vis && canseemon(mdef))
3373                 pline("%s looks confused.", Monnam(mdef));
3374             mdef->mconf = 1;
3375             mdef->mstrategy &= ~STRAT_WAITFORU;
3376         }
3377     }
3378 }
3379 
3380 void
mhitm_ad_poly(struct monst * magr,struct attack * mattk,struct monst * mdef,struct mhitm_data * mhm)3381 mhitm_ad_poly(struct monst *magr, struct attack *mattk,
3382               struct monst *mdef, struct mhitm_data *mhm)
3383 {
3384     if (magr == &g.youmonst) {
3385         /* uhitm */
3386         int armpro = magic_negation(mdef);
3387         /* require weaponless attack in order to honor AD_POLY;
3388            since hero can't be cancelled, only defender's armor applies */
3389         boolean negated = uwep || !(rn2(10) >= 3 * armpro);
3390 
3391         if (!negated && mhm->damage < mdef->mhp)
3392             mhm->damage = mon_poly(&g.youmonst, mdef, mhm->damage);
3393     } else if (mdef == &g.youmonst) {
3394         /* mhitu */
3395         int armpro = magic_negation(&g.youmonst);
3396         boolean uncancelled = !magr->mcan && (rn2(10) >= 3 * armpro);
3397 
3398         hitmsg(magr, mattk);
3399         if (uncancelled
3400             && Maybe_Half_Phys(mhm->damage) < (Upolyd ? u.mh : u.uhp))
3401             mhm->damage = mon_poly(magr, &g.youmonst, mhm->damage);
3402     } else {
3403         /* mhitm */
3404         if (!magr->mcan && mhm->damage < mdef->mhp)
3405             mhm->damage = mon_poly(magr, mdef, mhm->damage);
3406     }
3407 }
3408 
3409 void
mhitm_ad_pits(struct monst * magr,struct attack * mattk,struct monst * mdef,struct mhitm_data * mhm)3410 mhitm_ad_pits(struct monst *magr, struct attack *mattk,
3411               struct monst *mdef, struct mhitm_data *mhm)
3412 {
3413     if (magr == &g.youmonst) {
3414         /* uhitm */
3415         if (u.uswallow) {
3416             return;
3417         }
3418     } else if (mdef == &g.youmonst) {
3419         /* mhitu */
3420         /* For some reason, the uhitm code calls this for any AT_HUGS attack,
3421          * but the mhitu code doesn't. */
3422         if (mattk->aatyp == AT_HUGS) {
3423             set_ustuck(magr);
3424         }
3425         if (magr->mcan) {
3426             return;
3427         }
3428     } else {
3429         /* mhitm */
3430         if (magr->mcan) {
3431             return;
3432         }
3433     }
3434     if (!create_pit_under(mdef, magr)) {
3435         mhm->damage = 0;
3436     }
3437 }
3438 
3439 void
mhitm_ad_wthr(struct monst * magr,struct attack * mattk,struct monst * mdef,struct mhitm_data * mhm)3440 mhitm_ad_wthr(struct monst *magr, struct attack *mattk,
3441               struct monst *mdef, struct mhitm_data *mhm)
3442 {
3443     uchar withertime = max(2, mhm->damage);
3444     mhm->damage = 0; /* doesn't deal immediate damage */
3445     int armpro = magic_negation(mdef);
3446     boolean no_effect =
3447         (nonliving(mdef->data) /* This could use is_fleshy(), but that would
3448                                   make a large set of monsters immune like
3449                                   fungus, blobs, and jellies. */
3450          || is_vampshifter(mdef)
3451          || (magr != &g.youmonst && magr->mcan)
3452          || !(rn2(10) >= 3 * armpro));
3453     boolean lose_maxhp = (withertime >= 8); /* if already withering */
3454 
3455     if (mdef == &g.youmonst) {
3456         /* mhitu */
3457         hitmsg(magr, mattk);
3458         if (!no_effect) {
3459             if (Withering) {
3460                 Your("withering speeds up!");
3461             }
3462             else {
3463                 You("begin to wither away!");
3464             }
3465             incr_itimeout(&HWithering, withertime);
3466             if (lose_maxhp) {
3467                 if (Upolyd && u.mhmax > 1) {
3468                     u.mhmax--;
3469                     u.mh = min(u.mh, u.mhmax);
3470                 }
3471                 else if (u.uhpmax > 1) {
3472                     u.uhpmax--;
3473                     u.uhp = min(u.uhp, u.uhpmax);
3474                 }
3475             }
3476             g.context.botl = TRUE;
3477         }
3478     } else {
3479         /* uhitm, mhitm */
3480         if (!no_effect) {
3481             if (canseemon(mdef)) {
3482                 pline("%s is withering away!", Monnam(mdef));
3483             }
3484             if (mdef->mwither + withertime > UCHAR_MAX) {
3485                 mdef->mwither = UCHAR_MAX;
3486             }
3487             else {
3488                 mdef->mwither += withertime;
3489             }
3490             if (lose_maxhp && mdef->mhpmax > 1) {
3491                 mdef->mhpmax--;
3492                 mdef->mhp = min(mdef->mhp, mdef->mhpmax);
3493             }
3494             mdef->mwither_from_u = (magr == &g.youmonst);
3495         }
3496     }
3497 }
3498 
3499 void
mhitm_ad_famn(struct monst * magr,struct attack * mattk UNUSED,struct monst * mdef,struct mhitm_data * mhm)3500 mhitm_ad_famn(struct monst *magr, struct attack *mattk UNUSED,
3501               struct monst *mdef, struct mhitm_data *mhm)
3502 {
3503     if (magr == &g.youmonst) {
3504         /* uhitm */
3505         mhm->damage = 0;
3506     } else if (mdef == &g.youmonst) {
3507         /* mhitu */
3508         pline("%s reaches out, and your body shrivels.", Monnam(magr));
3509         exercise(A_CON, FALSE);
3510         if (!is_fainted())
3511             morehungry(rn1(40, 40));
3512         /* plus the normal damage */
3513     } else {
3514         /* mhitm */
3515         mhm->damage = 0;
3516     }
3517 }
3518 
3519 void
mhitm_ad_pest(struct monst * magr,struct attack * mattk UNUSED,struct monst * mdef,struct mhitm_data * mhm)3520 mhitm_ad_pest(struct monst *magr, struct attack *mattk UNUSED,
3521               struct monst *mdef, struct mhitm_data *mhm)
3522 {
3523     struct permonst *pa = magr->data;
3524 
3525     if (magr == &g.youmonst) {
3526         /* uhitm */
3527         mhm->damage = 0;
3528     } else if (mdef == &g.youmonst) {
3529         /* mhitu */
3530         pline("%s reaches out, and you feel fever and chills.", Monnam(magr));
3531         (void) diseasemu(pa);
3532         /* plus the normal damage */
3533     } else {
3534         /* mhitm */
3535         mhm->damage = 0;
3536     }
3537 }
3538 
3539 void
mhitm_ad_deth(struct monst * magr,struct attack * mattk UNUSED,struct monst * mdef,struct mhitm_data * mhm)3540 mhitm_ad_deth(struct monst *magr, struct attack *mattk UNUSED,
3541               struct monst *mdef, struct mhitm_data *mhm)
3542 {
3543     struct permonst *pd = mdef->data;
3544 
3545     if (magr == &g.youmonst) {
3546         /* uhitm */
3547         mhm->damage = 0;
3548     } else if (mdef == &g.youmonst) {
3549         /* mhitu */
3550         pline("%s reaches out with its deadly touch.", Monnam(magr));
3551         if (is_undead(pd)) {
3552             /* Still does normal damage */
3553             pline("Was that the touch of death?");
3554             return;
3555         }
3556         switch (rn2(20)) {
3557         case 19:
3558         case 18:
3559         case 17:
3560             if (!Antimagic) {
3561                 g.killer.format = KILLED_BY_AN;
3562                 Strcpy(g.killer.name, "touch of death");
3563                 done(DIED);
3564                 mhm->damage = 0;
3565                 return;
3566             }
3567             /*FALLTHRU*/
3568         default: /* case 16: ... case 5: */
3569             You_feel("your life force draining away...");
3570             mhm->permdmg = 1; /* actual damage done below */
3571             return;
3572         case 4:
3573         case 3:
3574         case 2:
3575         case 1:
3576         case 0:
3577             if (Antimagic)
3578                 shieldeff(u.ux, u.uy);
3579             pline("Lucky for you, it didn't work!");
3580             mhm->damage = 0;
3581             return;
3582         }
3583     } else {
3584         /* mhitm */
3585         mhm->damage = 0;
3586     }
3587 }
3588 
3589 void
mhitm_ad_halu(struct monst * magr,struct attack * mattk UNUSED,struct monst * mdef,struct mhitm_data * mhm)3590 mhitm_ad_halu(struct monst *magr, struct attack *mattk UNUSED,
3591               struct monst *mdef, struct mhitm_data *mhm)
3592 {
3593     struct permonst *pd = mdef->data;
3594 
3595     if (magr == &g.youmonst) {
3596         /* uhitm */
3597         mhm->damage = 0;
3598     } else if (mdef == &g.youmonst) {
3599         /* mhitu */
3600         mhm->damage = 0;
3601     } else {
3602         /* mhitm */
3603         if (!magr->mcan && haseyes(pd) && mdef->mcansee) {
3604             if (g.vis && canseemon(mdef))
3605                 pline("%s looks %sconfused.", Monnam(mdef),
3606                       mdef->mconf ? "more " : "");
3607             mdef->mconf = 1;
3608             mdef->mstrategy &= ~STRAT_WAITFORU;
3609         }
3610         mhm->damage = 0;
3611     }
3612 }
3613 
3614 boolean
do_stone_u(struct monst * mtmp)3615 do_stone_u(struct monst *mtmp)
3616 {
3617     if (!Stoned && !Stone_resistance
3618         && !(poly_when_stoned(g.youmonst.data)
3619              && polymon(PM_STONE_GOLEM, POLYMON_ALL_MSGS))) {
3620         int kformat = KILLED_BY_AN;
3621         const char *kname = pmname(mtmp->data, Mgender(mtmp));
3622 
3623         if (mtmp->data->geno & G_UNIQ) {
3624             if (!type_is_pname(mtmp->data))
3625                 kname = the(kname);
3626             kformat = KILLED_BY;
3627         }
3628         make_stoned(5L, (char *) 0, kformat, kname);
3629         return 1;
3630         /* done_in_by(mtmp, STONING); */
3631     }
3632     return 0;
3633 }
3634 
3635 void
do_stone_mon(struct monst * magr,struct attack * mattk UNUSED,struct monst * mdef,struct mhitm_data * mhm)3636 do_stone_mon(struct monst *magr, struct attack *mattk UNUSED,
3637              struct monst *mdef, struct mhitm_data *mhm)
3638 {
3639     struct permonst *pd = mdef->data;
3640 
3641     /* may die from the acid if it eats a stone-curing corpse */
3642     if (munstone(mdef, FALSE))
3643         goto post_stone;
3644     if (poly_when_stoned(pd)) {
3645         mon_to_stone(mdef);
3646         mhm->damage = 0;
3647         return;
3648     }
3649     if (!resists_ston(mdef)) {
3650         if (g.vis && canseemon(mdef))
3651             pline("%s turns to stone!", Monnam(mdef));
3652         monstone(mdef);
3653  post_stone:
3654         if (!DEADMONSTER(mdef)) {
3655             mhm->hitflags = MM_MISS;
3656             mhm->done = TRUE;
3657             return;
3658         } else if (mdef->mtame && !g.vis) {
3659             You(brief_feeling, "peculiarly sad");
3660         }
3661         mhm->hitflags = (MM_DEF_DIED
3662                          | (grow_up(magr, mdef) ? 0 : MM_AGR_DIED));
3663         mhm->done = TRUE;
3664         return;
3665     }
3666     mhm->damage = (mattk->adtyp == AD_STON ? 0 : 1);
3667 }
3668 
3669 void
mhitm_ad_phys(struct monst * magr,struct attack * mattk,struct monst * mdef,struct mhitm_data * mhm)3670 mhitm_ad_phys(struct monst *magr, struct attack *mattk, struct monst *mdef,
3671               struct mhitm_data *mhm)
3672 {
3673     struct permonst *pa = magr->data;
3674     struct permonst *pd = mdef->data;
3675     int artimsg = ARTIFACTHIT_NOMSG;
3676 
3677     if (magr == &g.youmonst) {
3678         /* uhitm */
3679         if (noncorporeal(pd)) {
3680             mhm->damage = 0;
3681             if (!mhm->specialdmg)
3682                 impossible("bad noncorporeal attack function flow?");
3683         }
3684         mhm->damage += mhm->specialdmg;
3685 
3686         if (mattk->aatyp == AT_WEAP) {
3687             /* hmonas() uses known_hitum() to deal physical damage,
3688                then also damageum() for non-AD_PHYS; don't inflict
3689                extra physical damage for unusual damage types */
3690             mhm->damage = 0;
3691         } else if (mattk->aatyp == AT_KICK
3692                    || mattk->aatyp == AT_CLAW
3693                    || mattk->aatyp == AT_TUCH
3694                    || mattk->aatyp == AT_HUGS) {
3695             if (thick_skinned(pd))
3696                 mhm->damage = (mattk->aatyp == AT_KICK) ? 0
3697                               : (mhm->damage + 1) / 2;
3698             /* add ring(s) of increase damage */
3699             if (u.udaminc > 0) {
3700                 /* applies even if damage was 0 */
3701                 mhm->damage += u.udaminc;
3702             } else if (mhm->damage > 0) {
3703                 /* ring(s) might be negative; avoid converting
3704                    0 to non-0 or positive to non-positive */
3705                 mhm->damage += u.udaminc;
3706                 if (mhm->damage < 1)
3707                     mhm->damage = 1;
3708             }
3709         }
3710     } else if (mdef == &g.youmonst) {
3711         /* mhitu */
3712         if (mattk->aatyp == AT_HUGS && !sticks(pd)) {
3713             if (!u.ustuck && rn2(2)) {
3714                 if (u_slip_free(magr, mattk)) {
3715                     mhm->damage = 0;
3716                 } else {
3717                     set_ustuck(magr);
3718                     pline("%s grabs you!", Monnam(magr));
3719                 }
3720             } else if (u.ustuck == magr) {
3721                 exercise(A_STR, FALSE);
3722                 You("are being %s.", (magr->data == &mons[PM_ROPE_GOLEM])
3723                                          ? "choked"
3724                                          : "crushed");
3725             }
3726         } else { /* hand to hand weapon */
3727             struct obj *otmp = MON_WEP(magr);
3728 
3729             if (mattk->aatyp == AT_WEAP && otmp) {
3730                 struct obj *marmg;
3731                 int tmp;
3732                 int wepmaterial = otmp->material;
3733                 boolean was_poisoned = (otmp->opoisoned || permapoisoned(otmp));
3734 
3735                 if (otmp->otyp == CORPSE
3736                     && touch_petrifies(&mons[otmp->corpsenm])) {
3737                     mhm->damage = 1;
3738                     pline("%s hits you with the %s corpse.", Monnam(magr),
3739                           mons[otmp->corpsenm].pmnames[NEUTRAL]);
3740                     if (!Stoned) {
3741                         if (do_stone_u(magr)) {
3742                             mhm->hitflags = MM_HIT;
3743                             mhm->done = 1;
3744                             return;
3745                         }
3746                     }
3747                 }
3748                 mhm->damage += dmgval(otmp, mdef);
3749                 if ((marmg = which_armor(magr, W_ARMG)) != 0
3750                     && marmg->otyp == GAUNTLETS_OF_POWER)
3751                     mhm->damage += rn1(4, 3); /* 3..6 */
3752                 if (mhm->damage <= 0)
3753                     mhm->damage = 1;
3754                 if (otmp->oartifact) {
3755                     artimsg = artifact_hit(magr, mdef, otmp, &mhm->damage,
3756                                            g.mhitu_dieroll);
3757                 }
3758                 if (artimsg == ARTIFACTHIT_NOMSG)
3759                     hitmsg(magr, mattk);
3760 
3761                 /* glass breakage from the attack */
3762                 break_glass_obj(some_armor(mdef));
3763                 if (break_glass_obj(MON_WEP(magr))) {
3764                     otmp = NULL;
3765                 }
3766 
3767                 if (!mhm->damage)
3768                     return;
3769                 if (Hate_material(wepmaterial)) {
3770                     /* dmgval() already added extra damage */
3771                     if ((artimsg & ARTIFACTHIT_INSTAKILLMSG) == 0) {
3772                         searmsg(magr, &g.youmonst, otmp, TRUE);
3773                     }
3774                     exercise(A_CON, FALSE);
3775                 }
3776                 /* this redundancy necessary because you have
3777                    to take the damage _before_ being cloned;
3778                    need to have at least 2 hp left to split */
3779                 tmp = mhm->damage;
3780                 if (u.uac < 0)
3781                     tmp -= rnd(-u.uac);
3782                 if (tmp < 1)
3783                     tmp = 1;
3784                 if (u.mh - tmp > 1
3785                     && (wepmaterial == IRON || wepmaterial == METAL)
3786                         /* relevant 'metal' objects are scalpel and tsurugi */
3787                     && (u.umonnum == PM_BLACK_PUDDING
3788                         || u.umonnum == PM_BROWN_PUDDING)) {
3789                     if (tmp > 1)
3790                         exercise(A_STR, FALSE);
3791                     /* inflict damage now; we know it can't be fatal */
3792                     u.mh -= tmp;
3793                     g.context.botl = 1;
3794                     mhm->damage = 0; /* don't inflict more damage below */
3795                     if (cloneu())
3796                         You("divide as %s hits you!", mon_nam(magr));
3797                 }
3798                 rustm(&g.youmonst, otmp); /* safe if otmp is NULL */
3799 
3800                 if (was_poisoned && g.mhitu_dieroll <= 5) {
3801                     char buf[BUFSZ];
3802                     /* similar to mhitm_really_poison, but we don't use the
3803                      * exact same values, nor do we want the same 1/8 chance of
3804                      * the poison taking (use 1/4, same as in the mhitm case). */
3805                     Sprintf(buf, "%s %s", s_suffix(Monnam(magr)),
3806                             mpoisons_subj(magr, mattk));
3807                     /* arbitrary, but most poison sources in the game are
3808                      * strength-based. With hpdamchance = 10, HP damage occurs
3809                      * 1/2 of the time and it will hit Str the rest of the time.
3810                      * (This is the same as poisoned ammo.) */
3811                     poisoned(buf, A_STR, pmname(magr->data, Mgender(magr)),
3812                              10, FALSE);
3813                 }
3814             } else if (mattk->aatyp != AT_TUCH || mhm->damage != 0
3815                        || magr != u.ustuck)
3816                 hitmsg(magr, mattk);
3817         }
3818     } else {
3819         /* mhitm */
3820         struct obj *mwep = MON_WEP(magr);
3821 
3822         if (mattk->aatyp != AT_WEAP && mattk->aatyp != AT_CLAW)
3823             mwep = 0;
3824 
3825         if (shade_miss(magr, mdef, mwep, FALSE, TRUE)) {
3826             mhm->damage = 0;
3827         } else if (mattk->aatyp == AT_KICK && thick_skinned(pd)) {
3828             /* [no 'kicking boots' check needed; monsters with kick attacks
3829                can't wear boots and monsters that wear boots don't kick] */
3830             mhm->damage = 0;
3831         } else if (mwep) { /* non-Null 'mwep' implies AT_WEAP || AT_CLAW */
3832             struct obj *marmg;
3833 
3834             if (mwep->otyp == CORPSE
3835                 && touch_petrifies(&mons[mwep->corpsenm])) {
3836                 do_stone_mon(magr, mattk, mdef, mhm);
3837                 if (mhm->done)
3838                     return;
3839             }
3840 
3841             mhm->damage += dmgval(mwep, mdef);
3842             if ((marmg = which_armor(magr, W_ARMG)) != 0
3843                 && marmg->otyp == GAUNTLETS_OF_POWER)
3844                 mhm->damage += rn1(4, 3); /* 3..6 */
3845             if (mhm->damage < 1) /* is this necessary?  mhitu.c has it... */
3846                 mhm->damage = 1;
3847             if (mwep->oartifact) {
3848                 /* when magr's weapon is an artifact, caller suppressed its
3849                    usual 'hit' message in case artifact_hit() delivers one;
3850                    now we'll know and might need to deliver skipped message
3851                    (note: if there's no message there'll be no auxilliary
3852                    damage so the message here isn't coming too late) */
3853                 artimsg = artifact_hit(magr, mdef, mwep, &mhm->damage,
3854                                        mhm->dieroll);
3855                 if (artimsg == ARTIFACTHIT_NOMSG) {
3856                     if (g.vis)
3857                         pline("%s hits %s.", Monnam(magr),
3858                               mon_nam_too(mdef, magr));
3859                 }
3860                 /* artifact_hit updates 'tmp' but doesn't inflict any
3861                    damage; however, it might cause carried items to be
3862                    destroyed and they might do so */
3863                 if (DEADMONSTER(mdef)) {
3864                     mhm->hitflags = (MM_DEF_DIED | (grow_up(magr, mdef) ? 0
3865                                                     : MM_AGR_DIED));
3866                     mhm->done = TRUE;
3867                     return;
3868                 }
3869             }
3870             if (mon_hates_material(mdef, mwep->material)
3871                 && (artimsg & ARTIFACTHIT_INSTAKILLMSG) == 0) {
3872                 /* extra damage already applied by dmgval() */
3873                 searmsg(magr, mdef, mwep, TRUE);
3874             }
3875             if (mhm->damage)
3876                 rustm(mdef, mwep);
3877             if ((mwep->opoisoned || permapoisoned(mwep)) && !rn2(4)) {
3878                 /* 1/4 chance of weapon poison applying is the same as in
3879                  * uhitm and mhitu cases. But since we don't need to call
3880                  * any special functions or go through tangled hmon_hitmon
3881                  * code, we can just jump straight to the poisoning. */
3882                 mhitm_really_poison(magr, mattk, mdef, mhm);
3883             }
3884         } else if (pa == &mons[PM_PURPLE_WORM] && pd == &mons[PM_SHRIEKER]) {
3885             /* hack to enhance mm_aggression(); we don't want purple
3886                worm's bite attack to kill a shrieker because then it
3887                won't swallow the corpse; but if the target survives,
3888                the subsequent engulf attack should accomplish that */
3889             if (mhm->damage >= mdef->mhp && mdef->mhp > 1)
3890                 mhm->damage = mdef->mhp - 1;
3891         }
3892     }
3893 }
3894 
3895 void
mhitm_ad_ston(struct monst * magr,struct attack * mattk,struct monst * mdef,struct mhitm_data * mhm)3896 mhitm_ad_ston(struct monst *magr, struct attack *mattk, struct monst *mdef,
3897               struct mhitm_data *mhm)
3898 {
3899     if (magr == &g.youmonst) {
3900         /* uhitm */
3901         if (!munstone(mdef, TRUE))
3902             minstapetrify(mdef, TRUE);
3903         mhm->damage = 0;
3904     } else if (mdef == &g.youmonst) {
3905         /* mhitu */
3906         hitmsg(magr, mattk);
3907         if (!rn2(3)) {
3908             if (magr->mcan) {
3909                 if (!Deaf)
3910                     You_hear("a cough from %s!", mon_nam(magr));
3911             } else {
3912                 if (Deaf)
3913                     return; /* no hiss = no stoning */
3914                 else
3915                     You_hear("%s hissing!", s_suffix(mon_nam(magr)));
3916                 if (!rn2(6)
3917                     || (flags.moonphase == NEW_MOON && !have_lizard())) {
3918                     if (Hallucination) {
3919                         You("are already stoned.");
3920                     }
3921                     else if (do_stone_u(magr)) {
3922                         mhm->hitflags = MM_HIT;
3923                         mhm->done = TRUE;
3924                         return;
3925                     }
3926                 }
3927             }
3928         }
3929     } else {
3930         /* mhitm */
3931         if (magr->mcan)
3932             return;
3933         do_stone_mon(magr, mattk, mdef, mhm);
3934         if (mhm->done)
3935             return;
3936     }
3937 }
3938 
3939 void
mhitm_ad_were(struct monst * magr,struct attack * mattk,struct monst * mdef,struct mhitm_data * mhm)3940 mhitm_ad_were(struct monst *magr, struct attack *mattk, struct monst *mdef,
3941               struct mhitm_data *mhm)
3942 {
3943     struct permonst *pa = magr->data;
3944 
3945     if (magr == &g.youmonst) {
3946         /* uhitm */
3947         mhitm_ad_phys(magr, mattk, mdef, mhm);
3948         if (mhm->done)
3949             return;
3950     } else if (mdef == &g.youmonst) {
3951         /* mhitu */
3952         int armpro = magic_negation(mdef);
3953         boolean uncancelled = !magr->mcan && (rn2(10) >= 3 * armpro);
3954 
3955         hitmsg(magr, mattk);
3956         if (uncancelled && !rn2(4) && u.ulycn == NON_PM
3957             && !Protection_from_shape_changers && !defends(AD_WERE, uwep)) {
3958             You_feel("feverish.");
3959             exercise(A_CON, FALSE);
3960             set_ulycn(monsndx(pa));
3961             retouch_equipment(2);
3962         }
3963     } else {
3964         /* mhitm */
3965         mhitm_ad_phys(magr, mattk, mdef, mhm);
3966         if (mhm->done)
3967             return;
3968     }
3969 }
3970 
3971 void
mhitm_ad_heal(struct monst * magr,struct attack * mattk,struct monst * mdef,struct mhitm_data * mhm)3972 mhitm_ad_heal(struct monst *magr, struct attack *mattk, struct monst *mdef,
3973               struct mhitm_data *mhm)
3974 {
3975     struct permonst *pd = mdef->data;
3976 
3977     if (magr == &g.youmonst) {
3978         /* uhitm */
3979         mhitm_ad_phys(magr, mattk, mdef, mhm);
3980         if (mhm->done)
3981             return;
3982     } else if (mdef == &g.youmonst) {
3983         /* mhitu */
3984         /* a cancelled nurse is just an ordinary monster,
3985          * nurses don't heal those that cause petrification */
3986         if (magr->mcan || (Upolyd && touch_petrifies(pd))) {
3987             hitmsg(magr, mattk);
3988             return;
3989         }
3990         /* weapon check should match the one in sounds.c for MS_NURSE */
3991         if (!(uwep && (uwep->oclass == WEAPON_CLASS || is_weptool(uwep)))
3992             && !uarmu && !uarm && !uarmc
3993             && !uarms && !uarmg && !uarmf && !uarmh) {
3994             boolean goaway = FALSE;
3995 
3996             pline("%s hits!  (I hope you don't mind.)", Monnam(magr));
3997             if (Upolyd) {
3998                 u.mh += rnd(7);
3999                 if (!rn2(7)) {
4000                     /* no upper limit necessary; effect is temporary */
4001                     u.mhmax++;
4002                     if (!rn2(13))
4003                         goaway = TRUE;
4004                 }
4005                 if (u.mh > u.mhmax)
4006                     u.mh = u.mhmax;
4007             } else {
4008                 u.uhp += rnd(7);
4009                 if (!rn2(7)) {
4010                     /* hard upper limit via nurse care: 25 * ulevel */
4011                     if (u.uhpmax < 5 * u.ulevel + d(2 * u.ulevel, 10))
4012                         u.uhpmax++;
4013                     if (!rn2(13))
4014                         goaway = TRUE;
4015                 }
4016                 if (u.uhp > u.uhpmax)
4017                     u.uhp = u.uhpmax;
4018             }
4019             if (!rn2(3))
4020                 exercise(A_STR, TRUE);
4021             if (!rn2(3))
4022                 exercise(A_CON, TRUE);
4023             if (Sick)
4024                 make_sick(0L, (char *) 0, FALSE, SICK_ALL);
4025             g.context.botl = 1;
4026             if (goaway) {
4027                 mongone(magr);
4028                 mhm->done = TRUE;
4029                 mhm->hitflags = MM_DEF_DIED; /* return 2??? */
4030                 return;
4031             } else if (!rn2(33)) {
4032                 if (!tele_restrict(magr))
4033                     (void) rloc(magr, TRUE);
4034                 monflee(magr, d(3, 6), TRUE, FALSE);
4035                 mhm->done = TRUE;
4036                 mhm->hitflags = MM_HIT | MM_DEF_DIED; /* return 3??? */
4037                 return;
4038             }
4039             mhm->damage = 0;
4040         } else {
4041             if (Role_if(PM_HEALER)) {
4042                 if (!Deaf && !(g.moves % 5))
4043                     verbalize("Doc, I can't help you unless you cooperate.");
4044                 mhm->damage = 0;
4045             } else
4046                 hitmsg(magr, mattk);
4047         }
4048     } else {
4049         /* mhitm */
4050         mhitm_ad_phys(magr, mattk, mdef, mhm);
4051         if (mhm->done)
4052             return;
4053     }
4054 }
4055 
4056 void
mhitm_ad_stun(struct monst * magr,struct attack * mattk,struct monst * mdef,struct mhitm_data * mhm)4057 mhitm_ad_stun(struct monst *magr, struct attack *mattk, struct monst *mdef,
4058               struct mhitm_data *mhm)
4059 {
4060     struct permonst *pd = mdef->data;
4061 
4062     if (magr == &g.youmonst) {
4063         /* uhitm */
4064         if (!Blind)
4065             pline("%s %s for a moment.", Monnam(mdef),
4066                   makeplural(stagger(pd, "stagger")));
4067         mdef->mstun = 1;
4068         mhitm_ad_phys(magr, mattk, mdef, mhm);
4069         if (mhm->done)
4070             return;
4071     } else if (mdef == &g.youmonst) {
4072         /* mhitu */
4073         hitmsg(magr, mattk);
4074         if (!magr->mcan && !rn2(4)) {
4075             make_stunned((HStun & TIMEOUT) + (long) mhm->damage, TRUE);
4076             mhm->damage /= 2;
4077         }
4078     } else {
4079         /* mhitm */
4080         if (magr->mcan)
4081             return;
4082         if (canseemon(mdef))
4083             pline("%s %s for a moment.", Monnam(mdef),
4084                   makeplural(stagger(pd, "stagger")));
4085         mdef->mstun = 1;
4086         mhitm_ad_phys(magr, mattk, mdef, mhm);
4087         if (mhm->done)
4088             return;
4089     }
4090 }
4091 
4092 void
mhitm_ad_legs(struct monst * magr,struct attack * mattk,struct monst * mdef,struct mhitm_data * mhm)4093 mhitm_ad_legs(struct monst *magr, struct attack *mattk, struct monst *mdef,
4094               struct mhitm_data *mhm)
4095 {
4096     if (magr == &g.youmonst) {
4097         /* uhitm */
4098 #if 0
4099         if (u.ucancelled) {
4100             mhm->damage = 0;
4101             return;
4102         }
4103 #endif
4104         mhitm_ad_phys(magr, mattk, mdef, mhm);
4105         if (mhm->done)
4106             return;
4107     } else if (mdef == &g.youmonst) {
4108         /* mhitu */
4109         long side = rn2(2) ? RIGHT_SIDE : LEFT_SIDE;
4110         const char *sidestr = (side == RIGHT_SIDE) ? "right" : "left",
4111                    *Monst_name = Monnam(magr), *leg = body_part(LEG);
4112 
4113         /* This case is too obvious to ignore, but Nethack is not in
4114          * general very good at considering height--most short monsters
4115          * still _can_ attack you when you're flying or mounted.
4116          */
4117         if ((u.usteed || Levitation || Flying) && !is_flyer(magr->data)) {
4118             pline("%s tries to reach your %s %s!", Monst_name, sidestr, leg);
4119             mhm->damage = 0;
4120         } else if (magr->mcan) {
4121             pline("%s nuzzles against your %s %s!", Monnam(magr),
4122                   sidestr, leg);
4123             mhm->damage = 0;
4124         } else {
4125             if (uarmf) {
4126                 if (rn2(2) && (uarmf->otyp == LOW_BOOTS
4127                                || uarmf->otyp == DWARVISH_BOOTS)) {
4128                     pline("%s pricks the exposed part of your %s %s!",
4129                           Monst_name, sidestr, leg);
4130                 } else if (!rn2(5)) {
4131                     pline("%s pricks through your %s boot!", Monst_name,
4132                           sidestr);
4133                 } else {
4134                     pline("%s scratches your %s boot!", Monst_name,
4135                           sidestr);
4136                     mhm->damage = 0;
4137                     return;
4138                 }
4139             } else
4140                 pline("%s pricks your %s %s!", Monst_name, sidestr, leg);
4141 
4142             set_wounded_legs(side, rnd(60 - ACURR(A_DEX)));
4143             exercise(A_STR, FALSE);
4144             exercise(A_DEX, FALSE);
4145         }
4146     } else {
4147         /* mhitm */
4148         if (magr->mcan) {
4149             mhm->damage = 0;
4150             return;
4151         }
4152         mhitm_ad_phys(magr, mattk, mdef, mhm);
4153         if (mhm->done)
4154             return;
4155     }
4156 }
4157 
4158 void
mhitm_ad_dgst(struct monst * magr,struct attack * mattk UNUSED,struct monst * mdef,struct mhitm_data * mhm)4159 mhitm_ad_dgst(struct monst *magr, struct attack *mattk UNUSED,
4160               struct monst *mdef, struct mhitm_data *mhm)
4161 {
4162     struct permonst *pd = mdef->data;
4163 
4164     if (magr == &g.youmonst) {
4165         /* uhitm */
4166         mhm->damage = 0;
4167     } else if (mdef == &g.youmonst) {
4168         /* mhitu */
4169         mhm->damage = 0;
4170     } else {
4171         /* mhitm */
4172         int num;
4173         struct obj *obj;
4174 
4175         /* eating a Rider or its corpse is fatal */
4176         if (is_rider(pd)) {
4177             if (g.vis && canseemon(magr))
4178                 pline("%s %s!", Monnam(magr),
4179                       (pd == &mons[PM_FAMINE])
4180                           ? "belches feebly, shrivels up and dies"
4181                           : (pd == &mons[PM_PESTILENCE])
4182                                 ? "coughs spasmodically and collapses"
4183                                 : "vomits violently and drops dead");
4184             mondied(magr);
4185             if (!DEADMONSTER(magr)) {
4186                 mhm->hitflags = MM_MISS; /* lifesaved */
4187                 mhm->done = TRUE;
4188                 return;
4189             } else if (magr->mtame && !g.vis)
4190                 You(brief_feeling, "queasy");
4191             mhm->hitflags = MM_AGR_DIED;
4192             mhm->done = TRUE;
4193             return;
4194         }
4195         if (flags.verbose && !Deaf)
4196             verbalize("Burrrrp!");
4197         mhm->damage = mdef->mhp;
4198         /* Use up amulet of life saving */
4199         if ((obj = mlifesaver(mdef)) != 0)
4200             m_useup(mdef, obj);
4201 
4202         /* Is a corpse for nutrition possible?  It may kill magr */
4203         if (!corpse_chance(mdef, magr, TRUE) || DEADMONSTER(magr))
4204             return;
4205 
4206         /* Pets get nutrition from swallowing monster whole.
4207          * No nutrition from G_NOCORPSE monster, eg, undead.
4208          * DGST monsters don't die from undead corpses
4209          */
4210         num = monsndx(pd);
4211         if (magr->mtame && !magr->isminion
4212             && !(g.mvitals[num].mvflags & G_NOCORPSE)) {
4213             struct obj *virtualcorpse = mksobj(CORPSE, FALSE, FALSE);
4214             int nutrit;
4215 
4216             set_corpsenm(virtualcorpse, num);
4217             nutrit = dog_nutrition(magr, virtualcorpse);
4218             dealloc_obj(virtualcorpse);
4219 
4220             /* only 50% nutrition, 25% of normal eating time */
4221             if (magr->meating > 1)
4222                 magr->meating = (magr->meating + 3) / 4;
4223             if (nutrit > 1)
4224                 nutrit /= 2;
4225             EDOG(magr)->hungrytime += nutrit;
4226         }
4227     }
4228 }
4229 
4230 void
mhitm_ad_samu(struct monst * magr,struct attack * mattk,struct monst * mdef,struct mhitm_data * mhm)4231 mhitm_ad_samu(struct monst *magr, struct attack *mattk, struct monst *mdef,
4232               struct mhitm_data *mhm)
4233 {
4234     if (magr == &g.youmonst) {
4235         /* uhitm */
4236         mhm->damage = 0;
4237     } else if (mdef == &g.youmonst) {
4238         /* mhitu */
4239         hitmsg(magr, mattk);
4240         /* when the Wizard or quest nemesis hits, there's a 1/20 chance
4241            to steal a quest artifact (any, not just the one for the hero's
4242            own role) or the Amulet or one of the invocation tools */
4243         if (!rn2(20))
4244             stealamulet(magr);
4245     } else {
4246         /* mhitm */
4247         mhm->damage = 0;
4248     }
4249 }
4250 
4251 void
mhitm_ad_dise(struct monst * magr,struct attack * mattk,struct monst * mdef,struct mhitm_data * mhm)4252 mhitm_ad_dise(struct monst *magr, struct attack *mattk, struct monst *mdef,
4253               struct mhitm_data *mhm)
4254 {
4255     struct permonst *pa = magr->data;
4256 
4257     if (magr == &g.youmonst) {
4258         /* uhitm */
4259         mhm->damage = 0;
4260     } else if (mdef == &g.youmonst) {
4261         /* mhitu */
4262         hitmsg(magr, mattk);
4263         if (!diseasemu(pa))
4264             mhm->damage = 0;
4265     } else {
4266         /* mhitm */
4267         mhm->damage = 0;
4268     }
4269 }
4270 
4271 void
mhitm_ad_sedu(struct monst * magr,struct attack * mattk,struct monst * mdef,struct mhitm_data * mhm)4272 mhitm_ad_sedu(struct monst *magr, struct attack *mattk, struct monst *mdef,
4273               struct mhitm_data *mhm)
4274 {
4275     struct permonst *pa = magr->data;
4276 
4277     if (magr == &g.youmonst) {
4278         /* uhitm */
4279         steal_it(mdef, mattk);
4280         mhm->damage = 0;
4281     } else if (mdef == &g.youmonst) {
4282         /* mhitu */
4283         char buf[BUFSZ];
4284 
4285         if (is_animal(magr->data)) {
4286             hitmsg(magr, mattk);
4287             if (magr->mcan)
4288                 return;
4289             /* Continue below */
4290         } else if (dmgtype(g.youmonst.data, AD_SEDU)
4291                    /* !SYSOPT_SEDUCE: when hero is attacking and AD_SSEX
4292                       is disabled, it would be changed to another damage
4293                       type, but when defending, it remains as-is */
4294                    || dmgtype(g.youmonst.data, AD_SSEX)) {
4295             pline("%s %s.", Monnam(magr),
4296                   Deaf ? "says something but you can't hear it"
4297                        : magr->minvent
4298                       ? "brags about the goods some dungeon explorer provided"
4299                   : "makes some remarks about how difficult theft is lately");
4300             if (!tele_restrict(magr))
4301                 (void) rloc(magr, TRUE);
4302             mhm->hitflags = MM_AGR_DONE; /* return 3??? */
4303             mhm->done = TRUE;
4304             return;
4305         } else {
4306             mintroduce(magr);
4307             if (magr->mcan) {
4308                 if (!Blind)
4309                     pline("%s tries to %s you, but you seem %s.",
4310                         Adjmonnam(magr, "plain"),
4311                         flags.female ? "charm" : "seduce",
4312                         flags.female ? "unaffected" : "uninterested");
4313                 if (rn2(3)) {
4314                     if (!tele_restrict(magr))
4315                         (void) rloc(magr, TRUE);
4316                     mhm->hitflags = MM_AGR_DONE; /* return 3??? */
4317                     mhm->done = TRUE;
4318                 }
4319                 return;
4320             }
4321         }
4322         buf[0] = '\0';
4323         switch (steal(magr, buf)) {
4324         case -1:
4325             mhm->hitflags = MM_AGR_DIED; /* return 2??? */
4326             mhm->done = TRUE;
4327             return;
4328         case 0:
4329             return;
4330         default:
4331             if (!is_animal(magr->data) && !tele_restrict(magr))
4332                 (void) rloc(magr, TRUE);
4333             if (is_animal(magr->data) && *buf) {
4334                 if (canseemon(magr))
4335                     pline("%s tries to %s away with %s.", Monnam(magr),
4336                           locomotion(magr->data, "run"), buf);
4337             }
4338             monflee(magr, 0, FALSE, FALSE);
4339             mhm->hitflags = MM_AGR_DONE; /* return 3??? */
4340             mhm->done = TRUE;
4341             return;
4342         }
4343     } else {
4344         /* mhitm */
4345         struct obj *obj;
4346 
4347         if (magr->mcan)
4348             return;
4349         /* find an object to steal, non-cursed if magr is tame */
4350         for (obj = mdef->minvent; obj; obj = obj->nobj)
4351             if (!magr->mtame || !obj->cursed)
4352                 break;
4353 
4354         if (obj) {
4355             char buf[BUFSZ];
4356             char onambuf[BUFSZ], mdefnambuf[BUFSZ];
4357 
4358             /* make a special x_monnam() call that never omits
4359                the saddle, and save it for later messages */
4360             Strcpy(mdefnambuf,
4361                    x_monnam(mdef, ARTICLE_THE, (char *) 0, 0, FALSE));
4362 
4363             if (u.usteed == mdef && obj == which_armor(mdef, W_SADDLE))
4364                 /* "You can no longer ride <steed>." */
4365                 dismount_steed(DISMOUNT_POLY);
4366             extract_from_minvent(mdef, obj, TRUE, FALSE);
4367             /* add_to_minv() might free 'obj' [if it merges] */
4368             if (g.vis)
4369                 Strcpy(onambuf, doname(obj));
4370             (void) add_to_minv(magr, obj);
4371             Strcpy(buf, Monnam(magr));
4372             if (g.vis && canseemon(mdef)) {
4373                 pline("%s steals %s from %s!", buf, onambuf, mdefnambuf);
4374             }
4375             possibly_unwield(mdef, FALSE);
4376             mdef->mstrategy &= ~STRAT_WAITFORU;
4377             mselftouch(mdef, (const char *) 0, FALSE);
4378             if (DEADMONSTER(mdef)) {
4379                 mhm->hitflags = (MM_DEF_DIED | (grow_up(magr, mdef) ? 0 : MM_AGR_DIED));
4380                 mhm->done = TRUE;
4381                 return;
4382             }
4383             if (pa->mlet == S_NYMPH && !tele_restrict(magr)) {
4384                 boolean couldspot = canspotmon(magr);
4385 
4386                 (void) rloc(magr, TRUE);
4387                 if (g.vis && couldspot && !canspotmon(magr))
4388                     pline("%s suddenly disappears!", buf);
4389             }
4390         }
4391         mhm->damage = 0;
4392     }
4393 }
4394 
4395 void
mhitm_ad_ssex(struct monst * magr,struct attack * mattk,struct monst * mdef,struct mhitm_data * mhm)4396 mhitm_ad_ssex(struct monst *magr, struct attack *mattk, struct monst *mdef,
4397               struct mhitm_data *mhm)
4398 {
4399     if (magr == &g.youmonst) {
4400         /* uhitm */
4401         mhitm_ad_sedu(magr, mattk, mdef, mhm);
4402         if (mhm->done)
4403             return;
4404     } else if (mdef == &g.youmonst) {
4405         /* mhitu */
4406         if (SYSOPT_SEDUCE) {
4407             if (could_seduce(magr, mdef, mattk) == 1 && !magr->mcan) {
4408                 mintroduce(magr);
4409                 if (doseduce(magr)) {
4410                     mhm->hitflags = MM_AGR_DONE;
4411                     mhm->done = TRUE;
4412                     return;
4413                 }
4414             }
4415             return;
4416         }
4417         mhitm_ad_sedu(magr, mattk, mdef, mhm);
4418         if (mhm->done)
4419             return;
4420     } else {
4421         /* mhitm */
4422         mhitm_ad_sedu(magr, mattk, mdef, mhm);
4423         if (mhm->done)
4424             return;
4425     }
4426 }
4427 
4428 void
mhitm_adtyping(struct monst * magr,struct attack * mattk,struct monst * mdef,struct mhitm_data * mhm)4429 mhitm_adtyping(struct monst *magr, struct attack *mattk, struct monst *mdef,
4430                struct mhitm_data *mhm)
4431 {
4432     switch (mattk->adtyp) {
4433     case AD_STUN: mhitm_ad_stun(magr, mattk, mdef, mhm); break;
4434     case AD_LEGS: mhitm_ad_legs(magr, mattk, mdef, mhm); break;
4435     case AD_WERE: mhitm_ad_were(magr, mattk, mdef, mhm); break;
4436     case AD_HEAL: mhitm_ad_heal(magr, mattk, mdef, mhm); break;
4437     case AD_PHYS: mhitm_ad_phys(magr, mattk, mdef, mhm); break;
4438     case AD_FIRE: mhitm_ad_fire(magr, mattk, mdef, mhm); break;
4439     case AD_COLD: mhitm_ad_cold(magr, mattk, mdef, mhm); break;
4440     case AD_ELEC: mhitm_ad_elec(magr, mattk, mdef, mhm); break;
4441     case AD_ACID: mhitm_ad_acid(magr, mattk, mdef, mhm); break;
4442     case AD_STON: mhitm_ad_ston(magr, mattk, mdef, mhm); break;
4443     case AD_SSEX: mhitm_ad_ssex(magr, mattk, mdef, mhm); break;
4444     case AD_SITM:
4445     case AD_SEDU: mhitm_ad_sedu(magr, mattk, mdef, mhm); break;
4446     case AD_SGLD: mhitm_ad_sgld(magr, mattk, mdef, mhm); break;
4447     case AD_TLPT: mhitm_ad_tlpt(magr, mattk, mdef, mhm); break;
4448     case AD_BLND: mhitm_ad_blnd(magr, mattk, mdef, mhm); break;
4449     case AD_CURS: mhitm_ad_curs(magr, mattk, mdef, mhm); break;
4450     case AD_DRLI: mhitm_ad_drli(magr, mattk, mdef, mhm); break;
4451     case AD_RUST: mhitm_ad_rust(magr, mattk, mdef, mhm); break;
4452     case AD_CORR: mhitm_ad_corr(magr, mattk, mdef, mhm); break;
4453     case AD_DCAY: mhitm_ad_dcay(magr, mattk, mdef, mhm); break;
4454     case AD_DREN: mhitm_ad_dren(magr, mattk, mdef, mhm); break;
4455     case AD_DRST:
4456     case AD_DRDX:
4457     case AD_DRCO: mhitm_ad_drst(magr, mattk, mdef, mhm); break;
4458     case AD_DRIN: mhitm_ad_drin(magr, mattk, mdef, mhm); break;
4459     case AD_STCK: mhitm_ad_stck(magr, mattk, mdef, mhm); break;
4460     case AD_WRAP: mhitm_ad_wrap(magr, mattk, mdef, mhm); break;
4461     case AD_PLYS: mhitm_ad_plys(magr, mattk, mdef, mhm); break;
4462     case AD_SLEE: mhitm_ad_slee(magr, mattk, mdef, mhm); break;
4463     case AD_SLIM: mhitm_ad_slim(magr, mattk, mdef, mhm); break;
4464     case AD_ENCH: mhitm_ad_ench(magr, mattk, mdef, mhm); break;
4465     case AD_SLOW: mhitm_ad_slow(magr, mattk, mdef, mhm); break;
4466     case AD_CONF: mhitm_ad_conf(magr, mattk, mdef, mhm); break;
4467     case AD_POLY: mhitm_ad_poly(magr, mattk, mdef, mhm); break;
4468     case AD_PITS: mhitm_ad_pits(magr, mattk, mdef, mhm); break;
4469     case AD_WTHR: mhitm_ad_wthr(magr, mattk, mdef, mhm); break;
4470     case AD_DISE: mhitm_ad_dise(magr, mattk, mdef, mhm); break;
4471     case AD_SAMU: mhitm_ad_samu(magr, mattk, mdef, mhm); break;
4472     case AD_DETH: mhitm_ad_deth(magr, mattk, mdef, mhm); break;
4473     case AD_PEST: mhitm_ad_pest(magr, mattk, mdef, mhm); break;
4474     case AD_FAMN: mhitm_ad_famn(magr, mattk, mdef, mhm); break;
4475     case AD_DGST: mhitm_ad_dgst(magr, mattk, mdef, mhm); break;
4476     case AD_HALU: mhitm_ad_halu(magr, mattk, mdef, mhm); break;
4477     default:
4478         mhm->damage = 0;
4479     }
4480 }
4481 
4482 int
damageum(struct monst * mdef,struct attack * mattk,int specialdmg)4483 damageum(
4484     struct monst *mdef,   /* target */
4485     struct attack *mattk, /* hero's attack */
4486     int specialdmg) /* blessed and/or silver bonus against various things */
4487 {
4488     struct mhitm_data mhm;
4489 
4490     mhm.damage = d((int) mattk->damn, (int) mattk->damd);
4491     mhm.hitflags = MM_MISS;
4492     mhm.permdmg = 0;
4493     mhm.specialdmg = specialdmg;
4494     mhm.done = FALSE;
4495 
4496     if (is_demon(g.youmonst.data) && !rn2(13) && !uwep
4497         && u.umonnum != PM_AMOROUS_DEMON && u.umonnum != PM_BALROG) {
4498         demonpet();
4499         return MM_MISS;
4500     }
4501 
4502     mhitm_adtyping(&g.youmonst, mattk, mdef, &mhm);
4503     if (mhm.done)
4504         return mhm.hitflags;
4505 
4506     mdef->mstrategy &= ~STRAT_WAITFORU; /* in case player is very fast */
4507     mdef->mhp -= mhm.damage;
4508     if (DEADMONSTER(mdef)) {
4509         /* troll killed by Trollsbane won't auto-revive; FIXME? same when
4510            Trollsbane is wielded as primary and two-weaponing kills with
4511            secondary, which matches monster vs monster behavior but is
4512            different from the non-poly'd hero vs monster behavior */
4513         if (mattk->aatyp == AT_WEAP || mattk->aatyp == AT_CLAW)
4514             g.mkcorpstat_norevive = troll_baned(mdef, uwep) ? TRUE : FALSE;
4515         /* (DEADMONSTER(mdef) and !mhm.damage => already killed) */
4516         if (mdef->mtame && !cansee(mdef->mx, mdef->my)) {
4517             You_feel("embarrassed for a moment.");
4518             if (mhm.damage)
4519                 xkilled(mdef, XKILL_NOMSG);
4520         } else if (!flags.verbose) {
4521             You("destroy it!");
4522             if (mhm.damage)
4523                 xkilled(mdef, XKILL_NOMSG);
4524         } else if (mhm.damage) {
4525             killed(mdef); /* regular "you kill <mdef>" message */
4526         }
4527         g.mkcorpstat_norevive = FALSE;
4528         return MM_DEF_DIED;
4529     }
4530     return MM_HIT;
4531 }
4532 
4533 /* Hero, as a monster which is capable of an exploding attack mattk, is
4534  * exploding at a target monster mdef, or just exploding at nothing (e.g. with
4535  * forcefight) if mdef is null.
4536  */
4537 int
explum(struct monst * mdef,struct attack * mattk)4538 explum(struct monst *mdef, struct attack *mattk)
4539 {
4540     register int tmp = d((int) mattk->damn, (int) mattk->damd);
4541 
4542     switch (mattk->adtyp) {
4543     case AD_BLND:
4544         if (mdef && !resists_blnd(mdef)) {
4545             pline("%s is blinded by your flash of light!", Monnam(mdef));
4546             mdef->mblinded = min((int) mdef->mblinded + tmp, 127);
4547             mdef->mcansee = 0;
4548         }
4549         break;
4550     case AD_HALU:
4551         if (mdef && haseyes(mdef->data) && mdef->mcansee) {
4552             pline("%s is affected by your flash of light!", Monnam(mdef));
4553             mdef->mconf = 1;
4554         }
4555         break;
4556     case AD_COLD:
4557     case AD_FIRE:
4558     case AD_ELEC:
4559         /* See comment in mon_explodes() and in zap.c for an explanation of this
4560          * math.  Here, the player is causing the explosion, so it should be in
4561          * the +20 to +29 range instead of negative. */
4562         explode(u.ux, u.uy, (mattk->adtyp - 1) + 20, tmp, MON_EXPLODE,
4563                 adtyp_to_expltype(mattk->adtyp));
4564         if (mdef && DEADMONSTER(mdef)) {
4565             /* Other monsters may have died too, but return 2 if the actual
4566              * target died. */
4567             return MM_DEF_DIED;
4568         }
4569         break;
4570     default:
4571         break;
4572     }
4573     wake_nearto(u.ux, u.uy, 7 * 7); /* same radius as exploding monster */
4574     return MM_HIT;
4575 }
4576 
4577 static void
start_engulf(struct monst * mdef)4578 start_engulf(struct monst *mdef)
4579 {
4580     if (!Invisible) {
4581         map_location(u.ux, u.uy, TRUE);
4582         tmp_at(DISP_ALWAYS, mon_to_glyph(&g.youmonst, rn2_on_display_rng));
4583         tmp_at(mdef->mx, mdef->my);
4584     }
4585     You("engulf %s!", mon_nam(mdef));
4586     delay_output();
4587     delay_output();
4588 }
4589 
4590 static void
end_engulf(void)4591 end_engulf(void)
4592 {
4593     if (!Invisible) {
4594         tmp_at(DISP_END, 0);
4595         newsym(u.ux, u.uy);
4596     }
4597 }
4598 
4599 static int
gulpum(struct monst * mdef,struct attack * mattk)4600 gulpum(struct monst *mdef, struct attack *mattk)
4601 {
4602 #ifdef LINT /* static char msgbuf[BUFSZ]; */
4603     char msgbuf[BUFSZ];
4604 #else
4605     static char msgbuf[BUFSZ]; /* for g.nomovemsg */
4606 #endif
4607     register int tmp;
4608     register int dam = d((int) mattk->damn, (int) mattk->damd);
4609     boolean fatal_gulp;
4610     struct obj *otmp;
4611     struct permonst *pd = mdef->data;
4612 
4613     /* Not totally the same as for real monsters.  Specifically, these
4614      * don't take multiple moves.  (It's just too hard, for too little
4615      * result, to program monsters which attack from inside you, which
4616      * would be necessary if done accurately.)  Instead, we arbitrarily
4617      * kill the monster immediately for AD_DGST and we regurgitate them
4618      * after exactly 1 round of attack otherwise.  -KAA
4619      */
4620 
4621     if (!engulf_target(&g.youmonst, mdef))
4622         return MM_MISS;
4623 
4624     if (u.uhunger < 1500 && !u.uswallow) {
4625         for (otmp = mdef->minvent; otmp; otmp = otmp->nobj)
4626             (void) snuff_lit(otmp);
4627 
4628         /* force vampire in bat, cloud, or wolf form to revert back to
4629            vampire form now instead of dealing with that when it dies */
4630         if (is_vampshifter(mdef)
4631             && newcham(mdef, &mons[mdef->cham], FALSE, FALSE)) {
4632             You("engulf it, then expel it.");
4633             if (canspotmon(mdef))
4634                 pline("It turns into %s.", a_monnam(mdef));
4635             else
4636                 map_invisible(mdef->mx, mdef->my);
4637             return MM_HIT;
4638         }
4639 
4640         /* engulfing a cockatrice or digesting a Rider or Medusa */
4641         fatal_gulp = (touch_petrifies(pd) && !Stone_resistance)
4642                      || (mattk->adtyp == AD_DGST
4643                          && (is_rider(pd) || (pd == &mons[PM_MEDUSA]
4644                                               && !Stone_resistance)));
4645 
4646         if ((mattk->adtyp == AD_DGST && !Slow_digestion) || fatal_gulp)
4647             eating_conducts(pd);
4648 
4649         if (fatal_gulp && !is_rider(pd)) { /* petrification */
4650             char kbuf[BUFSZ];
4651             const char *mnam = pmname(pd, Mgender(mdef));
4652 
4653             if (!type_is_pname(pd))
4654                 mnam = an(mnam);
4655             You("englut %s.", mon_nam(mdef));
4656             Sprintf(kbuf, "swallowing %s whole", mnam);
4657             instapetrify(kbuf);
4658         } else {
4659             start_engulf(mdef);
4660             switch (mattk->adtyp) {
4661             case AD_DGST:
4662                 /* eating a Rider or its corpse is fatal */
4663                 if (is_rider(pd)) {
4664                     pline("Unfortunately, digesting any of it is fatal.");
4665                     end_engulf();
4666                     Sprintf(g.killer.name, "unwisely tried to eat %s",
4667                             pmname(pd, Mgender(mdef)));
4668                     g.killer.format = NO_KILLER_PREFIX;
4669                     done(DIED);
4670                     return MM_MISS; /* lifesaved */
4671                 }
4672 
4673                 if (Slow_digestion) {
4674                     dam = 0;
4675                     break;
4676                 }
4677 
4678                 /* Use up amulet of life saving */
4679                 if ((otmp = mlifesaver(mdef)) != 0)
4680                     m_useup(mdef, otmp);
4681 
4682                 newuhs(FALSE);
4683                 /* start_engulf() issues "you engulf <mdef>" above; this
4684                    used to specify XKILL_NOMSG but we need "you kill <mdef>"
4685                    in case we're also going to get "welcome to level N+1";
4686                    "you totally digest <mdef>" will be coming soon (after
4687                    several turns) but the level-gain message seems out of
4688                    order if the kill message is left implicit */
4689                 xkilled(mdef, XKILL_GIVEMSG | XKILL_NOCORPSE);
4690                 if (!DEADMONSTER(mdef)) { /* monster lifesaved */
4691                     You("hurriedly regurgitate the sizzling in your %s.",
4692                         body_part(STOMACH));
4693                 } else {
4694                     tmp = 1 + (pd->cwt >> 8);
4695                     if (corpse_chance(mdef, &g.youmonst, TRUE)
4696                         && !(g.mvitals[monsndx(pd)].mvflags & G_NOCORPSE)) {
4697                         /* nutrition only if there can be a corpse */
4698                         u.uhunger += (pd->cnutrit + 1) / 2;
4699                     } else
4700                         tmp = 0;
4701                     Sprintf(msgbuf, "You totally digest %s.", mon_nam(mdef));
4702                     if (tmp != 0) {
4703                         /* setting afternmv = end_engulf is tempting,
4704                          * but will cause problems if the player is
4705                          * attacked (which uses his real location) or
4706                          * if his See_invisible wears off
4707                          */
4708                         You("digest %s.", mon_nam(mdef));
4709                         if (Slow_digestion)
4710                             tmp *= 2;
4711                         nomul(-tmp);
4712                         g.multi_reason = "digesting something";
4713                         g.nomovemsg = msgbuf;
4714                     } else
4715                         pline1(msgbuf);
4716                     if (pd == &mons[PM_GREEN_SLIME]) {
4717                         Sprintf(msgbuf, "%s isn't sitting well with you.",
4718                                 The(pmname(pd, Mgender(mdef))));
4719                         if (!Unchanging) {
4720                             make_slimed(5L, (char *) 0);
4721                         }
4722                     } else
4723                         exercise(A_CON, TRUE);
4724                 }
4725                 end_engulf();
4726                 return MM_DEF_DIED;
4727             case AD_PHYS:
4728                 if (g.youmonst.data == &mons[PM_FOG_CLOUD]) {
4729                     pline("%s is laden with your moisture.", Monnam(mdef));
4730                     if (amphibious(pd) && !flaming(pd)) {
4731                         dam = 0;
4732                         pline("%s seems unharmed.", Monnam(mdef));
4733                     }
4734                 } else
4735                     pline("%s is pummeled with your debris!", Monnam(mdef));
4736                 break;
4737             case AD_ACID:
4738                 pline("%s is covered with your goo!", Monnam(mdef));
4739                 if (resists_acid(mdef)) {
4740                     pline("It seems harmless to %s.", mon_nam(mdef));
4741                     dam = 0;
4742                 }
4743                 break;
4744             case AD_BLND:
4745                 if (can_blnd(&g.youmonst, mdef, mattk->aatyp,
4746                              (struct obj *) 0)) {
4747                     if (mdef->mcansee)
4748                         pline("%s can't see in there!", Monnam(mdef));
4749                     mdef->mcansee = 0;
4750                     dam += mdef->mblinded;
4751                     if (dam > 127)
4752                         dam = 127;
4753                     mdef->mblinded = dam;
4754                 }
4755                 dam = 0;
4756                 break;
4757             case AD_ELEC:
4758                 if (rn2(2)) {
4759                     pline_The("air around %s crackles with electricity.",
4760                               mon_nam(mdef));
4761                     if (resists_elec(mdef)) {
4762                         pline("%s seems unhurt.", Monnam(mdef));
4763                         dam = 0;
4764                     }
4765                     golemeffects(mdef, (int) mattk->adtyp, dam);
4766                 } else
4767                     dam = 0;
4768                 break;
4769             case AD_COLD:
4770                 if (rn2(2)) {
4771                     if (resists_cold(mdef)) {
4772                         pline("%s seems mildly chilly.", Monnam(mdef));
4773                         dam = 0;
4774                     } else
4775                         pline("%s is freezing to death!", Monnam(mdef));
4776                     golemeffects(mdef, (int) mattk->adtyp, dam);
4777                 } else
4778                     dam = 0;
4779                 break;
4780             case AD_FIRE:
4781                 if (rn2(2)) {
4782                     if (resists_fire(mdef)) {
4783                         pline("%s seems mildly hot.", Monnam(mdef));
4784                         dam = 0;
4785                     } else
4786                         pline("%s is burning to a crisp!", Monnam(mdef));
4787                     golemeffects(mdef, (int) mattk->adtyp, dam);
4788                 } else
4789                     dam = 0;
4790                 break;
4791             case AD_DREN:
4792                 if (!rn2(4))
4793                     xdrainenergym(mdef, TRUE);
4794                 dam = 0;
4795                 break;
4796             }
4797             end_engulf();
4798             mdef->mhp -= dam;
4799             if (DEADMONSTER(mdef)) {
4800                 killed(mdef);
4801                 if (DEADMONSTER(mdef)) /* not lifesaved */
4802                     return MM_DEF_DIED;
4803             }
4804             You("%s %s!", is_animal(g.youmonst.data) ? "regurgitate" : "expel",
4805                 mon_nam(mdef));
4806             if (Slow_digestion || is_animal(g.youmonst.data)) {
4807                 pline("Obviously, you didn't like %s taste.",
4808                       s_suffix(mon_nam(mdef)));
4809             }
4810         }
4811     }
4812     return MM_MISS;
4813 }
4814 
4815 void
missum(struct monst * mdef,struct attack * mattk,boolean wouldhavehit)4816 missum(struct monst *mdef, struct attack *mattk, boolean wouldhavehit)
4817 {
4818     if (wouldhavehit) /* monk is missing due to penalty for wearing suit */
4819         Your("armor is rather cumbersome...");
4820 
4821     if (could_seduce(&g.youmonst, mdef, mattk))
4822         You("pretend to be friendly to %s.", mon_nam(mdef));
4823     else if (canspotmon(mdef) && flags.verbose)
4824         You("miss %s.", mon_nam(mdef));
4825     else
4826         You("miss it.");
4827     if (!mdef->msleeping && mdef->mcanmove)
4828         wakeup(mdef, TRUE, TRUE);
4829 }
4830 
4831 /* attack monster as a monster; returns True if mon survives */
4832 static boolean
hmonas(struct monst * mon)4833 hmonas(struct monst *mon)
4834 {
4835     struct attack *mattk, alt_attk;
4836     struct obj *weapon, **originalweapon;
4837     boolean altwep = FALSE, weapon_used = FALSE;
4838     int i, tmp, armorpenalty, sum[NATTK], nsum = MM_MISS,
4839         dhit = 0, attknum = 0;
4840     int dieroll;
4841 
4842     g.skipdrin = FALSE; /* [see mattackm(mhitm.c)] */
4843 
4844     for (i = 0; i < NATTK; i++) {
4845         sum[i] = MM_MISS;
4846         mattk = getmattk(&g.youmonst, mon, i, sum, &alt_attk);
4847         if (g.skipdrin && mattk->aatyp == AT_TENT && mattk->adtyp == AD_DRIN)
4848             continue;
4849         weapon = 0;
4850         switch (mattk->aatyp) {
4851         case AT_WEAP:
4852             /* if (!uwep) goto weaponless; */
4853  use_weapon:
4854             /* if we've already hit with a two-handed weapon, we don't
4855                get to make another weapon attack (note:  monsters who
4856                use weapons do not have this restriction, but they also
4857                never have the opportunity to use two weapons) */
4858             if (weapon_used && (sum[i - 1] > MM_MISS)
4859                 && uwep && bimanual(uwep))
4860                 continue;
4861             /* Certain monsters don't use weapons when encountered as enemies,
4862              * but players who polymorph into them have hands or claws and
4863              * thus should be able to use weapons.  This shouldn't prohibit
4864              * the use of most special abilities, either.
4865              * If monster has multiple claw attacks, only one can use weapon.
4866              */
4867             weapon_used = TRUE;
4868             /* Potential problem: if the monster gets multiple weapon attacks,
4869              * we currently allow the player to get each of these as a weapon
4870              * attack.  Is this really desirable?
4871              */
4872             /* approximate two-weapon mode; known_hitum() -> hmon() -> &c
4873                might destroy the weapon argument, but it might also already
4874                be Null, and we want to track that for passive() */
4875             originalweapon = (altwep && uswapwep) ? &uswapwep : &uwep;
4876             if (uswapwep /* set up 'altwep' flag for next iteration */
4877                 /* only consider seconary when wielding one-handed primary */
4878                 && uwep && (uwep->oclass == WEAPON_CLASS || is_weptool(uwep))
4879                 && !bimanual(uwep)
4880                 /* only switch if not wearing shield and not at artifact;
4881                    shield limitation is iffy since still get extra swings
4882                    if polyform has them, but it matches twoweap behavior;
4883                    twoweap also only allows primary to be an artifact, so
4884                    if alternate weapon is one, don't use it */
4885                 && !uarms && !uswapwep->oartifact
4886                 /* only switch to uswapwep if it's a weapon */
4887                 && (uswapwep->oclass == WEAPON_CLASS || is_weptool(uswapwep))
4888                 /* only switch if uswapwep is not bow, arrows, or darts */
4889                 && !(is_launcher(uswapwep) || is_ammo(uswapwep)
4890                      || is_missile(uswapwep)) /* dart, shuriken, boomerang */
4891                 /* and not two-handed and not incapable of being wielded */
4892                 && !bimanual(uswapwep)
4893                 && !(Hate_material(uswapwep->material)))
4894                 altwep = !altwep; /* toggle for next attack */
4895             weapon = *originalweapon;
4896             if (!weapon) /* no need to go beyond no-gloves to rings; not ...*/
4897                 originalweapon = &uarmg; /*... subject to erosion damage */
4898 
4899             tmp = find_roll_to_hit(mon, AT_WEAP, weapon, &attknum,
4900                                    &armorpenalty);
4901             dieroll = rnd(20);
4902             dhit = (tmp > dieroll || u.uswallow);
4903             /* caller must set g.bhitpos */
4904             if (!known_hitum(mon, weapon, &dhit, tmp,
4905                              armorpenalty, mattk, dieroll)) {
4906                 /* enemy dead, before any special abilities used */
4907                 sum[i] = MM_DEF_DIED;
4908                 break;
4909             } else
4910                 sum[i] = dhit ? MM_HIT : MM_MISS;
4911             /* originalweapon points to an equipment slot which might
4912                now be empty if the weapon was destroyed during the hit;
4913                passive(,weapon,...) won't call passive_obj() in that case */
4914             weapon = *originalweapon; /* might receive passive erosion */
4915             /* might be a worm that gets cut in half; if so, early return */
4916             if (m_at(u.ux + u.dx, u.uy + u.dy) != mon) {
4917                 i = NATTK; /* skip additional attacks */
4918                 /* proceed with uswapwep->cursed check, then exit loop */
4919                 goto passivedone;
4920             }
4921             /* Do not print "You hit" message; known_hitum already did it. */
4922             if (dhit && mattk->adtyp != AD_SPEL && mattk->adtyp != AD_PHYS)
4923                 sum[i] = damageum(mon, mattk, 0);
4924             break;
4925         case AT_CLAW:
4926             if (uwep && !cantwield(g.youmonst.data) && !weapon_used)
4927                 goto use_weapon;
4928             /*FALLTHRU*/
4929         case AT_TUCH:
4930             if (uwep && g.youmonst.data->mlet == S_LICH && !weapon_used)
4931                 goto use_weapon;
4932             /*FALLTHRU*/
4933         case AT_KICK:
4934         case AT_BITE:
4935         case AT_STNG:
4936         case AT_BUTT:
4937         case AT_TENT:
4938         /*weaponless:*/
4939             tmp = find_roll_to_hit(mon, mattk->aatyp, (struct obj *) 0,
4940                                    &attknum, &armorpenalty);
4941             dieroll = rnd(20);
4942             dhit = (tmp > dieroll || u.uswallow);
4943             if (dhit) {
4944                 int compat, specialdmg;
4945                 struct obj * hated_obj = NULL;
4946                 const char *verb = 0; /* verb or body part */
4947 
4948                 if (!u.uswallow
4949                     && (compat = could_seduce(&g.youmonst, mon, mattk)) != 0) {
4950                     You("%s %s %s.",
4951                         (mon->mcansee && haseyes(mon->data)) ? "smile at"
4952                                                              : "talk to",
4953                         mon_nam(mon),
4954                         (compat == 2) ? "engagingly" : "seductively");
4955                     /* doesn't anger it; no wakeup() */
4956                     sum[i] = damageum(mon, mattk, 0);
4957                     break;
4958                 }
4959                 wakeup(mon, TRUE, TRUE);
4960                 /* There used to be a bunch of code here to ensure that W_RINGL
4961                  * and W_RINGR slots got chosen on alternating claw/touch
4962                  * attacks. There's no such logic for monsters, and if you know
4963                  * that the ring on one of your hands will be especially
4964                  * effective, you'll probably keep hitting with that hand. So
4965                  * just do the default and take whatever the most damaging piece
4966                  * of gear is. */
4967                 specialdmg = special_dmgval(&g.youmonst, mon,
4968                                             attack_contact_slots(&g.youmonst,
4969                                                                  mattk->aatyp),
4970                                             &hated_obj);
4971                 switch (mattk->aatyp) {
4972                 case AT_CLAW:
4973                     /* verb=="claws" may be overridden below */
4974                     verb = "claws";
4975                     break;
4976                 case AT_TUCH:
4977                     verb = "touch";
4978                     break;
4979                 case AT_TENT:
4980                     /* assumes mind flayer's tentacles-on-head rather
4981                        than sea monster's tentacle-as-arm */
4982                     verb = "tentacles";
4983                     break;
4984                 case AT_KICK:
4985                     verb = "kick";
4986                     break;
4987                 case AT_BUTT:
4988                     verb = "head butt"; /* mbodypart(mon,HEAD)=="head" */
4989                     break;
4990                 case AT_BITE:
4991                     verb = "bite";
4992                     break;
4993                 case AT_STNG:
4994                     verb = "sting";
4995                     break;
4996                 default:
4997                     verb = "hit";
4998                     break;
4999                 }
5000                 if (noncorporeal(mon->data) && !specialdmg) {
5001                     if (!strcmp(verb, "hit")
5002                         || (mattk->aatyp == AT_CLAW && humanoid(mon->data)))
5003                         verb = "attack";
5004                     Your("%s %s harmlessly through %s.",
5005                          verb, vtense(verb, "pass"), mon_nam(mon));
5006                 } else {
5007                     if (mattk->aatyp == AT_TENT) {
5008                         Your("tentacles suck %s.", mon_nam(mon));
5009                     } else {
5010                         if (mattk->aatyp == AT_CLAW)
5011                             verb = "hit"; /* not "claws" */
5012                         You("%s %s.", verb, mon_nam(mon));
5013                         if (hated_obj && flags.verbose)
5014                             searmsg(&g.youmonst, mon, hated_obj, FALSE);
5015                     }
5016                     sum[i] = damageum(mon, mattk, specialdmg);
5017                 }
5018             } else { /* !dhit */
5019                 missum(mon, mattk, (tmp + armorpenalty > dieroll));
5020             }
5021             break;
5022 
5023         case AT_HUGS: {
5024             int specialdmg;
5025             struct obj* hated_obj = NULL;
5026             boolean byhand = hug_throttles(&mons[u.umonnum]), /* rope golem */
5027                     unconcerned = (byhand && !can_be_strangled(mon));
5028 
5029             if (sticks(mon->data) || u.uswallow || g.notonhead
5030                 || (byhand && (uwep || !has_head(mon->data)))) {
5031                 /* can't hold a holder due to subsequent ambiguity over
5032                    who is holding whom; can't hug engulfer from inside;
5033                    can't hug a worm tail (would immobilize entire worm!);
5034                    byhand: can't choke something that lacks a head;
5035                    not allowed to make a choking hug if wielding a weapon
5036                    (but might have grabbed w/o weapon, then wielded one,
5037                    and may even be attacking a different monster now) */
5038                 if (byhand && uwep && u.ustuck
5039                     && !(sticks(u.ustuck->data) || u.uswallow))
5040                     uunstick();
5041                 continue; /* not 'break'; bypass passive counter-attack */
5042             }
5043             /* automatic if prev two attacks succeed, or if
5044                already grabbed in a previous attack */
5045             dhit = 1;
5046             wakeup(mon, TRUE, TRUE);
5047             specialdmg = special_dmgval(&g.youmonst, mon,
5048                                         attack_contact_slots(&g.youmonst,
5049                                                              AT_HUGS),
5050                                         &hated_obj);
5051             if (unconcerned) {
5052                 /* strangling something which can't be strangled */
5053                 if (mattk != &alt_attk) {
5054                     alt_attk = *mattk;
5055                     mattk = &alt_attk;
5056                 }
5057                 /* change damage to 1d1; not strangling but still
5058                    doing [minimal] physical damage to victim's body */
5059                 mattk->damn = mattk->damd = 1;
5060                 /* don't give 'unconcerned' feedback if there is extra damage
5061                    or if it is nearly destroyed or if creature doesn't have
5062                    the mental ability to be concerned in the first place */
5063                 if (specialdmg || mindless(mon->data)
5064                     || mon->mhp <= 1 + max(u.udaminc, 1))
5065                     unconcerned = FALSE;
5066             }
5067             if (noncorporeal(mon->data)) {
5068                 const char *verb = byhand ? "grasp" : "hug";
5069 
5070                 /* hugging a shade; successful if blessed outermost armor
5071                    for normal hug, or blessed gloves or silver ring(s) for
5072                    choking hug; deals damage but never grabs hold */
5073                 if (specialdmg) {
5074                     You("%s %s%s", verb, mon_nam(mon), exclam(specialdmg));
5075                     if (hated_obj && flags.verbose)
5076                         searmsg(&g.youmonst, mon, hated_obj, FALSE);
5077                     sum[i] = damageum(mon, mattk, specialdmg);
5078                 } else {
5079                     Your("%s passes harmlessly through %s.",
5080                          verb, mon_nam(mon));
5081                 }
5082                 break;
5083             }
5084             /* hug attack against ordinary foe */
5085             if (mon == u.ustuck) {
5086                 pline("%s is being %s%s.", Monnam(mon),
5087                       byhand ? "throttled" : "crushed",
5088                       /* extra feedback for non-breather being choked */
5089                       unconcerned ? " but doesn't seem concerned" : "");
5090                 if (hated_obj && flags.verbose)
5091                     searmsg(&g.youmonst, mon, hated_obj, FALSE);
5092                 sum[i] = damageum(mon, mattk, specialdmg);
5093             } else if (i >= 2 && (sum[i - 1] > MM_MISS)
5094                        && (sum[i - 2] > MM_MISS)) {
5095                 /* in case we're hugging a new target while already
5096                    holding something else; yields feedback
5097                    "<u.ustuck> is no longer in your clutches" */
5098                 if (u.ustuck && u.ustuck != mon)
5099                     uunstick();
5100                 You("grab %s!", mon_nam(mon));
5101                 set_ustuck(mon);
5102                 if (hated_obj && flags.verbose)
5103                     searmsg(&g.youmonst, mon, hated_obj, FALSE);
5104                 sum[i] = damageum(mon, mattk, specialdmg);
5105             }
5106             break; /* AT_HUGS */
5107         }
5108 
5109         case AT_EXPL: /* automatic hit if next to */
5110             dhit = -1;
5111             wakeup(mon, TRUE, TRUE);
5112             You("explode!");
5113             sum[i] = explum(mon, mattk);
5114             break;
5115 
5116         case AT_ENGL:
5117             tmp = find_roll_to_hit(mon, mattk->aatyp, (struct obj *) 0,
5118                                    &attknum, &armorpenalty);
5119             if ((dhit = (tmp > rnd(20 + i)))) {
5120                 wakeup(mon, TRUE, TRUE);
5121                 if (noncorporeal(mon->data)) {
5122                     Your("attempt to surround %s is harmless.", mon_nam(mon));
5123                 } else {
5124                     sum[i] = gulpum(mon, mattk);
5125                     if (sum[i] == MM_DEF_DIED && (mon->data->mlet == S_ZOMBIE
5126                                         || mon->data->mlet == S_MUMMY)
5127                         && rn2(5) && !Sick_resistance) {
5128                         You_feel("%ssick.", (Sick) ? "very " : "");
5129                         mdamageu(mon, rnd(8));
5130                     }
5131                 }
5132             } else {
5133                 missum(mon, mattk, FALSE);
5134             }
5135             break;
5136 
5137         case AT_MAGC:
5138             /* No check for uwep; if wielding nothing we want to
5139              * do the normal 1-2 points bare hand damage...
5140              */
5141             if ((g.youmonst.data->mlet == S_KOBOLD
5142                  || g.youmonst.data->mlet == S_ORC
5143                  || g.youmonst.data->mlet == S_GNOME) && !weapon_used)
5144                 goto use_weapon;
5145             /*FALLTHRU*/
5146 
5147         case AT_NONE:
5148         case AT_BOOM:
5149             continue;
5150         /* Not break--avoid passive attacks from enemy */
5151 
5152         case AT_BREA:
5153         case AT_SPIT:
5154         case AT_GAZE: /* all done using #monster command */
5155             dhit = 0;
5156             break;
5157 
5158         default: /* Strange... */
5159             impossible("strange attack of yours (%d)", mattk->aatyp);
5160         }
5161         if (dhit == -1) {
5162             u.mh = -1; /* dead in the current form */
5163             Sprintf(g.killer.name, "blew %sself up", uhim());
5164             g.killer.format = NO_KILLER_PREFIX;
5165             rehumanize();
5166         }
5167         if (sum[i] == MM_DEF_DIED) {
5168             /* defender dead */
5169             (void) passive(mon, weapon, 1, 0, mattk->aatyp, FALSE);
5170             nsum = MM_MISS; /* return value below used to be 'nsum > 0' */
5171         } else {
5172             (void) passive(mon, weapon, (sum[i] != MM_MISS), 1,
5173                            mattk->aatyp, FALSE);
5174             nsum |= sum[i];
5175         }
5176 
5177         /* don't use sum[i] beyond this point;
5178            'i' will be out of bounds if we get here via 'goto' */
5179  passivedone:
5180         /* when using dual weapons, cursed secondary weapon doesn't weld,
5181            it gets dropped; do the same when multiple AT_WEAP attacks
5182            simulate twoweap */
5183         if (uswapwep && weapon == uswapwep && weapon->cursed) {
5184             drop_uswapwep();
5185             break; /* don't proceed with additional attacks */
5186         }
5187         /* stop attacking if defender has died;
5188            needed to defer this until after uswapwep->cursed check */
5189         if (DEADMONSTER(mon))
5190             break;
5191         if (!Upolyd)
5192             break; /* No extra attacks if no longer a monster */
5193         if (g.multi < 0)
5194             break; /* If paralyzed while attacking, i.e. floating eye */
5195     }
5196 
5197     g.vis = FALSE; /* reset */
5198     /* return value isn't used, but make it match hitum()'s */
5199     return !DEADMONSTER(mon);
5200 }
5201 
5202 /*      Special (passive) attacks on you by monsters done here.
5203  */
5204 int
passive(struct monst * mon,struct obj * weapon,boolean mhitb,boolean maliveb,uchar aatyp,boolean wep_was_destroyed)5205 passive(struct monst *mon,
5206         struct obj *weapon, /* uwep or uswapwep or uarmg or uarmf or Null */
5207         boolean mhitb,
5208         boolean maliveb,
5209         uchar aatyp,
5210         boolean wep_was_destroyed)
5211 {
5212     register struct permonst *ptr = mon->data;
5213     register int i, tmp;
5214     int mhit = mhitb ? MM_HIT : MM_MISS;
5215     int malive = maliveb ? MM_HIT : MM_MISS;
5216 
5217     for (i = 0;; i++) {
5218         if (i >= NATTK)
5219             return (malive | mhit); /* no passive attacks */
5220         if (ptr->mattk[i].aatyp == AT_NONE)
5221             break; /* try this one */
5222     }
5223     /* Note: tmp not always used */
5224     if (ptr->mattk[i].damn)
5225         tmp = d((int) ptr->mattk[i].damn, (int) ptr->mattk[i].damd);
5226     else if (ptr->mattk[i].damd)
5227         tmp = d((int) mon->m_lev + 1, (int) ptr->mattk[i].damd);
5228     else
5229         tmp = 0;
5230 
5231     /*  These affect you even if they just died.
5232      */
5233     switch (ptr->mattk[i].adtyp) {
5234     case AD_FIRE:
5235         if (mhitb && !mon->mcan && weapon) {
5236             if (aatyp == AT_KICK) {
5237                 if (uarmf && !rn2(6))
5238                     (void) erode_obj(uarmf, xname(uarmf), ERODE_BURN,
5239                                      EF_GREASE | EF_VERBOSE);
5240             } else if (aatyp == AT_WEAP || aatyp == AT_CLAW
5241                        || aatyp == AT_MAGC || aatyp == AT_TUCH)
5242                 passive_obj(mon, weapon, &(ptr->mattk[i]));
5243         }
5244         break;
5245     case AD_ACID:
5246         if (mhitb && rn2(2)) {
5247             if (Blind || !flags.verbose)
5248                 You("are splashed!");
5249             else
5250                 You("are splashed by %s %s!", s_suffix(mon_nam(mon)),
5251                     hliquid("acid"));
5252 
5253             if (!Acid_resistance)
5254                 mdamageu(mon, tmp);
5255             if (!rn2(30))
5256                 erode_armor(&g.youmonst, ERODE_CORRODE);
5257         }
5258         if (mhitb && weapon) {
5259             if (aatyp == AT_KICK) {
5260                 if (uarmf && !rn2(6))
5261                     (void) erode_obj(uarmf, xname(uarmf), ERODE_CORRODE,
5262                                      EF_GREASE | EF_VERBOSE);
5263             } else if (aatyp == AT_WEAP || aatyp == AT_CLAW
5264                        || aatyp == AT_MAGC || aatyp == AT_TUCH)
5265                 passive_obj(mon, weapon, &(ptr->mattk[i]));
5266         }
5267         exercise(A_STR, FALSE);
5268         break;
5269     case AD_STON:
5270         if (mhitb) { /* successful attack */
5271             long protector = attk_protection((int) aatyp);
5272 
5273             /* hero using monsters' AT_MAGC attack is hitting hand to
5274                hand rather than casting a spell */
5275             if (aatyp == AT_MAGC)
5276                 protector = W_ARMG;
5277 
5278             if (protector == 0L /* no protection */
5279                 || (protector == W_ARMG && !uarmg
5280                     && !uwep && !wep_was_destroyed)
5281                 || (protector == W_ARMF && !uarmf)
5282                 || (protector == W_ARMH && !uarmh)
5283                 || (protector == (W_ARMC | W_ARMG) && (!uarmc || !uarmg))) {
5284                 if (!Stone_resistance
5285                     && !(poly_when_stoned(g.youmonst.data)
5286                          && polymon(PM_STONE_GOLEM, POLYMON_ALL_MSGS))) {
5287                     done_in_by(mon, STONING); /* "You turn to stone..." */
5288                     return MM_DEF_DIED;
5289                 }
5290             }
5291         }
5292         break;
5293     case AD_RUST:
5294         if (mhitb && !mon->mcan && weapon) {
5295             if (aatyp == AT_KICK) {
5296                 if (uarmf)
5297                     (void) erode_obj(uarmf, xname(uarmf), ERODE_RUST,
5298                                      EF_GREASE | EF_VERBOSE);
5299             } else if (aatyp == AT_WEAP || aatyp == AT_CLAW
5300                        || aatyp == AT_MAGC || aatyp == AT_TUCH)
5301                 passive_obj(mon, weapon, &(ptr->mattk[i]));
5302         }
5303         break;
5304     case AD_CORR:
5305         if (mhitb && !mon->mcan && weapon) {
5306             if (aatyp == AT_KICK) {
5307                 if (uarmf)
5308                     (void) erode_obj(uarmf, xname(uarmf), ERODE_CORRODE,
5309                                      EF_GREASE | EF_VERBOSE);
5310             } else if (aatyp == AT_WEAP || aatyp == AT_CLAW
5311                        || aatyp == AT_MAGC || aatyp == AT_TUCH)
5312                 passive_obj(mon, weapon, &(ptr->mattk[i]));
5313         }
5314         break;
5315     case AD_MAGM:
5316         /* wrath of gods for attacking Oracle */
5317         if (Antimagic) {
5318             shieldeff(u.ux, u.uy);
5319             pline("A hail of magic missiles narrowly misses you!");
5320         } else {
5321             You("are hit by magic missiles appearing from thin air!");
5322             mdamageu(mon, tmp);
5323         }
5324         break;
5325     case AD_ENCH: /* KMH -- remove enchantment (disenchanter) */
5326         if (mhitb) {
5327             if (aatyp == AT_KICK) {
5328                 if (!weapon)
5329                     break;
5330             } else if (aatyp == AT_BITE || aatyp == AT_BUTT
5331                        || (aatyp >= AT_STNG && aatyp < AT_WEAP)) {
5332                 break; /* no object involved */
5333             }
5334             passive_obj(mon, weapon, &(ptr->mattk[i]));
5335         }
5336         break;
5337     default:
5338         break;
5339     }
5340 
5341     /*  These only affect you if they still live.
5342      */
5343     if (malive && !mon->mcan && rn2(3)) {
5344         switch (ptr->mattk[i].adtyp) {
5345         case AD_PLYS:
5346             if (ptr == &mons[PM_FLOATING_EYE]) {
5347                 if (!canseemon(mon)) {
5348                     break;
5349                 }
5350                 if (mon->mcansee) {
5351                     const char* reflectsrc = ureflectsrc();
5352                     if (reflectsrc) {
5353                         pline("%s gaze is reflected by your %s.",
5354                                   s_suffix(Monnam(mon)), reflectsrc);
5355                     } else if (Hallucination) {
5356                         /* [it's the hero who should be getting paralyzed
5357                            and isn't; this message describes the monster's
5358                            reaction rather than the hero's escape] */
5359                         pline("%s looks %s%s.", Monnam(mon),
5360                               !rn2(2) ? "" : "rather ",
5361                               !rn2(2) ? "numb" : "stupefied");
5362                     } else if (Free_action) {
5363                         You("momentarily stiffen under %s gaze!",
5364                             s_suffix(mon_nam(mon)));
5365                     } else if (rnd(30) < ACURR(A_WIS)) {
5366                         You("avert your eyes from %s gaze.",
5367                             s_suffix(mon_nam(mon)));
5368                     } else {
5369                         You("are frozen by %s gaze!", s_suffix(mon_nam(mon)));
5370                         dynamic_multi_reason(mon, "frozen", TRUE);
5371                         make_paralyzed(tmp, FALSE, (const char *) 0);
5372                     }
5373                 } else {
5374                     pline("%s cannot defend itself.",
5375                           Adjmonnam(mon, "blind"));
5376                     if (!rn2(500))
5377                         change_luck(-1);
5378                 }
5379             } else { /* gelatinous cube */
5380                 if (!Free_action) {
5381                     You("are frozen by %s!", mon_nam(mon));
5382                 }
5383                 dynamic_multi_reason(mon, "frozen", FALSE);
5384                 make_paralyzed(tmp, FALSE, (const char *) 0);
5385             }
5386             break;
5387         case AD_COLD: /* brown mold or blue jelly */
5388             if (monnear(mon, u.ux, u.uy)) {
5389                 if (Cold_resistance) {
5390                     shieldeff(u.ux, u.uy);
5391                     You_feel("a mild chill.");
5392                     ugolemeffects(AD_COLD, tmp);
5393                     break;
5394                 }
5395                 You("are suddenly very cold!");
5396                 mdamageu(mon, tmp);
5397                 /* monster gets stronger with your heat! */
5398                 mon->mhp += tmp / 2;
5399                 if (mon->mhpmax < mon->mhp)
5400                     mon->mhpmax = mon->mhp;
5401                 /* at a certain point, the monster will reproduce! */
5402                 if (mon->mhpmax > ((int) (mon->m_lev + 1) * 8))
5403                     (void) split_mon(mon, &g.youmonst);
5404             }
5405             break;
5406         case AD_STUN: /* specifically yellow mold */
5407             if (!Stunned)
5408                 make_stunned((long) tmp, TRUE);
5409             break;
5410         case AD_FIRE:
5411             if (monnear(mon, u.ux, u.uy)) {
5412                 if (Fire_resistance) {
5413                     shieldeff(u.ux, u.uy);
5414                     You_feel("mildly warm.");
5415                     ugolemeffects(AD_FIRE, tmp);
5416                     break;
5417                 }
5418                 You("are suddenly very hot!");
5419                 mdamageu(mon, tmp); /* fire damage */
5420             }
5421             break;
5422         case AD_ELEC:
5423             if (Shock_resistance) {
5424                 shieldeff(u.ux, u.uy);
5425                 You_feel("a mild tingle.");
5426                 ugolemeffects(AD_ELEC, tmp);
5427                 break;
5428             }
5429             You("are jolted with electricity!");
5430             mdamageu(mon, tmp);
5431             break;
5432         default:
5433             break;
5434         }
5435     }
5436     return (malive | mhit);
5437 }
5438 
5439 /*
5440  * Special (passive) attacks on an attacking object by monsters done here.
5441  * Assumes the attack was successful.
5442  */
5443 void
passive_obj(struct monst * mon,struct obj * obj,struct attack * mattk)5444 passive_obj(struct monst *mon,
5445             struct obj *obj,      /* null means pick uwep, uswapwep or uarmg */
5446             struct attack *mattk) /* null means we find one internally */
5447 {
5448     struct permonst *ptr = mon->data;
5449     int i;
5450 
5451     /* [this first bit is obsolete; we're not called with Null anymore] */
5452     /* if caller hasn't specified an object, use uwep, uswapwep or uarmg */
5453     if (!obj) {
5454         obj = (u.twoweap && uswapwep && !rn2(2)) ? uswapwep : uwep;
5455         if (!obj && mattk->adtyp == AD_ENCH)
5456             obj = uarmg; /* no weapon? then must be gloves */
5457         if (!obj)
5458             return; /* no object to affect */
5459     }
5460 
5461     /* if caller hasn't specified an attack, find one */
5462     if (!mattk) {
5463         for (i = 0;; i++) {
5464             if (i >= NATTK)
5465                 return; /* no passive attacks */
5466             if (ptr->mattk[i].aatyp == AT_NONE)
5467                 break; /* try this one */
5468         }
5469         mattk = &(ptr->mattk[i]);
5470     }
5471 
5472     switch (mattk->adtyp) {
5473     case AD_FIRE:
5474         if (!rn2(6) && !mon->mcan
5475             /* steam vortex: fire resist applies, fire damage doesn't */
5476             && mon->data != &mons[PM_STEAM_VORTEX]) {
5477             (void) erode_obj(obj, NULL, ERODE_BURN, EF_NONE);
5478         }
5479         break;
5480     case AD_ACID:
5481         if (!rn2(6)) {
5482             (void) erode_obj(obj, NULL, ERODE_CORRODE, EF_GREASE);
5483         }
5484         break;
5485     case AD_RUST:
5486         if (!mon->mcan) {
5487             (void) erode_obj(obj, (char *) 0, ERODE_RUST, EF_GREASE);
5488         }
5489         break;
5490     case AD_CORR:
5491         if (!mon->mcan) {
5492             (void) erode_obj(obj, (char *) 0, ERODE_CORRODE, EF_GREASE);
5493         }
5494         break;
5495     case AD_ENCH:
5496         if (!mon->mcan) {
5497             if (drain_item(obj, TRUE) && carried(obj)
5498                 && (obj->known || obj->oclass == ARMOR_CLASS)) {
5499                 pline("%s less effective.", Yobjnam2(obj, "seem"));
5500             }
5501             break;
5502         }
5503     default:
5504         break;
5505     }
5506 
5507     if (carried(obj))
5508         update_inventory();
5509 }
5510 
5511 /* An item intercepts life drainage, at the cost of itself or its own
5512  * enchantment. Currently implemented only for bone armor with a positive
5513  * enchantment. */
5514 boolean
item_catches_drain(struct monst * mdef)5515 item_catches_drain(struct monst *mdef)
5516 {
5517     int bone_armor_ct = 0;
5518     struct obj *otmp, *interceptor = NULL;
5519     otmp = (mdef == &g.youmonst) ? g.invent : mdef->minvent;
5520     for (; otmp != NULL; otmp = otmp->nobj) {
5521         if (otmp->oclass == ARMOR_CLASS && (otmp->owornmask & W_ARMOR) != 0L
5522             && otmp->material == BONE && otmp->spe > 0) {
5523             bone_armor_ct++;
5524             if (!rn2(bone_armor_ct)) {
5525                 interceptor = otmp;
5526             }
5527         }
5528     }
5529     if (interceptor) {
5530         /* use mon_moving to check for hero responsibility */
5531         drain_item(interceptor, !g.context.mon_moving);
5532         pline("%s less effective.", Yobjnam2(interceptor, "seem"));
5533         /* Drain was intercepted, so the monster "resisted" it. */
5534         return TRUE;
5535     }
5536     /* nothing eligible to intercept */
5537     return FALSE;
5538 }
5539 
5540 /* Note: caller must ascertain mtmp is mimicking... */
5541 void
stumble_onto_mimic(struct monst * mtmp)5542 stumble_onto_mimic(struct monst *mtmp)
5543 {
5544     const char *fmt = "Wait!  That's %s!", *generic = "a monster", *what = 0;
5545 
5546     if (!u.ustuck && !mtmp->mflee && dmgtype(mtmp->data, AD_STCK)
5547         /* must be adjacent; attack via polearm could be from farther away */
5548         && distu(mtmp->mx, mtmp->my) <= 2)
5549         set_ustuck(mtmp);
5550 
5551     if (Blind) {
5552         if (!Blind_telepat)
5553             what = generic; /* with default fmt */
5554         else if (M_AP_TYPE(mtmp) == M_AP_MONSTER)
5555             what = a_monnam(mtmp); /* differs from what was sensed */
5556     } else {
5557         int glyph = levl[u.ux + u.dx][u.uy + u.dy].glyph;
5558 
5559         if (glyph_is_cmap(glyph) && (glyph_to_cmap(glyph) == S_hcdoor
5560                                      || glyph_to_cmap(glyph) == S_vcdoor))
5561             fmt = "The door actually was %s!";
5562         else if (glyph_is_object(glyph) && glyph_to_obj(glyph) == GOLD_PIECE)
5563             fmt = "That gold was %s!";
5564 
5565         /* cloned Wiz starts out mimicking some other monster and
5566            might make himself invisible before being revealed */
5567         if (mtmp->minvis && !See_invisible)
5568             what = generic;
5569         else
5570             what = a_monnam(mtmp);
5571     }
5572     if (what)
5573         pline(fmt, what);
5574 
5575     wakeup(mtmp, FALSE, TRUE); /* clears mimicking */
5576     /* if hero is blind, wakeup() won't display the monster even though
5577        it's no longer concealed */
5578     if (!canspotmon(mtmp)
5579         && !glyph_is_invisible(levl[mtmp->mx][mtmp->my].glyph))
5580         map_invisible(mtmp->mx, mtmp->my);
5581 }
5582 
5583 static void
nohandglow(struct monst * mon)5584 nohandglow(struct monst *mon)
5585 {
5586     char *hands = makeplural(body_part(HAND));
5587 
5588     if (!u.umconf || mon->mconf)
5589         return;
5590     if (u.umconf == 1) {
5591         if (Blind)
5592             Your("%s stop tingling.", hands);
5593         else
5594             Your("%s stop glowing %s.", hands, hcolor(NH_RED));
5595     } else {
5596         if (Blind)
5597             pline_The("tingling in your %s lessens.", hands);
5598         else
5599             Your("%s no longer glow so brightly %s.", hands, hcolor(NH_RED));
5600     }
5601     u.umconf--;
5602 }
5603 
5604 /* returns 1 if light flash has noticeable effect on 'mtmp', 0 otherwise */
5605 int
flash_hits_mon(struct monst * mtmp,struct obj * otmp)5606 flash_hits_mon(struct monst *mtmp,
5607                struct obj *otmp) /* source of flash */
5608 {
5609     struct rm *lev;
5610     int tmp, amt, useeit, res = 0;
5611 
5612     if (g.notonhead)
5613         return 0;
5614     lev = &levl[mtmp->mx][mtmp->my];
5615     useeit = canseemon(mtmp);
5616 
5617     if (mtmp->msleeping && haseyes(mtmp->data)) {
5618         mtmp->msleeping = 0;
5619         if (useeit) {
5620             pline_The("flash awakens %s.", mon_nam(mtmp));
5621             res = 1;
5622         }
5623     } else if (mtmp->data->mlet != S_LIGHT) {
5624         if (!resists_blnd(mtmp)) {
5625             tmp = dist2(otmp->ox, otmp->oy, mtmp->mx, mtmp->my);
5626             if (useeit) {
5627                 pline("%s is blinded by the flash!", Monnam(mtmp));
5628                 res = 1;
5629             }
5630             if (mtmp->data == &mons[PM_GREMLIN]) {
5631                 /* Rule #1: Keep them out of the light. */
5632                 amt = otmp->otyp == WAN_LIGHT ? d(1 + otmp->spe, 4)
5633                                               : rn2(min(mtmp->mhp, 4));
5634                 light_hits_gremlin(mtmp, amt);
5635             }
5636             if (!DEADMONSTER(mtmp)) {
5637                 if (!g.context.mon_moving)
5638                     setmangry(mtmp, TRUE);
5639                 if (tmp < 9 && !mtmp->isshk && rn2(4))
5640                     monflee(mtmp, rn2(4) ? rnd(100) : 0, FALSE, TRUE);
5641                 mtmp->mcansee = 0;
5642                 mtmp->mblinded = (tmp < 3) ? 0 : rnd(1 + 50 / tmp);
5643             }
5644         } else if (flags.verbose && useeit) {
5645             if (lev->lit)
5646                 pline("The flash of light shines on %s.", mon_nam(mtmp));
5647             else
5648                 pline("%s is illuminated.", Monnam(mtmp));
5649             res = 2; /* 'message has been given' temporary value */
5650         }
5651     }
5652     if (res) {
5653         if (!lev->lit)
5654             display_nhwindow(WIN_MESSAGE, TRUE);
5655         res &= 1; /* change temporary 2 back to 0 */
5656     }
5657     return res;
5658 }
5659 
5660 void
light_hits_gremlin(struct monst * mon,int dmg)5661 light_hits_gremlin(struct monst *mon, int dmg)
5662 {
5663     if (canspotmon(mon)) {
5664         pline("%s %s!", Monnam(mon),
5665             (dmg > mon->mhp / 2) ? "wails in agony" : "cries out in pain");
5666     }
5667     mon->mhp -= dmg;
5668     wake_nearto(mon->mx, mon->my, 30);
5669     if (DEADMONSTER(mon)) {
5670         if (g.context.mon_moving)
5671             monkilled(mon, (char *) 0, AD_BLND);
5672         else
5673             killed(mon);
5674     } else {
5675         if (cansee(mon->mx, mon->my) && !canspotmon(mon)) {
5676             map_invisible(mon->mx, mon->my);
5677         }
5678         if (!g.context.mon_moving) {
5679             setmangry(mon, FALSE);
5680         }
5681     }
5682 }
5683 
5684 /*uhitm.c*/
5685