1 | /* SCCS Id: @(#)uhitm.c 3.3 2000/02/20 */
2 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 | /* NetHack may be freely redistributed. See license for details. */
4 |
5 | #include "hack.h"
6 |
7 | STATIC_DCL boolean FDECL(known_hitum, (struct monst *,int *,struct attack *));
8 | STATIC_DCL void FDECL(steal_it, (struct monst *, struct attack *));
9 | STATIC_DCL boolean FDECL(hitum, (struct monst *,int,struct attack *));
10 | STATIC_DCL boolean FDECL(hmon_hitmon, (struct monst *,struct obj *,int));
11 | STATIC_DCL boolean FDECL(m_slips_free, (struct monst *mtmp,struct attack *mattk));
12 | STATIC_DCL int FDECL(explum, (struct monst *,struct attack *));
13 | STATIC_DCL void FDECL(start_engulf, (struct monst *));
14 | STATIC_DCL void NDECL(end_engulf);
15 | STATIC_DCL int FDECL(gulpum, (struct monst *,struct attack *));
16 | STATIC_DCL boolean FDECL(hmonas, (struct monst *,int));
17 | STATIC_DCL void FDECL(nohandglow, (struct monst *));
18 |
19 | extern boolean notonhead; /* for long worms */
20 | /* The below might become a parameter instead if we use it a lot */
21 | static int dieroll;
22 | /* Used to flag attacks caused by Stormbringer's maliciousness. */
23 | static boolean override_confirmation = FALSE;
24 |
25 | #define PROJECTILE(obj) ((obj) && is_ammo(obj))
26 |
27 | /* modified from hurtarmor() in mhitu.c */
28 | /* This is not static because it is also used for monsters rusting monsters */
29 | void
30 | hurtmarmor(mdef, attk)
31 | struct monst *mdef;
32 | int attk;
33 | {
34 | int hurt;
35 | struct obj *target;
36 |
37 | switch(attk) {
38 | /* 0 is burning, which we should never be called with */
39 | case AD_RUST: hurt = 1; break;
40 | case AD_CORRODE: hurt = 3; break;
41 | default: hurt = 2; break;
42 | }
43 | /* What the following code does: it keeps looping until it
44 | * finds a target for the rust monster.
45 | * Head, feet, etc... not covered by metal, or covered by
46 | * rusty metal, are not targets. However, your body always
47 | * is, no matter what covers it.
48 | */
49 | while (1) {
50 | switch(rn2(5)) {
51 | case 0:
52 | target = which_armor(mdef, W_ARMH);
53 | if (!target || !rust_dmg(target, xname(target), hurt, FALSE, mdef))
54 | continue;
55 | break;
56 | case 1:
57 | target = which_armor(mdef, W_ARMC);
58 | if (target) {
59 | (void)rust_dmg(target, xname(target), hurt, TRUE, mdef);
60 | break;
61 | }
62 | if ((target = which_armor(mdef, W_ARM)) != (struct obj *)0) {
63 | (void)rust_dmg(target, xname(target), hurt, TRUE, mdef);
64 | #ifdef TOURIST
65 | } else if ((target = which_armor(mdef, W_ARMU)) != (struct obj *)0) {
66 | (void)rust_dmg(target, xname(target), hurt, TRUE, mdef);
67 | #endif
68 | }
69 | break;
70 | case 2:
71 | target = which_armor(mdef, W_ARMS);
72 | if (!target || !rust_dmg(target, xname(target), hurt, FALSE, mdef))
73 | continue;
74 | break;
75 | case 3:
76 | target = which_armor(mdef, W_ARMG);
77 | if (!target || !rust_dmg(target, xname(target), hurt, FALSE, mdef))
78 | continue;
79 | break;
80 | case 4:
81 | target = which_armor(mdef, W_ARMF);
82 | if (!target || !rust_dmg(target, xname(target), hurt, FALSE, mdef))
83 | continue;
84 | break;
85 | }
86 | break; /* Out of while loop */
87 | }
88 | }
89 |
90 | boolean
91 | attack_checks(mtmp, wep)
92 | register struct monst *mtmp;
93 | struct obj *wep; /* uwep for attack(), null for kick_monster() */
94 | {
95 | char qbuf[QBUFSZ];
96 |
97 | /* if you're close enough to attack, alert any waiting monster */
98 | mtmp->mstrategy &= ~STRAT_WAITMASK;
99 |
100 | if (u.uswallow && mtmp == u.ustuck) return FALSE;
101 |
102 | if (flags.forcefight) {
103 | /* Do this in the caller, after we checked that the monster
104 | * didn't die from the blow. Reason: putting the 'I' there
105 | * causes the hero to forget the square's contents since
106 | * both 'I' and remembered contents are stored in .glyph.
107 | * If the monster dies immediately from the blow, the 'I' will
108 | * not stay there, so the player will have suddenly forgotten
109 | * the square's contents for no apparent reason.
110 | if (!canspotmon(mtmp) &&
111 | !glyph_is_invisible(levl[u.ux+u.dx][u.uy+u.dy].glyph))
112 | map_invisible(u.ux+u.dx, u.uy+u.dy);
113 | */
114 | return FALSE;
115 | }
116 |
117 | /* Put up an invisible monster marker, but one exception is for
118 | * monsters that hide. That already prints a warning message and
119 | * prevents you from hitting the monster just via the hidden monster
120 | * code below; if we also did that here, similar behavior would be
121 | * happening two turns in a row.
122 | */
123 | if (!canspotmon(mtmp) &&
124 | !glyph_is_invisible(levl[u.ux+u.dx][u.uy+u.dy].glyph) &&
125 | !(!Blind && mtmp->mundetected && hides_under(mtmp->data))) {
126 | pline("Wait! There's %s there you can't see!",
127 | something);
128 | map_invisible(u.ux+u.dx, u.uy+u.dy);
129 | /* if it was an invisible mimic, treat it as if we stumbled
130 | * onto a visible mimic
131 | */
132 | if(mtmp->m_ap_type && !Protection_from_shape_changers) {
133 | if(!u.ustuck && !mtmp->mflee && dmgtype(mtmp->data,AD_STCK))
134 | u.ustuck = mtmp;
135 | }
136 | wakeup(mtmp); /* always necessary; also un-mimics mimics */
137 | return TRUE;
138 | }
139 |
140 | if(mtmp->m_ap_type && !Protection_from_shape_changers
141 | && !sensemon(mtmp)) {
142 | /* If a hidden mimic was in a square where a player remembers
143 | * some (probably different) unseen monster, the player is in
144 | * luck--he attacks it even though it's hidden.
145 | */
146 | if (glyph_is_invisible(levl[mtmp->mx][mtmp->my].glyph)) {
147 | seemimic(mtmp);
148 | return(FALSE);
149 | }
150 | stumble_onto_mimic(mtmp);
151 | return TRUE;
152 | }
153 |
154 | if (mtmp->mundetected && !canseemon(mtmp) &&
155 | (hides_under(mtmp->data) || mtmp->data->mlet == S_EEL)) {
156 | mtmp->mundetected = mtmp->msleeping = 0;
157 | newsym(mtmp->mx, mtmp->my);
158 | if (glyph_is_invisible(levl[mtmp->mx][mtmp->my].glyph)) {
159 | seemimic(mtmp);
160 | return(FALSE);
161 | }
162 | if (!(Blind ? Blind_telepat : Unblind_telepat)) {
163 | struct obj *obj;
164 |
165 | if (Blind || (is_pool(mtmp->mx,mtmp->my) && !Underwater))
166 | pline("Wait! There's a hidden monster there!");
167 | else if ((obj = level.objects[mtmp->mx][mtmp->my]) != 0)
168 | pline("Wait! There's %s hiding under %s!",
169 | an(l_monnam(mtmp)), doname(obj));
170 | return TRUE;
171 | }
172 | }
173 |
174 | if (flags.confirm && mtmp->mpeaceful
175 | && !Confusion && !Hallucination && !Stunned) {
176 | /* Intelligent chaotic weapons (Stormbringer) want blood */
177 | if (wep && wep->oartifact == ART_STORMBRINGER) {
178 | override_confirmation = TRUE;
179 | return(FALSE);
180 | }
181 | if (canspotmon(mtmp)) {
182 | Sprintf(qbuf, "Really attack %s?", mon_nam(mtmp));
183 | if (yn(qbuf) != 'y') {
184 | flags.move = 0;
185 | return(TRUE);
186 | }
187 | }
188 | }
189 |
190 | return(FALSE);
191 | }
192 |
193 | schar
194 | find_roll_to_hit(mtmp)
195 | register struct monst *mtmp;
196 | {
197 | schar tmp;
198 | int tmp2;
199 |
200 | tmp = 1 + Luck + abon() + find_mac(mtmp) + u.uhitinc +
201 | maybe_polyd(youmonst.data->mlevel, u.ulevel);
202 |
203 | /* it is unchivalrous to attack the defenseless or from behind */
204 | if (Role_if(PM_KNIGHT) && u.ualign.type == A_LAWFUL &&
205 | (!mtmp->mcanmove || mtmp->msleeping || mtmp->mflee) &&
206 | u.ualign.record > -10) {
207 | You("caitiff!");
208 | adjalign(-1);
209 | }
210 |
211 | /* attacking peaceful creatures is bad for the samurai's giri */
212 | if (Role_if(PM_SAMURAI) && mtmp->mpeaceful &&
213 | u.ualign.record > -10) {
214 | You("dishonorably attack the innocent!");
215 | adjalign(-1);
216 | }
217 |
218 | /* Adjust vs. (and possibly modify) monster state. */
219 |
220 | if(mtmp->mstun) tmp += 2;
221 | if(mtmp->mflee) tmp += 2;
222 |
223 | if (mtmp->msleeping) {
224 | mtmp->msleeping = 0;
225 | tmp += 2;
226 | }
227 | if(!mtmp->mcanmove) {
228 | tmp += 4;
229 | if(!rn2(10)) {
230 | mtmp->mcanmove = 1;
231 | mtmp->mfrozen = 0;
232 | }
233 | }
234 | if (is_orc(mtmp->data) && maybe_polyd(is_elf(youmonst.data),
235 | Race_if(PM_ELF)))
236 | tmp++;
237 | if(Role_if(PM_MONK) && !Upolyd) {
238 | if (uarm) {
239 | Your("armor is rather cumbersome...");
240 | tmp -= urole.spelarmr;
241 | } else if (!uwep)
242 | tmp += (u.ulevel / 3) + 2;
243 | }
244 |
245 | /* with a lot of luggage, your agility diminishes */
246 | if ((tmp2 = near_capacity()) != 0) tmp -= (tmp2*2) - 1;
247 | if (u.utrap) tmp -= 3;
248 | /* Some monsters have a combination of weapon attacks and non-weapon
249 | * attacks. It is therefore wrong to add hitval to tmp; we must add
250 | * it only for the specific attack (in hmonas()).
251 | */
252 | if (uwep && !Upolyd) {
253 | tmp += hitval(uwep, mtmp);
254 | tmp += weapon_hit_bonus(uwep);
255 | }
256 | return tmp;
257 | }
258 |
259 | /* try to attack; return FALSE if monster evaded */
260 | /* u.dx and u.dy must be set */
261 | boolean
262 | attack(mtmp)
263 | register struct monst *mtmp;
264 | {
265 | schar tmp;
266 | register struct permonst *mdat = mtmp->data;
267 |
268 | /* This section of code provides protection against accidentally
269 | * hitting peaceful (like '@') and tame (like 'd') monsters.
270 | * Protection is provided as long as player is not: blind, confused,
271 | * hallucinating or stunned.
272 | * changes by wwp 5/16/85
273 | * More changes 12/90, -dkh-. if its tame and safepet, (and protected
274 | * 07/92) then we assume that you're not trying to attack. Instead,
275 | * you'll usually just swap places if this is a movement command
276 | */
277 | /* Intelligent chaotic weapons (Stormbringer) want blood */
278 | if (is_safepet(mtmp) && !flags.forcefight) {
279 | if (!uwep || uwep->oartifact != ART_STORMBRINGER) {
280 | /* there are some additional considerations: this won't work
281 | * if in a shop or Punished or you miss a random roll or
282 | * if you can walk thru walls and your pet cannot (KAA) or
283 | * if your pet is a long worm (unless someone does better).
284 | * there's also a chance of displacing a "frozen" monster.
285 | * sleeping monsters might magically walk in their sleep.
286 | */
287 | unsigned int foo = (Punished ||
288 | !rn2(7) || is_longworm(mtmp->data));
289 |
290 | if (*in_rooms(mtmp->mx, mtmp->my, SHOPBASE) || foo
291 | || (IS_ROCK(levl[u.ux][u.uy].typ) &&
292 | !passes_walls(mtmp->data))) {
293 | char buf[BUFSZ];
294 |
295 | mtmp->mflee = 1;
296 | mtmp->mfleetim = rnd(6);
297 | Strcpy(buf, y_monnam(mtmp));
298 | buf[0] = highc(buf[0]);
299 | You("stop. %s is in the way!", buf);
300 | return(TRUE);
301 | } else if ((mtmp->mfrozen || (! mtmp->mcanmove)
302 | || (mtmp->data->mmove == 0)) && rn2(6)) {
303 | pline("%s doesn't seem to move!", Monnam(mtmp));
304 | return(TRUE);
305 | } else return(FALSE);
306 | }
307 | }
308 |
309 | /* possibly set in attack_checks;
310 | examined in known_hitum, called via hitum or hmonas below */
311 | override_confirmation = FALSE;
312 | if (attack_checks(mtmp, uwep)) return(TRUE);
313 |
314 | if (Upolyd) {
315 | /* certain "pacifist" monsters don't attack */
316 | if(noattacks(youmonst.data)) {
317 | You("have no way to attack monsters physically.");
318 | mtmp->mstrategy &= ~STRAT_WAITMASK;
319 | goto atk_done;
320 | }
321 | }
322 |
323 | if(check_capacity("You cannot fight while so heavily loaded."))
324 | goto atk_done;
325 |
326 | if (u.twoweap && !can_twoweapon())
327 | untwoweapon();
328 |
329 | if(unweapon) {
330 | unweapon = FALSE;
331 | if(flags.verbose) {
332 | if(uwep)
333 | You("begin bashing monsters with your %s.",
334 | aobjnam(uwep, (char *)0));
335 | else if (!cantwield(youmonst.data))
336 | You("begin %sing monsters with your %s %s.",
337 | Role_if(PM_MONK) ? "strik" : "bash",
338 | uarmg ? "gloved" : "bare", /* Del Lamb */
339 | makeplural(body_part(HAND)));
340 | }
341 | }
342 | exercise(A_STR, TRUE); /* you're exercising muscles */
343 | /* andrew@orca: prevent unlimited pick-axe attacks */
344 | u_wipe_engr(3);
345 |
346 | /* Is the "it died" check actually correct? */
347 | if(mdat->mlet == S_LEPRECHAUN && !mtmp->mfrozen && !mtmp->msleeping &&
348 | !mtmp->mconf && mtmp->mcansee && !rn2(7) &&
349 | (m_move(mtmp, 0) == 2 || /* it died */
350 | mtmp->mx != u.ux+u.dx || mtmp->my != u.uy+u.dy)) /* it moved */
351 | return(FALSE);
352 |
353 | tmp = find_roll_to_hit(mtmp);
354 | if (Upolyd)
355 | (void) hmonas(mtmp, tmp);
356 | else
357 | (void) hitum(mtmp, tmp, youmonst.data->mattk);
358 | mtmp->mstrategy &= ~STRAT_WAITMASK;
359 |
360 | atk_done:
361 | /* see comment in attack_checks() */
362 | /* we only need to check for this if we did an attack_checks()
363 | * and it returned 0 (it's okay to attack), and the monster didn't
364 | * evade.
365 | */
366 | if (flags.forcefight && mtmp->mhp > 0 && !canspotmon(mtmp) &&
367 | !glyph_is_invisible(levl[u.ux+u.dx][u.uy+u.dy].glyph) &&
368 | !(u.uswallow && mtmp == u.ustuck))
369 | map_invisible(u.ux+u.dx, u.uy+u.dy);
370 |
371 | return(TRUE);
372 | }
373 |
374 | STATIC_OVL boolean
375 | known_hitum(mon, mhit, uattk) /* returns TRUE if monster still lives */
376 | register struct monst *mon;
377 | register int *mhit;
378 | struct attack *uattk;
379 | {
380 | register boolean malive = TRUE;
381 |
382 | if (override_confirmation) {
383 | /* this may need to be generalized if weapons other than
384 | Stormbringer acquire similar anti-social behavior... */
385 | if (flags.verbose) Your("bloodthirsty blade attacks!");
386 | }
387 |
388 | if(!*mhit) {
389 | missum(mon, uattk);
390 | } else {
391 | int oldhp = mon->mhp;
392 |
393 | /* KMH, conduct */
394 | if (uwep && (uwep->oclass == WEAPON_CLASS || is_weptool(uwep)))
395 | u.uconduct.weaphit++;
396 |
397 | /* we hit the monster; be careful: it might die! */
398 | notonhead = (mon->mx != u.ux+u.dx || mon->my != u.uy+u.dy);
399 | malive = hmon(mon, uwep, 0);
400 | if (malive && u.twoweap) malive = hmon(mon, uswapwep, 0);
401 | if (malive) {
402 | /* monster still alive */
403 | if(!rn2(25) && mon->mhp < mon->mhpmax/2
404 | && !(u.uswallow && mon == u.ustuck)) {
405 | /* maybe should regurgitate if swallowed? */
406 | mon->mflee = 1;
407 | if(!rn2(3)) {
408 | mon->mfleetim = rnd(100);
409 | if (!Blind) pline("%s turns to flee!", (Monnam(mon)));
410 | }
411 | if(u.ustuck == mon && !u.uswallow && !sticks(youmonst.data))
412 | u.ustuck = 0;
413 | }
414 | /* Vorpal Blade hit converted to miss */
415 | /* could be headless monster or worm tail */
416 | if (mon->mhp == oldhp)
417 | *mhit = 0;
418 | if (mon->wormno && *mhit)
419 | cutworm(mon, u.ux+u.dx, u.uy+u.dy, uwep);
420 | }
421 | }
422 | return(malive);
423 | }
424 |
425 | STATIC_OVL boolean
426 | hitum(mon, tmp, uattk) /* returns TRUE if monster still lives */
427 | struct monst *mon;
428 | int tmp;
429 | struct attack *uattk;
430 | {
431 | boolean malive;
432 | int mhit = (tmp > (dieroll = rnd(20)) || u.uswallow);
433 |
434 | if(tmp > dieroll) exercise(A_DEX, TRUE);
435 | malive = known_hitum(mon, &mhit, uattk);
436 | (void) passive(mon, mhit, malive, AT_WEAP);
437 | return(malive);
438 | }
439 |
440 | boolean /* general "damage monster" routine */
441 | hmon(mon, obj, thrown) /* return TRUE if mon still alive */
442 | struct monst *mon;
443 | struct obj *obj;
444 | int thrown;
445 | {
446 | boolean result, anger_guards;
447 |
448 | anger_guards = (mon->mpeaceful &&
449 | (mon->ispriest || mon->isshk ||
450 | mon->data == &mons[PM_WATCHMAN] ||
451 | mon->data == &mons[PM_WATCH_CAPTAIN]));
452 | result = hmon_hitmon(mon, obj, thrown);
453 | if (mon->ispriest && !rn2(2)) ghod_hitsu(mon);
454 | if (anger_guards) (void)angry_guards(!flags.soundok);
455 | return result;
456 | }
457 |
458 | /* guts of hmon() */
459 | STATIC_OVL boolean
460 | hmon_hitmon(mon, obj, thrown)
461 | struct monst *mon;
462 | struct obj *obj;
463 | int thrown;
464 | {
465 | int tmp;
466 | struct permonst *mdat = mon->data;
467 | int barehand_silver_rings = 0;
468 | /* The basic reason we need all these booleans is that we don't want
469 | * a "hit" message when a monster dies, so we have to know how much
470 | * damage it did _before_ outputting a hit message, but any messages
471 | * associated with the damage don't come out until _after_ outputting
472 | * a hit message.
473 | */
474 | boolean hittxt = FALSE, destroyed = FALSE;
475 | boolean get_dmg_bonus = TRUE;
476 | boolean ispoisoned = FALSE, needpoismsg = FALSE, poiskilled = FALSE;
477 | boolean silvermsg = FALSE;
478 | boolean valid_weapon_attack = FALSE;
479 | int wtype;
480 | struct obj *monwep;
481 | char yourbuf[BUFSZ];
482 |
483 | wakeup(mon);
484 | if(!obj) { /* attack with bare hands */
485 | if (mdat == &mons[PM_SHADE])
486 | tmp = 0;
487 | else if (martial_bonus())
488 | tmp = rnd(4); /* bonus for martial arts */
489 | else
490 | tmp = rnd(2);
491 | valid_weapon_attack = (tmp > 1);
492 | /* blessed gloves give bonuses when fighting 'bare-handed' */
493 | if (uarmg && uarmg->blessed && (is_undead(mdat) || is_demon(mdat)))
494 | tmp += rnd(4);
495 | /* So do silver rings. Note: rings are worn under gloves, so you
496 | * don't get both bonuses.
497 | */
498 | if (!uarmg) {
499 | if (uleft && objects[uleft->otyp].oc_material == SILVER)
500 | barehand_silver_rings++;
501 | if (uright && objects[uright->otyp].oc_material == SILVER)
502 | barehand_silver_rings++;
503 | if (barehand_silver_rings && hates_silver(mdat)) {
504 | tmp += rnd(20);
505 | silvermsg = TRUE;
506 | }
507 | }
508 | } else {
509 | if(obj->oclass == WEAPON_CLASS || is_weptool(obj) ||
510 | obj->oclass == GEM_CLASS) {
511 |
512 | /* is it not a melee weapon? */
513 | if (/* if you strike with a bow... */
514 | is_launcher(obj) ||
515 | /* or strike with a missile in your hand... */
516 | (!thrown && (is_missile(obj) || is_ammo(obj))) ||
517 | /* or use a pole at short range and not mounted... */
518 | (!thrown &&
519 | #ifdef STEED
520 | !u.usteed &&
521 | #endif
522 | is_pole(obj)) ||
523 | /* or throw a missile without the proper bow... */
524 | (is_ammo(obj) && !ammo_and_launcher(obj, uwep))) {
525 | /* then do only 1-2 points of damage */
526 | if (mdat == &mons[PM_SHADE] && obj->otyp != SILVER_ARROW)
527 | tmp = 0;
528 | else
529 | tmp = rnd(2);
530 | } else {
531 | tmp = dmgval(obj, mon);
532 | /* a minimal hit doesn't exercise proficiency */
533 | valid_weapon_attack = (tmp > 1);
534 | if (!valid_weapon_attack || mon == u.ustuck || u.twoweap) {
535 | ; /* no special bonuses */
536 | } else if (mon->mflee && Role_if(PM_ROGUE) && !Upolyd) {
537 | You("strike %s from behind!", mon_nam(mon));
538 | tmp += rnd(u.ulevel);
539 | hittxt = TRUE;
540 | } else if (dieroll == 2 && obj == uwep &&
541 | obj->oclass == WEAPON_CLASS &&
542 | (bimanual(obj) ||
543 | (Role_if(PM_SAMURAI) && obj->otyp == KATANA && !uarms)) &&
544 | ((wtype = uwep_skill_type()) != P_NONE &&
545 | P_SKILL(wtype) >= P_SKILLED) &&
546 | ((monwep = MON_WEP(mon)) != 0 &&
547 | weapon_type(monwep) != P_WHIP &&
548 | !obj_resists(monwep,
549 | 50 + 15 * greatest_erosion(monwep), 100))) {
550 | /*
551 | * 2.5% chance of shattering defender's weapon when
552 | * using a two-handed weapon; less if uwep is rusted.
553 | * [dieroll == 2 is most successful non-beheading or
554 | * -bisecting hit, in case of special artifact damage;
555 | * the percentage chance is (1/20)*(50/100).]
556 | */
557 | monwep->owornmask &= ~W_WEP;
558 | MON_NOWEP(mon);
559 | mon->weapon_check = NEED_WEAPON;
560 | pline("%s %s shatter%s from the force of your blow!",
561 | s_suffix(Monnam(mon)), xname(monwep),
562 | (monwep->quan) == 1L ? "s" : "");
563 | m_useup(mon, monwep);
564 | /* If someone just shattered MY weapon, I'd flee! */
565 | if (rn2(4) && !mon->mflee) {
566 | mon->mflee = 1;
567 | mon->mfleetim = d(2,3);
568 | }
569 | hittxt = TRUE;
570 | }
571 |
572 | if (obj->oartifact &&
573 | artifact_hit(&youmonst, mon, obj, &tmp, dieroll)) {
574 | if(mon->mhp <= 0) /* artifact killed monster */
575 | return FALSE;
576 | if (tmp == 0) return TRUE;
577 | hittxt = TRUE;
578 | }
579 | if (objects[obj->otyp].oc_material == SILVER
580 | && hates_silver(mdat))
581 | silvermsg = TRUE;
582 | if(!thrown && obj == uwep && obj->otyp == BOOMERANG &&
583 | !rnl(3)) {
584 | pline("As you hit %s, %s breaks into splinters.",
585 | mon_nam(mon), the(xname(obj)));
586 | useup(obj);
587 | obj = (struct obj *) 0;
588 | hittxt = TRUE;
589 | if (mdat != &mons[PM_SHADE])
590 | tmp++;
591 | } else if(thrown && (is_ammo(obj) || is_missile(obj))) {
592 | if (ammo_and_launcher(obj, uwep)) {
593 | /* Elves and Samurai do extra damage using
594 | * their bows&arrows; they're highly trained.
595 | */
596 | if (Role_if(PM_SAMURAI) &&
597 | obj->otyp == YA && uwep->otyp == YUMI)
598 | tmp++;
599 | else if (Race_if(PM_ELF) &&
600 | obj->otyp == ELVEN_ARROW &&
601 | uwep->otyp == ELVEN_BOW)
602 | tmp++;
603 | }
604 | if(obj->opoisoned && is_poisonable(obj))
605 | ispoisoned = TRUE;
606 | }
607 | }
608 | } else if(obj->oclass == POTION_CLASS) {
609 | if (obj->quan > 1L)
610 | setworn(splitobj(obj, 1L), W_WEP);
611 | else
612 | setuwep((struct obj *)0);
613 | freeinv(obj);
614 | potionhit(mon, obj, TRUE);
615 | if (mon->mhp <= 0) return FALSE; /* killed */
616 | hittxt = TRUE;
617 | /* in case potion effect causes transformation */
618 | mdat = mon->data;
619 | tmp = (mdat == &mons[PM_SHADE]) ? 0 : 1;
620 | } else {
621 | boolean shade_aware = FALSE;
622 |
623 | switch(obj->otyp) {
624 | case BOULDER: /* 1d20 */
625 | case HEAVY_IRON_BALL: /* 1d25 */
626 | case IRON_CHAIN: /* 1d4+1 */
627 | tmp = dmgval(obj, mon);
628 | shade_aware = TRUE; /* dmgval handles it */
629 | break;
630 | case MIRROR:
631 | if (breaktest(obj)) {
632 | You("break %s mirror. That's bad luck!",
633 | shk_your(yourbuf, obj));
634 | change_luck(-2);
635 | useup(obj);
636 | obj = (struct obj *) 0;
637 | hittxt = TRUE;
638 | }
639 | tmp = 1;
640 | break;
641 | #ifdef TOURIST
642 | case EXPENSIVE_CAMERA:
643 | You("succeed in destroying %s camera. Congratulations!",
644 | shk_your(yourbuf, obj));
645 | useup(obj);
646 | return(TRUE);
647 | #endif
648 | case CORPSE: /* fixed by polder@cs.vu.nl */
649 | if (touch_petrifies(&mons[obj->corpsenm])) {
650 | tmp = 1;
651 | hittxt = TRUE;
652 | You("hit %s with %s corpse.", mon_nam(mon),
653 | obj->dknown ? the(mons[obj->corpsenm].mname) :
654 | an(mons[obj->corpsenm].mname));
655 | if (!munstone(mon, TRUE))
656 | minstapetrify(mon, TRUE);
657 | if (resists_ston(mon)) break;
658 | /* note: hp may be <= 0 even if munstoned==TRUE */
659 | return (boolean) (mon->mhp > 0);
660 | #if 0
661 | } else if (touch_petrifies(mdat)) {
662 | /* maybe turn the corpse into a statue? */
663 | #endif
664 | }
665 | tmp = (obj->corpsenm >= LOW_PM ?
666 | mons[obj->corpsenm].msize : 0) + 1;
667 | break;
668 | case EGG:
669 | {
670 | #define useup_eggs(o) { if (thrown) obfree(o,(struct obj *)0); \
671 | else useupall(o); \
672 | o = (struct obj *)0; } /* now gone */
673 | long cnt = obj->quan;
674 |
675 | tmp = 1; /* nominal physical damage */
676 | get_dmg_bonus = FALSE;
677 | hittxt = TRUE; /* message always given */
678 | /* egg is always either used up or transformed, so next
679 | hand-to-hand attack should yield a "bashing" mesg */
680 | if (obj == uwep) unweapon = TRUE;
681 | if (obj->spe && obj->corpsenm >= LOW_PM) {
682 | if (obj->quan < 5)
683 | change_luck((schar) -(obj->quan));
684 | else
685 | change_luck(-5);
686 | }
687 |
688 | if (touch_petrifies(&mons[obj->corpsenm])) {
689 | /*learn_egg_type(obj->corpsenm);*/
690 | You("hit %s with %s %s egg%s. Splat!",
691 | mon_nam(mon),
692 | obj->known ? "the" : cnt > 1L ? "some" : "a",
693 | obj->known ? mons[obj->corpsenm].mname : "petrifying",
694 | plur(cnt));
695 | obj->known = 1; /* (not much point...) */
696 | useup_eggs(obj);
697 | if (!munstone(mon, TRUE))
698 | minstapetrify(mon, TRUE);
699 | if (resists_ston(mon)) break;
700 | return (boolean) (mon->mhp > 0);
701 | } else { /* ordinary egg(s) */
702 | const char *eggp =
703 | (obj->corpsenm != NON_PM && obj->known) ?
704 | the(mons[obj->corpsenm].mname) :
705 | (cnt > 1L) ? "some" : "an";
706 | You("hit %s with %s egg%s.",
707 | mon_nam(mon), eggp, plur(cnt));
708 | if (touch_petrifies(mdat)) {
709 | pline_The("egg%s %s alive any more...",
710 | plur(cnt),
711 | (cnt == 1L) ? "isn't" : "aren't");
712 | if (obj->timed) obj_stop_timers(obj);
713 | obj->otyp = ROCK;
714 | obj->oclass = GEM_CLASS;
715 | obj->oartifact = 0;
716 | obj->spe = 0;
717 | obj->known = obj->dknown = obj->bknown = 0;
718 | obj->owt = weight(obj);
719 | if (thrown) place_object(obj, mon->mx, mon->my);
720 | } else {
721 | pline("Splat!");
722 | useup_eggs(obj);
723 | exercise(A_WIS, FALSE);
724 | }
725 | }
726 | break;
727 | #undef useup_eggs
728 | }
729 | case CLOVE_OF_GARLIC: /* no effect against demons */
730 | if (is_undead(mdat)) {
731 | mon->mflee = 1;
732 | mon->mfleetim += d(2,4);
733 | pline("%s turns to flee!", Monnam(mon));
734 | }
735 | tmp = 1;
736 | break;
737 | case CREAM_PIE:
738 | case BLINDING_VENOM:
739 | mon->msleeping = 0;
740 | if (can_blnd(&youmonst, mon, (uchar)
741 | (obj->otyp == BLINDING_VENOM
742 | ? AT_SPIT : AT_WEAP), obj)) {
743 | if (Blind) {
744 | pline(obj->otyp == CREAM_PIE ?
745 | "Splat!" : "Splash!");
746 | } else if (obj->otyp == BLINDING_VENOM) {
747 | pline_The("venom blinds %s%s!", mon_nam(mon),
748 | mon->mcansee ? "" : " further");
749 | } else {
750 | char *whom = mon_nam(mon);
751 | /* note: s_suffix returns a modifiable buffer */
752 | if (haseyes(mdat)
753 | && mdat != &mons[PM_FLOATING_EYE])
754 | whom = strcat(s_suffix(whom), " face");
755 | pline_The("%s splashes over %s!",
756 | xname(obj), whom);
757 | }
758 | setmangry(mon);
759 | mon->mcansee = 0;
760 | tmp = rn1(25, 21);
761 | if(((int) mon->mblinded + tmp) > 127)
762 | mon->mblinded = 127;
763 | else mon->mblinded += tmp;
764 | } else {
765 | pline(obj->otyp==CREAM_PIE ? "Splat!" : "Splash!");
766 | setmangry(mon);
767 | }
768 | if (thrown) obfree(obj, (struct obj *)0);
769 | else useup(obj);
770 | hittxt = TRUE;
771 | get_dmg_bonus = FALSE;
772 | tmp = 0;
773 | break;
774 | case ACID_VENOM: /* thrown (or spit) */
775 | if (resists_acid(mon)) {
776 | Your("venom hits %s harmlessly.",
777 | mon_nam(mon));
778 | tmp = 0;
779 | } else {
780 | Your("venom burns %s!", mon_nam(mon));
781 | tmp = dmgval(obj, mon);
782 | }
783 | if (thrown) obfree(obj, (struct obj *)0);
784 | else useup(obj);
785 | hittxt = TRUE;
786 | get_dmg_bonus = FALSE;
787 | break;
788 | default:
789 | /* non-weapons can damage because of their weight */
790 | /* (but not too much) */
791 | tmp = obj->owt/100;
792 | if(tmp < 1) tmp = 1;
793 | else tmp = rnd(tmp);
794 | if(tmp > 6) tmp = 6;
795 | }
796 |
797 | if (!shade_aware && mdat == &mons[PM_SHADE] && obj &&
798 | objects[obj->otyp].oc_material != SILVER)
799 | tmp = 0;
800 | }
801 | }
802 |
803 | /****** NOTE: perhaps obj is undefined!! (if !thrown && BOOMERANG)
804 | * *OR* if attacking bare-handed!! */
805 |
806 | if (get_dmg_bonus && tmp > 0) {
807 | tmp += u.udaminc;
808 | /* If you throw using a propellor, you don't get a strength
809 | * bonus but you do get an increase-damage bonus.
810 | */
811 | if(!thrown || !obj || !uwep || !ammo_and_launcher(obj, uwep))
812 | tmp += dbon();
813 | }
814 |
815 | if (valid_weapon_attack) {
816 | struct obj *wep;
817 |
818 | /* to be valid a projectile must have had the correct projector */
819 | wep = PROJECTILE(obj) ? uwep : obj;
820 | tmp += weapon_dam_bonus(wep);
821 | /* [this assumes that `!thrown' implies wielded...] */
822 | wtype = thrown ? weapon_type(wep) : uwep_skill_type();
823 | use_skill(wtype, 1);
824 | }
825 |
826 | if (ispoisoned) {
827 | int nopoison = (10 - (obj->owt/10));
828 | if(nopoison < 2) nopoison = 2;
829 | if Role_if(PM_SAMURAI) {
830 | You("dishonorably use a poisoned weapon!");
831 | adjalign(-sgn(u.ualign.type));
832 | } else if ((u.ualign.type == A_LAWFUL) && (u.ualign.record > -10)) {
833 | You_feel("like an evil coward for using a poisoned weapon.");
834 | adjalign(-1);
835 | }
836 | if (obj && !rn2(nopoison)) {
837 | obj->opoisoned = FALSE;
838 | Your("%s%s no longer poisoned.", xname(obj),
839 | (obj->quan == 1L) ? " is" : "s are"); /**FIXME**/
840 | }
841 | if (resists_poison(mon))
842 | needpoismsg = TRUE;
843 | else if (rn2(10))
844 | tmp += rnd(6);
845 | else poiskilled = TRUE;
846 | }
847 | if (tmp < 1) {
848 | /* make sure that negative damage adjustment can't result
849 | in inadvertently boosting the victim's hit points */
850 | tmp = 0;
851 | if (mdat == &mons[PM_SHADE]) {
852 | if (!hittxt) {
853 | Your("attack passes harmlessly through %s.",
854 | mon_nam(mon));
855 | hittxt = TRUE;
856 | }
857 | } else {
858 | if (get_dmg_bonus) tmp = 1;
859 | }
860 | }
861 |
862 | /* VERY small chance of stunning opponent if unarmed. */
863 | if (tmp > 1 && !thrown && !obj && !uwep && !uarm && !uarms && !Upolyd) {
864 | if (rnd(100) < P_SKILL(P_BARE_HANDED_COMBAT) &&
865 | !bigmonst(mdat) && !thick_skinned(mdat)) {
866 | if (canspotmon(mon))
867 | pline("%s staggers from your powerful strike!",
868 | Monnam(mon));
869 | mon->mstun = 1;
870 | hittxt = TRUE;
871 | if (mon->mcanmove && mon != u.ustuck) {
872 | xchar mdx, mdy;
873 |
874 | /* see if the monster has a place to move into */
875 | mdx = mon->mx + u.dx;
876 | mdy = mon->my + u.dy;
877 | if (goodpos(mdx, mdy, mon)) {
878 | remove_monster(mon->mx, mon->my);
879 | newsym(mon->mx, mon->my);
880 | place_monster(mon, mdx, mdy);
881 | newsym(mon->mx, mon->my);
882 | set_apparxy(mon);
883 | }
884 | }
885 | }
886 | }
887 |
888 | mon->mhp -= tmp;
889 | if(mon->mhp < 1)
890 | destroyed = TRUE;
891 | if (mon->mtame && (!mon->mflee || mon->mfleetim) && tmp > 0) {
892 | unsigned fleetim;
893 |
894 | abuse_dog(mon);
895 | mon->mflee = TRUE; /* Rick Richardson */
896 | fleetim = mon->mfleetim + (unsigned)(10 * rnd(tmp));
897 | mon->mfleetim = min(fleetim,127);
898 | }
899 | if((mdat == &mons[PM_BLACK_PUDDING] || mdat == &mons[PM_BROWN_PUDDING])
900 | && obj && obj == uwep
901 | && objects[obj->otyp].oc_material == IRON
902 | && mon->mhp > 1 && !thrown && !mon->mcan
903 | /* && !destroyed -- guaranteed by mhp > 1 */ ) {
904 | if (clone_mon(mon)) {
905 | pline("%s divides as you hit it!", Monnam(mon));
906 | hittxt = TRUE;
907 | }
908 | }
909 |
910 | if(!hittxt && !destroyed) {
911 | if(thrown)
912 | /* thrown => obj exists */
913 | hit(xname(obj), mon, exclam(tmp) );
914 | else if(!flags.verbose) You("hit it.");
915 | else You("%s %s%s", Role_if(PM_BARBARIAN) ? "smite" : "hit",
916 | mon_nam(mon), canseemon(mon) ? exclam(tmp) : ".");
917 | }
918 |
919 | if (silvermsg) {
920 | const char *fmt;
921 | char *whom = mon_nam(mon);
922 |
923 | if (canspotmon(mon)) {
924 | if (barehand_silver_rings == 1)
925 | fmt = "Your silver ring sears %s!";
926 | else if (barehand_silver_rings == 2)
927 | fmt = "Your silver rings sear %s!";
928 | else
929 | fmt = "The silver sears %s!";
930 | } else {
931 | *whom = highc(*whom); /* "it" -> "It" */
932 | fmt = "%s is seared!";
933 | }
934 | /* note: s_suffix returns a modifiable buffer */
935 | if (!noncorporeal(mdat))
936 | whom = strcat(s_suffix(whom), " flesh");
937 | pline(fmt, whom);
938 | }
939 |
940 | if (needpoismsg)
941 | pline_The("poison doesn't seem to affect %s.", mon_nam(mon));
942 | if (poiskilled) {
943 | pline_The("poison was deadly...");
944 | xkilled(mon, 0);
945 | return FALSE;
946 | } else if (destroyed) {
947 | killed(mon); /* takes care of most messages */
948 | } else if(u.umconf && !thrown) {
949 | nohandglow(mon);
950 | if(!mon->mconf && !resist(mon, '+', 0, NOTELL)) {
951 | mon->mconf = 1;
952 | if (!mon->mstun && mon->mcanmove && !mon->msleeping &&
953 | canseemon(mon))
954 | pline("%s appears confused.", Monnam(mon));
955 | }
956 | }
957 |
958 | return((boolean)(destroyed ? FALSE : TRUE));
959 | }
960 |
961 | /* check whether slippery clothing protects from hug or wrap attack */
962 | /* [currently assumes that you are the attacker] */
963 | STATIC_OVL boolean
964 | m_slips_free(mdef, mattk)
965 | struct monst *mdef;
966 | struct attack *mattk;
967 | {
968 | struct obj *obj;
969 |
970 | if (mattk->adtyp == AD_DRIN) {
971 | /* intelligence drain attacks the head */
972 | obj = which_armor(mdef, W_ARMH);
973 | } else {
974 | /* grabbing attacks the body */
975 | obj = which_armor(mdef, W_ARMC); /* cloak */
976 | if (!obj) obj = which_armor(mdef, W_ARM); /* suit */
977 | #ifdef TOURIST
978 | if (!obj) obj = which_armor(mdef, W_ARMU); /* shirt */
979 | #endif
980 | }
981 |
982 | /* if your cloak/armor is greased, monster slips off; this
983 | protection might fail (33% chance) when the armor is cursed */
984 | if (obj && (obj->greased || obj->otyp == OILSKIN_CLOAK) &&
985 | (!obj->cursed || rn2(3))) {
986 | You("%s %s %s %s!",
987 | mattk->adtyp == AD_WRAP ?
988 | "slip off of" : "grab, but cannot hold onto",
989 | s_suffix(mon_nam(mdef)),
990 | obj->greased ? "greased" : "slippery",
991 | /* avoid "slippery slippery cloak"
992 | for undiscovered oilskin cloak */
993 | (obj->greased || objects[obj->otyp].oc_name_known) ?
994 | xname(obj) : "cloak");
995 |
996 | if (obj->greased && !rn2(2)) {
997 | pline_The("grease wears off.");
998 | obj->greased = 0;
999 | }
1000 | return TRUE;
1001 | }
1002 | return FALSE;
1003 | }
1004 |
1005 | STATIC_DCL void NDECL(demonpet);
1006 | /*
1007 | * Send in a demon pet for the hero. Exercise wisdom.
1008 | *
1009 | * This function used to be inline to damageum(), but the Metrowerks compiler
1010 | * (DR4 and DR4.5) screws up with an internal error 5 "Expression Too Complex."
1011 | * Pulling it out makes it work.
1012 | */
1013 | STATIC_OVL void
1014 | demonpet()
1015 | {
1016 | struct permonst *pm;
1017 | struct monst *dtmp;
1018 |
1019 | pline("Some hell-p has arrived!");
1020 | pm = !rn2(6) ? &mons[ndemon(u.ualign.type)] : youmonst.data;
1021 | if ((dtmp = makemon(pm, u.ux, u.uy, NO_MM_FLAGS)) != 0)
1022 | (void)tamedog(dtmp, (struct obj *)0);
1023 | exercise(A_WIS, TRUE);
1024 | }
1025 |
1026 | /*
1027 | * Player uses theft attack against monster.
1028 | *
1029 | * If the target is wearing body armor, take all of its possesions;
1030 | * otherwise, take one object. [Is this really the behavior we want?]
1031 | *
1032 | * This routine implicitly assumes that there is no way to be able to
1033 | * resist petfication (ie, be polymorphed into a xorn or golem) at the
1034 | * same time as being able to steal (poly'd into nymph or succubus).
1035 | * If that ever changes, the check for touching a cockatrice corpse
1036 | * will need to be smarter about whether to break out of the theft loop.
1037 | */
1038 | STATIC_OVL void
1039 | steal_it(mdef, mattk)
1040 | struct monst *mdef;
1041 | struct attack *mattk;
1042 | {
1043 | struct obj *otmp, *stealoid, **minvent_ptr;
1044 | long unwornmask;
1045 |
1046 | if (!mdef->minvent) return; /* nothing to take */
1047 |
1048 | /* look for worn body armor */
1049 | stealoid = (struct obj *)0;
1050 | if (could_seduce(&youmonst, mdef, mattk)) {
1051 | /* find armor, and move it to end of inventory in the process */
1052 | minvent_ptr = &mdef->minvent;
1053 | while ((otmp = *minvent_ptr) != 0)
1054 | if (otmp->owornmask & W_ARM) {
1055 | if (stealoid) panic("steal_it: multiple worn suits");
1056 | *minvent_ptr = otmp->nobj; /* take armor out of minvent */
1057 | stealoid = otmp;
1058 | stealoid->nobj = (struct obj *)0;
1059 | } else {
1060 | minvent_ptr = &otmp->nobj;
1061 | }
1062 | *minvent_ptr = stealoid; /* put armor back into minvent */
1063 | }
1064 |
1065 | if (stealoid) { /* we will be taking everything */
1066 | if (gender(mdef) == (int) u.mfemale &&
1067 | youmonst.data->mlet == S_NYMPH)
1068 | You("charm %s. She gladly hands over her possessions.",
1069 | mon_nam(mdef));
1070 | else
1071 | You("seduce %s and %s starts to take off %s clothes.",
1072 | mon_nam(mdef), he[pronoun_gender(mdef)],
1073 | his[pronoun_gender(mdef)]);
1074 | }
1075 |
1076 | while ((otmp = mdef->minvent) != 0) {
1077 | /* take the object away from the monster */
1078 | obj_extract_self(otmp);
1079 | if ((unwornmask = otmp->owornmask) != 0L) {
1080 | mdef->misc_worn_check &= ~unwornmask;
1081 | otmp->owornmask = 0L;
1082 | update_mon_intrinsics(mdef, otmp, FALSE);
1083 |
1084 | if (otmp == stealoid) /* special message for final item */
1085 | pline("%s finishes taking off %s suit.",
1086 | Monnam(mdef), his[pronoun_gender(mdef)]);
1087 | }
1088 | /* give the object to the character */
1089 | otmp = hold_another_object(otmp, "You steal %s.",
1090 | doname(otmp), "You steal: ");
1091 | if (otmp->otyp == CORPSE &&
1092 | touch_petrifies(&mons[otmp->corpsenm]) && !uarmg) {
1093 | char kbuf[BUFSZ];
1094 |
1095 | Sprintf(kbuf, "stolen %s corpse", mons[otmp->corpsenm].mname);
1096 | instapetrify(kbuf);
1097 | break; /* stop the theft even if hero survives */
1098 | }
1099 | /* more take-away handling, after theft message */
1100 | if (unwornmask & W_WEP) { /* stole wielded weapon */
1101 | possibly_unwield(mdef);
1102 | } else if (unwornmask & W_ARMG) { /* stole worn gloves */
1103 | mselftouch(mdef, (const char *)0, TRUE);
1104 | if (mdef->mhp <= 0) /* it's now a statue */
1105 | return; /* can't continue stealing */
1106 | }
1107 |
1108 | if (!stealoid) break; /* only taking one item */
1109 | }
1110 | }
1111 |
1112 | int
1113 | damageum(mdef, mattk)
1114 | register struct monst *mdef;
1115 | register struct attack *mattk;
1116 | {
1117 | register struct permonst *pd = mdef->data;
1118 | register int tmp = d((int)mattk->damn, (int)mattk->damd);
1119 |
1120 | if (is_demon(youmonst.data) && !rn2(13) && !uwep
1121 | && u.umonnum != PM_SUCCUBUS && u.umonnum != PM_INCUBUS
1122 | && u.umonnum != PM_BALROG) {
1123 | demonpet();
1124 | return(0);
1125 | }
1126 | switch(mattk->adtyp) {
1127 | case AD_STUN:
1128 | if(!Blind)
1129 | pline("%s staggers for a moment.", Monnam(mdef));
1130 | mdef->mstun = 1;
1131 | /* fall through to next case */
1132 | case AD_WERE: /* no effect on monsters */
1133 | case AD_HEAL:
1134 | case AD_LEGS:
1135 | case AD_PHYS:
1136 | if(mattk->aatyp == AT_WEAP) {
1137 | if(uwep) tmp = 0;
1138 | } else if(mattk->aatyp == AT_KICK) {
1139 | if(thick_skinned(mdef->data)) tmp = 0;
1140 | if(mdef->data == &mons[PM_SHADE]) {
1141 | if (!(uarmf && uarmf->blessed)) {
1142 | impossible("bad shade attack function flow?");
1143 | tmp = 0;
1144 | } else
1145 | tmp = rnd(4); /* bless damage */
1146 | }
1147 | }
1148 | break;
1149 | case AD_FIRE:
1150 | if (!Blind)
1151 | pline("%s is %s!", Monnam(mdef),
1152 | mattk->aatyp == AT_HUGS ?
1153 | "being roasted" : "on fire");
1154 | if (pd == &mons[PM_STRAW_GOLEM] ||
1155 | pd == &mons[PM_PAPER_GOLEM]) {
1156 | if (!Blind)
1157 | pline("%s burns completely!", Monnam(mdef));
1158 | xkilled(mdef,0);
1159 | }
1160 | tmp += destroy_mitem(mdef, SCROLL_CLASS, AD_FIRE);
1161 | tmp += destroy_mitem(mdef, SPBOOK_CLASS, AD_FIRE);
1162 | if (resists_fire(mdef)) {
1163 | if (!Blind)
1164 | pline_The("fire doesn't heat %s!", mon_nam(mdef));
1165 | golemeffects(mdef, AD_FIRE, tmp);
1166 | shieldeff(mdef->mx, mdef->my);
1167 | tmp = 0;
1168 | }
1169 | /* only potions damage resistant players in destroy_item */
1170 | tmp += destroy_mitem(mdef, POTION_CLASS, AD_FIRE);
1171 | break;
1172 | case AD_COLD:
1173 | if (!Blind) pline("%s is covered in frost!", Monnam(mdef));
1174 | if (resists_cold(mdef)) {
1175 | shieldeff(mdef->mx, mdef->my);
1176 | if (!Blind)
1177 | pline_The("frost doesn't chill %s!", mon_nam(mdef));
1178 | golemeffects(mdef, AD_COLD, tmp);
1179 | tmp = 0;
1180 | }
1181 | tmp += destroy_mitem(mdef, POTION_CLASS, AD_COLD);
1182 | break;
1183 | case AD_ELEC:
1184 | if (!Blind) pline("%s is zapped!", Monnam(mdef));
1185 | tmp += destroy_mitem(mdef, WAND_CLASS, AD_ELEC);
1186 | if (resists_elec(mdef)) {
1187 | if (!Blind)
1188 | pline_The("zap doesn't shock %s!", mon_nam(mdef));
1189 | golemeffects(mdef, AD_ELEC, tmp);
1190 | shieldeff(mdef->mx, mdef->my);
1191 | tmp = 0;
1192 | }
1193 | /* only rings damage resistant players in destroy_item */
1194 | tmp += destroy_mitem(mdef, RING_CLASS, AD_ELEC);
1195 | break;
1196 | case AD_ACID:
1197 | if (resists_acid(mdef)) tmp = 0;
1198 | break;
1199 | case AD_STON:
1200 | if (!munstone(mdef, TRUE))
1201 | minstapetrify(mdef, TRUE);
1202 | tmp = 0;
1203 | break;
1204 | #ifdef SEDUCE
1205 | case AD_SSEX:
1206 | #endif
1207 | case AD_SEDU:
1208 | case AD_SITM:
1209 | steal_it(mdef, mattk);
1210 | tmp = 0;
1211 | break;
1212 | case AD_SGLD:
1213 | if (mdef->mgold) {
1214 | u.ugold += mdef->mgold;
1215 | mdef->mgold = 0;
1216 | Your("purse feels heavier.");
1217 | }
1218 | exercise(A_DEX, TRUE);
1219 | tmp = 0;
1220 | break;
1221 | case AD_TLPT:
1222 | if(tmp <= 0) tmp = 1;
1223 | if(tmp < mdef->mhp) {
1224 | char nambuf[BUFSZ];
1225 | boolean u_saw_mon = canseemon(mdef);
1226 | /* record the name before losing sight of monster */
1227 | Strcpy(nambuf, Monnam(mdef));
1228 | if (u_teleport_mon(mdef, FALSE) &&
1229 | u_saw_mon && !canseemon(mdef))
1230 | pline("%s suddenly disappears!", nambuf);
1231 | }
1232 | break;
1233 | case AD_BLND:
1234 | if (can_blnd(&youmonst, mdef, mattk->aatyp, (struct obj*)0)) {
1235 | if(!Blind && mdef->mcansee)
1236 | pline("%s is blinded.", Monnam(mdef));
1237 | mdef->mcansee = 0;
1238 | tmp += mdef->mblinded;
1239 | if (tmp > 127) tmp = 127;
1240 | mdef->mblinded = tmp;
1241 | }
1242 | tmp = 0;
1243 | break;
1244 | case AD_CURS:
1245 | if (night() && !rn2(10) && !mdef->mcan) {
1246 | if (mdef->data == &mons[PM_CLAY_GOLEM]) {
1247 | if (!Blind)
1248 | pline("Some writing vanishes from %s head!",
1249 | s_suffix(mon_nam(mdef)));
1250 | xkilled(mdef, 0);
1251 | /* Don't return yet; keep hp<1 and tmp=0 for pet msg */
1252 | } else {
1253 | mdef->mcan = 1;
1254 | You("chuckle.");
1255 | }
1256 | }
1257 | tmp = 0;
1258 | break;
1259 | case AD_DRLI:
1260 | if (rn2(2) && !resists_drli(mdef)) {
1261 | int xtmp = d(2,6);
1262 | pline("%s suddenly seems weaker!", Monnam(mdef));
1263 | mdef->mhpmax -= xtmp;
1264 | if ((mdef->mhp -= xtmp) <= 0 || !mdef->m_lev) {
1265 | pline("%s dies!", Monnam(mdef));
1266 | xkilled(mdef,0);
1267 | } else
1268 | mdef->m_lev--;
1269 | }
1270 | tmp = 0;
1271 | break;
1272 | case AD_RUST:
1273 | if (pd == &mons[PM_IRON_GOLEM]) {
1274 | pline("%s falls to pieces!", Monnam(mdef));
1275 | xkilled(mdef,0);
1276 | }
1277 | hurtmarmor(mdef, AD_RUST);
1278 | tmp = 0;
1279 | break;
1280 | case AD_CORRODE:
1281 | hurtmarmor(mdef, AD_CORRODE);
1282 | tmp = 0;
1283 | break;
1284 | case AD_DCAY:
1285 | if (pd == &mons[PM_WOOD_GOLEM] ||
1286 | pd == &mons[PM_LEATHER_GOLEM]) {
1287 | pline("%s falls to pieces!", Monnam(mdef));
1288 | xkilled(mdef,0);
1289 | }
1290 | hurtmarmor(mdef, AD_DCAY);
1291 | tmp = 0;
1292 | break;
1293 | case AD_DRST:
1294 | case AD_DRDX:
1295 | case AD_DRCO:
1296 | if (!rn2(8)) {
1297 | Your("%s was poisoned!", mpoisons_subj(&youmonst, mattk));
1298 | if (resists_poison(mdef))
1299 | pline_The("poison doesn't seem to affect %s.",
1300 | mon_nam(mdef));
1301 | else {
1302 | if (!rn2(10)) {
1303 | Your("poison was deadly...");
1304 | tmp = mdef->mhp;
1305 | } else tmp += rn1(10,6);
1306 | }
1307 | }
1308 | break;
1309 | case AD_DRIN:
1310 | if (notonhead || !has_head(mdef->data)) {
1311 | pline("%s doesn't seem harmed.", Monnam(mdef));
1312 | tmp = 0;
1313 | break;
1314 | }
1315 | if (m_slips_free(mdef, mattk)) break;
1316 |
1317 | if ((mdef->misc_worn_check & W_ARMH) && rn2(8)) {
1318 | pline("%s helmet blocks your attack to %s head.",
1319 | s_suffix(Monnam(mdef)), his[pronoun_gender(mdef)]);
1320 | break;
1321 | }
1322 |
1323 | You("eat %s brain!", s_suffix(mon_nam(mdef)));
1324 | u.uconduct.food++;
1325 | if (!vegan(mdef->data))
1326 | u.uconduct.unvegan++;
1327 | if (!vegetarian(mdef->data))
1328 | violated_vegetarian();
1329 | if (mindless(mdef->data)) {
1330 | pline("%s doesn't notice.", Monnam(mdef));
1331 | break;
1332 | }
1333 | tmp += rnd(10);
1334 | morehungry(-rnd(30)); /* cannot choke */
1335 | if (ABASE(A_INT) < AMAX(A_INT)) {
1336 | ABASE(A_INT) += rnd(4);
1337 | if (ABASE(A_INT) > AMAX(A_INT))
1338 | ABASE(A_INT) = AMAX(A_INT);
1339 | flags.botl = 1;
1340 | }
1341 | exercise(A_WIS, TRUE);
1342 | break;
1343 | case AD_STCK:
1344 | if (!sticks(mdef->data))
1345 | u.ustuck = mdef; /* it's now stuck to you */
1346 | break;
1347 | case AD_WRAP:
1348 | if (!sticks(mdef->data)) {
1349 | if (!u.ustuck && !rn2(10)) {
1350 | if (m_slips_free(mdef, mattk)) {
1351 | tmp = 0;
1352 | } else {
1353 | You("swing yourself around %s!",
1354 | mon_nam(mdef));
1355 | u.ustuck = mdef;
1356 | }
1357 | } else if(u.ustuck == mdef) {
1358 | /* Monsters don't wear amulets of magical breathing */
1359 | if (is_pool(u.ux,u.uy) && !is_swimmer(mdef->data)) {
1360 | You("drown %s...", mon_nam(mdef));
1361 | tmp = mdef->mhp;
1362 | } else if(mattk->aatyp == AT_HUGS)
1363 | pline("%s is being crushed.", Monnam(mdef));
1364 | } else {
1365 | tmp = 0;
1366 | if (flags.verbose)
1367 | You("brush against %s %s.",
1368 | s_suffix(mon_nam(mdef)),
1369 | mbodypart(mdef, LEG));
1370 | }
1371 | } else tmp = 0;
1372 | break;
1373 | case AD_PLYS:
1374 | if (mdef->mcanmove && !rn2(3) && tmp < mdef->mhp) {
1375 | if (!Blind) pline("%s is frozen by you!", Monnam(mdef));
1376 | mdef->mcanmove = 0;
1377 | mdef->mfrozen = rnd(10);
1378 | }
1379 | break;
1380 | case AD_SLEE:
1381 | if (!mdef->msleeping && sleep_monst(mdef, rnd(10), -1)) {
1382 | if (!Blind)
1383 | pline("%s is put to sleep by you!", Monnam(mdef));
1384 | slept_monst(mdef);
1385 | }
1386 | break;
1387 | case AD_SLIM:
1388 | if (!rn2(4) && mdef->data != &mons[PM_FIRE_VORTEX] &&
1389 | mdef->data != &mons[PM_FIRE_ELEMENTAL] &&
1390 | mdef->data != &mons[PM_GREEN_SLIME]) {
1391 | You("turn %s into slime.", mon_nam(mdef));
1392 | (void) newcham(mdef, &mons[PM_GREEN_SLIME]);
1393 | tmp = 0;
1394 | }
1395 | break;
1396 | case AD_ENCH: /* KMH -- remove enchantment (disenchanter) */
1397 | /* There's no msomearmor() function, so just do damage */
1398 | break;
1399 | default: tmp = 0;
1400 | break;
1401 | }
1402 |
1403 | if((mdef->mhp -= tmp) < 1) {
1404 | if (mdef->mtame && !cansee(mdef->mx,mdef->my)) {
1405 | You_feel("embarrassed for a moment.");
1406 | if (tmp) xkilled(mdef, 0); /* !tmp but hp<1: already killed */
1407 | } else if (!flags.verbose) {
1408 | You("destroy it!");
1409 | if (tmp) xkilled(mdef, 0);
1410 | } else
1411 | if (tmp) killed(mdef);
1412 | return(2);
1413 | }
1414 | return(1);
1415 | }
1416 |
1417 | STATIC_OVL int
1418 | explum(mdef, mattk)
1419 | register struct monst *mdef;
1420 | register struct attack *mattk;
1421 | {
1422 | register int tmp = d((int)mattk->damn, (int)mattk->damd);
1423 |
1424 | You("explode!");
1425 | switch(mattk->adtyp) {
1426 | boolean resistance; /* only for cold/fire/elec */
1427 |
1428 | case AD_BLND:
1429 | if (!resists_blnd(mdef)) {
1430 | pline("%s is blinded by your flash of light!", Monnam(mdef));
1431 | mdef->mblinded = min((int)mdef->mblinded + tmp, 127);
1432 | mdef->mcansee = 0;
1433 | }
1434 | break;
1435 | case AD_HALU:
1436 | if (haseyes(mdef->data) && mdef->mcansee) {
1437 | pline("%s is affected by your flash of light!",
1438 | Monnam(mdef));
1439 | mdef->mconf = 1;
1440 | }
1441 | break;
1442 | case AD_COLD:
1443 | resistance = resists_cold(mdef);
1444 | goto common;
1445 | case AD_FIRE:
1446 | resistance = resists_fire(mdef);
1447 | goto common;
1448 | case AD_ELEC:
1449 | resistance = resists_elec(mdef);
1450 | common:
1451 | if (!resistance) {
1452 | pline("%s gets blasted!", Monnam(mdef));
1453 | mdef->mhp -= tmp;
1454 | if (mdef->mhp <= 0) {
1455 | killed(mdef);
1456 | return(2);
1457 | }
1458 | } else {
1459 | shieldeff(mdef->mx, mdef->my);
1460 | if (is_golem(mdef->data))
1461 | golemeffects(mdef, (int)mattk->adtyp, tmp);
1462 | else
1463 | pline_The("blast doesn't seem to affect %s.",
1464 | mon_nam(mdef));
1465 | }
1466 | break;
1467 | default:
1468 | break;
1469 | }
1470 | return(1);
1471 | }
1472 |
1473 | STATIC_OVL void
1474 | start_engulf(mdef)
1475 | struct monst *mdef;
1476 | {
1477 | if (!Invisible) {
1478 | map_location(u.ux, u.uy, TRUE);
1479 | tmp_at(DISP_ALWAYS, mon_to_glyph(&youmonst));
1480 | tmp_at(mdef->mx, mdef->my);
1481 | }
1482 | You("engulf %s!", mon_nam(mdef));
1483 | delay_output();
1484 | delay_output();
1485 | }
1486 |
1487 | STATIC_OVL void
1488 | end_engulf()
1489 | {
1490 | if (!Invisible) {
1491 | tmp_at(DISP_END, 0);
1492 | newsym(u.ux, u.uy);
1493 | }
1494 | }
1495 |
1496 | STATIC_OVL int
1497 | gulpum(mdef,mattk)
1498 | register struct monst *mdef;
1499 | register struct attack *mattk;
1500 | {
1501 | register int tmp;
1502 | register int dam = d((int)mattk->damn, (int)mattk->damd);
1503 | struct obj *otmp;
1504 | /* Not totally the same as for real monsters. Specifically, these
1505 | * don't take multiple moves. (It's just too hard, for too little
1506 | * result, to program monsters which attack from inside you, which
1507 | * would be necessary if done accurately.) Instead, we arbitrarily
1508 | * kill the monster immediately for AD_DGST and we regurgitate them
1509 | * after exactly 1 round of attack otherwise. -KAA
1510 | */
1511 |
1512 | if(mdef->data->msize >= MZ_HUGE) return 0;
1513 |
1514 | if(u.uhunger < 1500 && !u.uswallow) {
1515 | for (otmp = mdef->minvent; otmp; otmp = otmp->nobj)
1516 | (void) snuff_lit(otmp);
1517 |
1518 | if(!touch_petrifies(mdef->data) || Stone_resistance) {
1519 | #ifdef LINT /* static char msgbuf[BUFSZ]; */
1520 | char msgbuf[BUFSZ];
1521 | #else
1522 | static char msgbuf[BUFSZ];
1523 | #endif
1524 | start_engulf(mdef);
1525 | switch(mattk->adtyp) {
1526 | case AD_DGST:
1527 | /* eating a Rider or its corpse is fatal */
1528 | if (is_rider(mdef->data)) {
1529 | pline("Unfortunately, digesting any of it is fatal.");
1530 | end_engulf();
1531 | Sprintf(msgbuf, "unwisely tried to eat %s",
1532 | mdef->data->mname);
1533 | killer = msgbuf;
1534 | killer_format = NO_KILLER_PREFIX;
1535 | done(DIED);
1536 | return 0; /* lifesaved */
1537 | }
1538 |
1539 | if (Slow_digestion) {
1540 | dam = 0;
1541 | break;
1542 | }
1543 |
1544 | /* KMH, conduct */
1545 | u.uconduct.food++;
1546 | if (!vegan(mdef->data))
1547 | u.uconduct.unvegan++;
1548 | if (!vegetarian(mdef->data))
1549 | violated_vegetarian();
1550 |
1551 | /* Use up amulet of life saving */
1552 | if (!!(otmp = mlifesaver(mdef))) m_useup(mdef, otmp);
1553 |
1554 | newuhs(FALSE);
1555 | xkilled(mdef,2);
1556 | if (mdef->mhp > 0) { /* monster lifesaved */
1557 | You("hurriedly regurgitate the sizzling in your stomach.");
1558 | } else {
1559 | u.uhunger += mdef->data->cnutrit;
1560 | Sprintf(msgbuf, "You totally digest %s.",
1561 | mon_nam(mdef));
1562 | if ((tmp = 3 + (mdef->data->cwt >> 6)) != 0) {
1563 | /* setting afternmv = end_engulf is tempting,
1564 | * but will cause problems if the player is
1565 | * attacked (which uses his real location) or
1566 | * if his See_invisible wears off
1567 | */
1568 | You("digest %s.", mon_nam(mdef));
1569 | if (Slow_digestion) tmp *= 2;
1570 | nomul(-tmp);
1571 | nomovemsg = msgbuf;
1572 | } else pline("%s", msgbuf);
1573 | exercise(A_CON, TRUE);
1574 | }
1575 | end_engulf();
1576 | return(2);
1577 | case AD_PHYS:
1578 | pline("%s is pummeled with your debris!",Monnam(mdef));
1579 | break;
1580 | case AD_ACID:
1581 | pline("%s is covered with your goo!", Monnam(mdef));
1582 | if (resists_acid(mdef)) {
1583 | pline("It seems harmless to %s.", mon_nam(mdef));
1584 | dam = 0;
1585 | }
1586 | break;
1587 | case AD_BLND:
1588 | if (can_blnd(&youmonst, mdef, mattk->aatyp, (struct obj *)0)) {
1589 | if (mdef->mcansee)
1590 | pline("%s can't see in there!", Monnam(mdef));
1591 | mdef->mcansee = 0;
1592 | dam += mdef->mblinded;
1593 | if (dam > 127) dam = 127;
1594 | mdef->mblinded = dam;
1595 | }
1596 | dam = 0;
1597 | break;
1598 | case AD_ELEC:
1599 | if (rn2(2)) {
1600 | pline_The("air around %s crackles with electricity.", mon_nam(mdef));
1601 | if (resists_elec(mdef)) {
1602 | pline("%s seems unhurt.", Monnam(mdef));
1603 | dam = 0;
1604 | }
1605 | golemeffects(mdef,(int)mattk->adtyp,dam);
1606 | } else dam = 0;
1607 | break;
1608 | case AD_COLD:
1609 | if (rn2(2)) {
1610 | if (resists_cold(mdef)) {
1611 | pline("%s seems mildly chilly.", Monnam(mdef));
1612 | dam = 0;
1613 | } else
1614 | pline("%s is freezing to death!",Monnam(mdef));
1615 | golemeffects(mdef,(int)mattk->adtyp,dam);
1616 | } else dam = 0;
1617 | break;
1618 | case AD_FIRE:
1619 | if (rn2(2)) {
1620 | if (resists_fire(mdef)) {
1621 | pline("%s seems mildly hot.", Monnam(mdef));
1622 | dam = 0;
1623 | } else
1624 | pline("%s is burning to a crisp!",Monnam(mdef));
1625 | golemeffects(mdef,(int)mattk->adtyp,dam);
1626 | } else dam = 0;
1627 | break;
1628 | }
1629 | end_engulf();
1630 | if ((mdef->mhp -= dam) <= 0) {
1631 | killed(mdef);
1632 | if (mdef->mhp <= 0) /* not lifesaved */
1633 | return(2);
1634 | }
1635 | You("%s %s!", is_animal(youmonst.data) ? "regurgitate"
1636 | : "expel", mon_nam(mdef));
1637 | if (Slow_digestion || is_animal(youmonst.data)) {
1638 | pline("Obviously, you didn't like %s taste.",
1639 | s_suffix(mon_nam(mdef)));
1640 | }
1641 | } else {
1642 | char kbuf[BUFSZ];
1643 |
1644 | You("bite into %s.", mon_nam(mdef));
1645 | Sprintf(kbuf, "swallowing %s whole", an(mdef->data->mname));
1646 | instapetrify(kbuf);
1647 | }
1648 | }
1649 | return(0);
1650 | }
1651 |
1652 | void
1653 | missum(mdef,mattk)
1654 | register struct monst *mdef;
1655 | register struct attack *mattk;
1656 | {
1657 | if (could_seduce(&youmonst, mdef, mattk))
1658 | You("pretend to be friendly to %s.", mon_nam(mdef));
1659 | else if(canspotmon(mdef) && flags.verbose)
1660 | You("miss %s.", mon_nam(mdef));
1661 | else
1662 | You("miss it.");
1663 | if (!mdef->msleeping && mdef->mcanmove)
1664 | wakeup(mdef);
1665 | }
1666 |
1667 | STATIC_OVL boolean
1668 | hmonas(mon, tmp) /* attack monster as a monster. */
1669 | register struct monst *mon;
1670 | register int tmp;
1671 | {
1672 | register struct attack *mattk;
1673 | int i, sum[NATTK], hittmp = 0;
1674 | int nsum = 0;
1675 | int dhit = 0;
1676 |
1677 | for(i = 0; i < NATTK; i++) {
1678 |
1679 | sum[i] = 0;
1680 | mattk = &(youmonst.data->mattk[i]);
1681 | switch(mattk->aatyp) {
1682 | case AT_WEAP:
1683 | use_weapon:
1684 | /* Certain monsters don't use weapons when encountered as enemies,
1685 | * but players who polymorph into them have hands or claws and thus
1686 | * should be able to use weapons. This shouldn't prohibit the use
1687 | * of most special abilities, either.
1688 | */
1689 | /* Potential problem: if the monster gets multiple weapon attacks,
1690 | * we currently allow the player to get each of these as a weapon
1691 | * attack. Is this really desirable?
1692 | */
1693 | if (uwep) {
1694 | hittmp = hitval(uwep, mon);
1695 | hittmp += weapon_hit_bonus(uwep);
1696 | tmp += hittmp;
1697 | }
1698 | dhit = (tmp > (dieroll = rnd(20)) || u.uswallow);
1699 | /* KMH -- Don't accumulate to-hit bonuses */
1700 | if (uwep) tmp -= hittmp;
1701 | /* Enemy dead, before any special abilities used */
1702 | if (!known_hitum(mon,&dhit,mattk)) {
1703 | sum[i] = 2;
1704 | break;
1705 | } else sum[i] = 1;
1706 | /* might be a worm that gets cut in half */
1707 | if (m_at(u.ux+u.dx, u.uy+u.dy) != mon) return((boolean)(nsum != 0));
1708 | /* Do not print "You hit" message, since known_hitum
1709 | * already did it.
1710 | */
1711 | if (dhit && mattk->adtyp != AD_SPEL
1712 | && mattk->adtyp != AD_PHYS)
1713 | sum[i] = damageum(mon,mattk);
1714 | break;
1715 | case AT_CLAW:
1716 | if (i==0 && uwep && !cantwield(youmonst.data)) goto use_weapon;
1717 | #ifdef SEDUCE
1718 | /* succubi/incubi are humanoid, but their _second_
1719 | * attack is AT_CLAW, not their first...
1720 | */
1721 | if (i==1 && uwep && (u.umonnum == PM_SUCCUBUS ||
1722 | u.umonnum == PM_INCUBUS)) goto use_weapon;
1723 | #endif
1724 | case AT_KICK:
1725 | case AT_BITE:
1726 | case AT_STNG:
1727 | case AT_TUCH:
1728 | case AT_BUTT:
1729 | case AT_TENT:
1730 | if (i==0 && uwep && (youmonst.data->mlet==S_LICH)) goto use_weapon;
1731 | if ((dhit = (tmp > rnd(20) || u.uswallow)) != 0) {
1732 | int compat;
1733 |
1734 | if (!u.uswallow &&
1735 | (compat=could_seduce(&youmonst, mon, mattk))) {
1736 | You("%s %s %s.",
1737 | mon->mcansee && haseyes(mon->data)
1738 | ? "smile at" : "talk to",
1739 | mon_nam(mon),
1740 | compat == 2 ? "engagingly":"seductively");
1741 | /* doesn't anger it; no wakeup() */
1742 | sum[i] = damageum(mon, mattk);
1743 | break;
1744 | }
1745 | wakeup(mon);
1746 | /* maybe this check should be in damageum()? */
1747 | if (mon->data == &mons[PM_SHADE] &&
1748 | !(mattk->aatyp == AT_KICK &&
1749 | uarmf && uarmf->blessed)) {
1750 | Your("attack passes harmlessly through %s.",
1751 | mon_nam(mon));
1752 | break;
1753 | }
1754 | if (mattk->aatyp == AT_KICK)
1755 | You("kick %s.", mon_nam(mon));
1756 | else if (mattk->aatyp == AT_BITE)
1757 | You("bite %s.", mon_nam(mon));
1758 | else if (mattk->aatyp == AT_STNG)
1759 | You("sting %s.", mon_nam(mon));
1760 | else if (mattk->aatyp == AT_BUTT)
1761 | You("butt %s.", mon_nam(mon));
1762 | else if (mattk->aatyp == AT_TUCH)
1763 | You("touch %s.", mon_nam(mon));
1764 | else if (mattk->aatyp == AT_TENT)
1765 | Your("tentacles suck %s.", mon_nam(mon));
1766 | else You("hit %s.", mon_nam(mon));
1767 | sum[i] = damageum(mon, mattk);
1768 | } else
1769 | missum(mon, mattk);
1770 | break;
1771 |
1772 | case AT_HUGS:
1773 | /* automatic if prev two attacks succeed, or if
1774 | * already grabbed in a previous attack
1775 | */
1776 | dhit = 1;
1777 | wakeup(mon);
1778 | if (mon->data == &mons[PM_SHADE])
1779 | Your("hug passes harmlessly through %s.",
1780 | mon_nam(mon));
1781 | else if (!sticks(mon->data) && !u.uswallow) {
1782 | if (mon==u.ustuck) {
1783 | pline("%s is being %s.", Monnam(mon),
1784 | u.umonnum==PM_ROPE_GOLEM ? "choked":
1785 | "crushed");
1786 | sum[i] = damageum(mon, mattk);
1787 | } else if(i >= 2 && sum[i-1] && sum[i-2]) {
1788 | You("grab %s!", mon_nam(mon));
1789 | u.ustuck = mon;
1790 | sum[i] = damageum(mon, mattk);
1791 | }
1792 | }
1793 | break;
1794 |
1795 | case AT_EXPL: /* automatic hit if next to */
1796 | dhit = -1;
1797 | wakeup(mon);
1798 | sum[i] = explum(mon, mattk);
1799 | break;
1800 |
1801 | case AT_ENGL:
1802 | if((dhit = (tmp > rnd(20+i)))) {
1803 | wakeup(mon);
1804 | if (mon->data == &mons[PM_SHADE])
1805 | Your("attempt to surround %s is harmless.",
1806 | mon_nam(mon));
1807 | else
1808 | sum[i]= gulpum(mon,mattk);
1809 | } else
1810 | missum(mon, mattk);
1811 | break;
1812 |
1813 | case AT_MAGC:
1814 | /* No check for uwep; if wielding nothing we want to
1815 | * do the normal 1-2 points bare hand damage...
1816 | */
1817 | if (i==0 && (youmonst.data->mlet==S_KOBOLD
1818 | || youmonst.data->mlet==S_ORC
1819 | || youmonst.data->mlet==S_GNOME
1820 | )) goto use_weapon;
1821 |
1822 | case AT_NONE:
1823 | case AT_BOOM:
1824 | continue;
1825 | /* Not break--avoid passive attacks from enemy */
1826 |
1827 | case AT_BREA:
1828 | case AT_SPIT:
1829 | case AT_GAZE: /* all done using #monster command */
1830 | dhit = 0;
1831 | break;
1832 |
1833 | default: /* Strange... */
1834 | impossible("strange attack of yours (%d)",
1835 | mattk->aatyp);
1836 | }
1837 | if (dhit == -1)
1838 | rehumanize();
1839 | if (sum[i] == 2)
1840 | return((boolean)passive(mon, 1, 0, mattk->aatyp));
1841 | /* defender dead */
1842 | else {
1843 | (void) passive(mon, sum[i], 1, mattk->aatyp);
1844 | nsum |= sum[i];
1845 | }
1846 | if (!Upolyd)
1847 | break; /* No extra attacks if no longer a monster */
1848 | if (multi < 0)
1849 | break; /* If paralyzed while attacking, i.e. floating eye */
1850 | }
1851 | return((boolean)(nsum != 0));
1852 | }
1853 |
1854 | /* Special (passive) attacks on you by monsters done here. */
1855 |
1856 | int
1857 | passive(mon, mhit, malive, aatyp)
1858 | register struct monst *mon;
1859 | register boolean mhit;
1860 | register int malive;
1861 | uchar aatyp;
1862 | {
1863 | register struct permonst *ptr = mon->data;
1864 | register int i, tmp;
1865 |
1866 | for(i = 0; ; i++) {
1867 | if(i >= NATTK) return(malive | mhit); /* no passive attacks */
1868 | if(ptr->mattk[i].aatyp == AT_NONE) break; /* try this one */
1869 | }
1870 | /* Note: tmp not always used */
1871 | if (ptr->mattk[i].damn)
1872 | tmp = d((int)ptr->mattk[i].damn, (int)ptr->mattk[i].damd);
1873 | else if(ptr->mattk[i].damd)
1874 | tmp = d((int)mon->m_lev+1, (int)ptr->mattk[i].damd);
1875 | else
1876 | tmp = 0;
1877 |
1878 | /* These affect you even if they just died */
1879 |
1880 | switch(ptr->mattk[i].adtyp) {
1881 |
1882 | case AD_ACID:
1883 | if(mhit && rn2(2)) {
1884 | if (Blind || !flags.verbose) You("are splashed!");
1885 | else You("are splashed by %s acid!",
1886 | s_suffix(mon_nam(mon)));
1887 |
1888 | if (!Acid_resistance)
1889 | mdamageu(mon, tmp);
1890 | if(!rn2(30)) erode_armor(&youmonst, TRUE);
1891 | }
1892 | if(mhit && !rn2(6)) {
1893 | if (aatyp == AT_KICK) {
1894 | if (uarmf)
1895 | (void) rust_dmg(uarmf, xname(uarmf), 3, TRUE, &youmonst);
1896 | } else if (aatyp == AT_WEAP || aatyp == AT_CLAW || aatyp == AT_MAGC || aatyp == AT_TUCH)
1897 | erode_weapon(uwep, TRUE);
1898 | }
1899 | exercise(A_STR, FALSE);
1900 | break;
1901 | case AD_STON:
1902 | if(mhit) {
1903 | /* mhit does not mean you physically hit; it just means the
1904 | attack was successful */
1905 | if ((aatyp == AT_KICK && !uarmf) ||
1906 | ((aatyp == AT_WEAP || aatyp == AT_CLAW || aatyp == AT_MAGC
1907 | || aatyp == AT_TUCH) && !uwep && !uarmg) ||
1908 | aatyp == AT_BITE || aatyp == AT_STNG || aatyp == AT_BUTT ||
1909 | aatyp == AT_TENT || aatyp == AT_HUGS || aatyp == AT_ENGL) {
1910 | if (!Stone_resistance &&
1911 | !(poly_when_stoned(youmonst.data) && polymon(PM_STONE_GOLEM))) {
1912 | You("turn to stone...");
1913 | done_in_by(mon);
1914 | return 2;
1915 | }
1916 | }
1917 | }
1918 | break;
1919 | case AD_RUST:
1920 | if(mhit && !mon->mcan) {
1921 | if (aatyp == AT_KICK) {
1922 | if (uarmf)
1923 | (void) rust_dmg(uarmf, xname(uarmf), 1, TRUE, &youmonst);
1924 | } else if (aatyp == AT_WEAP || aatyp == AT_CLAW ||
1925 | aatyp == AT_MAGC || aatyp == AT_TUCH)
1926 | erode_weapon(uwep, FALSE);
1927 | }
1928 | break;
1929 | case AD_CORRODE:
1930 | if(mhit && !mon->mcan) {
1931 | if (aatyp == AT_KICK) {
1932 | if (uarmf)
1933 | (void) rust_dmg(uarmf, xname(uarmf), 3, TRUE, &youmonst);
1934 | } else if (aatyp == AT_WEAP || aatyp == AT_CLAW ||
1935 | aatyp == AT_MAGC || aatyp == AT_TUCH)
1936 | erode_weapon(uwep, TRUE);
1937 | }
1938 | break;
1939 | case AD_MAGM:
1940 | /* wrath of gods for attacking Oracle */
1941 | if(Antimagic) {
1942 | shieldeff(u.ux, u.uy);
1943 | pline("A hail of magic missiles narrowly misses you!");
1944 | } else {
1945 | You("are hit by magic missiles appearing from thin air!");
1946 | mdamageu(mon, tmp);
1947 | }
1948 | break;
1949 | case AD_ENCH: /* KMH -- remove enchantment (disenchanter) */
1950 | {
1951 | struct obj *obj = (aatyp == AT_KICK) ? uarmf :
1952 | uwep ? uwep : uarmg;
1953 |
1954 | if (mhit && !mon->mcan && obj) {
1955 | if (drain_item(obj) && (obj->known ||
1956 | obj->oclass == ARMOR_CLASS))
1957 | Your("%s less effective.", aobjnam(obj, "seem"));
1958 | }
1959 | break;
1960 | }
1961 | default:
1962 | break;
1963 | }
1964 |
1965 | /* These only affect you if they still live */
1966 |
1967 | if(malive && !mon->mcan && rn2(3)) {
1968 |
1969 | switch(ptr->mattk[i].adtyp) {
1970 |
1971 | case AD_PLYS:
1972 | if(ptr == &mons[PM_FLOATING_EYE]) {
1973 | if (!canseemon(mon)) {
1974 | break;
1975 | }
1976 | if(mon->mcansee) {
1977 | if (ureflects("%s gaze is reflected by your %s.",
1978 | s_suffix(Monnam(mon))))
1979 | ;
1980 | else if (Free_action)
1981 | You("momentarily stiffen under %s gaze!",
1982 | s_suffix(mon_nam(mon)));
1983 | else {
1984 | You("are frozen by %s gaze!",
1985 | s_suffix(mon_nam(mon)));
1986 | nomul((ACURR(A_WIS) > 12 || rn2(4)) ? -tmp : -127);
1987 | }
1988 | } else {
1989 | pline("%s cannot defend itself.",
1990 | Adjmonnam(mon,"blind"));
1991 | if(!rn2(500)) change_luck(-1);
1992 | }
1993 | } else if (Free_action) {
1994 | You("momentarily stiffen.");
1995 | } else { /* gelatinous cube */
1996 | You("are frozen by %s!", mon_nam(mon));
1997 | nomul(-tmp);
1998 | exercise(A_DEX, FALSE);
1999 | }
2000 | break;
2001 | case AD_COLD: /* brown mold or blue jelly */
2002 | if(monnear(mon, u.ux, u.uy)) {
2003 | if(Cold_resistance) {
2004 | shieldeff(u.ux, u.uy);
2005 | You_feel("a mild chill.");
2006 | ugolemeffects(AD_COLD, tmp);
2007 | break;
2008 | }
2009 | You("are suddenly very cold!");
2010 | mdamageu(mon, tmp);
2011 | /* monster gets stronger with your heat! */
2012 | mon->mhp += tmp / 2;
2013 | if (mon->mhpmax < mon->mhp) mon->mhpmax = mon->mhp;
2014 | /* at a certain point, the monster will reproduce! */
2015 | if(mon->mhpmax > ((int) (mon->m_lev+1) * 8))
2016 | (void)split_mon(mon, &youmonst);
2017 | }
2018 | break;
2019 | case AD_STUN: /* specifically yellow mold */
2020 | if(!Stunned)
2021 | make_stunned((long)tmp, TRUE);
2022 | break;
2023 | case AD_FIRE:
2024 | if(monnear(mon, u.ux, u.uy)) {
2025 | if(Fire_resistance) {
2026 | shieldeff(u.ux, u.uy);
2027 | You_feel("mildly warm.");
2028 | ugolemeffects(AD_FIRE, tmp);
2029 | break;
2030 | }
2031 | You("are suddenly very hot!");
2032 | mdamageu(mon, tmp);
2033 | }
2034 | break;
2035 | case AD_ELEC:
2036 | if(Shock_resistance) {
2037 | shieldeff(u.ux, u.uy);
2038 | You_feel("a mild tingle.");
2039 | ugolemeffects(AD_ELEC, tmp);
2040 | break;
2041 | }
2042 | You("are jolted with electricity!");
2043 | mdamageu(mon, tmp);
2044 | break;
2045 | default:
2046 | break;
2047 | }
2048 | }
2049 | return(malive | mhit);
2050 | }
2051 |
2052 | /* Note: caller must ascertain mtmp is mimicking... */
2053 | void
2054 | stumble_onto_mimic(mtmp)
2055 | struct monst *mtmp;
2056 | {
2057 | const char *fmt = "Wait! That's %s!",
2058 | *generic = "a monster",
2059 | *what = 0;
2060 |
2061 | if(!u.ustuck && !mtmp->mflee && dmgtype(mtmp->data,AD_STCK))
2062 | u.ustuck = mtmp;
2063 |
2064 | if (Blind) {
2065 | if (!Blind_telepat)
2066 | what = generic; /* with default fmt */
2067 | else if (mtmp->m_ap_type == M_AP_MONSTER)
2068 | what = a_monnam(mtmp); /* differs from what was sensed */
2069 | } else {
2070 | int glyph = levl[u.ux+u.dx][u.uy+u.dy].glyph;
2071 |
2072 | if (glyph_is_cmap(glyph) &&
2073 | (glyph_to_cmap(glyph) == S_hcdoor ||
2074 | glyph_to_cmap(glyph) == S_vcdoor))
2075 | fmt = "The door actually was %s!";
2076 | else if (glyph_is_object(glyph) &&
2077 | glyph_to_obj(glyph) == GOLD_PIECE)
2078 | fmt = "That gold was %s!";
2079 |
2080 | /* cloned Wiz starts out mimicking some other monster and
2081 | might make himself invisible before being revealed */
2082 | if (mtmp->minvis && !See_invisible)
2083 | what = generic;
2084 | else
2085 | what = a_monnam(mtmp);
2086 | }
2087 | if (what) pline(fmt, what);
2088 |
2089 | wakeup(mtmp); /* clears mimicking */
2090 | }
2091 |
2092 | STATIC_OVL void
2093 | nohandglow(mon)
2094 | struct monst *mon;
2095 | {
2096 | char *hands=makeplural(body_part(HAND));
2097 |
2098 | if (!u.umconf || mon->mconf) return;
2099 | if (u.umconf == 1) {
2100 | if (Blind)
2101 | Your("%s stop tingling.", hands);
2102 | else
2103 | Your("%s stop glowing %s.", hands, hcolor(red));
2104 | } else {
2105 | if (Blind)
2106 | pline_The("tingling in your %s lessens.", hands);
2107 | else
2108 | Your("%s no longer glow so brightly %s.", hands,
2109 | hcolor(red));
2110 | }
2111 | u.umconf--;
2112 | }
2113 |
2114 | int
2115 | flash_hits_mon(mtmp, otmp)
2116 | struct monst *mtmp;
2117 | struct obj *otmp; /* source of flash */
2118 | {
2119 | int tmp, amt, res = 0, useeit = canseemon(mtmp);
2120 |
2121 | if (mtmp->msleeping) {
2122 | mtmp->msleeping = 0;
2123 | if (useeit) {
2124 | pline_The("flash awakens %s.", mon_nam(mtmp));
2125 | res = 1;
2126 | }
2127 | } else if (mtmp->data->mlet != S_LIGHT) {
2128 | if (!resists_blnd(mtmp)) {
2129 | tmp = dist2(otmp->ox, otmp->oy, mtmp->mx, mtmp->my);
2130 | if (useeit) {
2131 | pline("%s is blinded by the flash!", Monnam(mtmp));
2132 | res = 1;
2133 | }
2134 | if (mtmp->data == &mons[PM_GREMLIN]) {
2135 | /* Rule #1: Keep them out of the light. */
2136 | amt = otmp->otyp == WAN_LIGHT ? d(1 + otmp->spe, 4) :
2137 | rn2(min(mtmp->mhp,4));
2138 | pline("%s %s!", Monnam(mtmp), amt > mtmp->mhp / 2 ?
2139 | "wails in agony" : "cries out in pain");
2140 | if ((mtmp->mhp -= amt) <= 0) {
2141 | if (flags.mon_moving)
2142 | monkilled(mtmp, (char *)0, AD_BLND);
2143 | else
2144 | killed(mtmp);
2145 | } else if (cansee(mtmp->mx,mtmp->my) && !canspotmon(mtmp)){
2146 | map_invisible(mtmp->mx, mtmp->my);
2147 | }
2148 | }
2149 | if (mtmp->mhp > 0) {
2150 | if (!flags.mon_moving) setmangry(mtmp);
2151 | if (tmp < 9 && !mtmp->isshk && rn2(4)) {
2152 | mtmp->mflee = 1;
2153 | if (rn2(4)) mtmp->mfleetim = rnd(100);
2154 | }
2155 | mtmp->mcansee = 0;
2156 | mtmp->mblinded = (tmp < 3) ? 0 : rnd(1 + 50/tmp);
2157 | }
2158 | }
2159 | }
2160 | return res;
2161 | }
2162 |
2163 | /*uhitm.c*/