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