1 | /* SCCS Id: @(#)mhitu.c 3.3 2000/04/19 */
2 | /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 | /* NetHack may be freely redistributed. See license for details. */
4 |
5 | #include "hack.h"
6 | #include "artifact.h"
7 |
8 | STATIC_VAR NEARDATA struct obj *otmp;
9 |
10 | STATIC_DCL void FDECL(urustm, (struct monst *, struct obj *));
11 | # ifdef OVL1
12 | STATIC_DCL boolean FDECL(u_slip_free, (struct monst *,struct attack *));
13 | STATIC_DCL int FDECL(passiveum, (struct permonst *,struct monst *,struct attack *));
14 | # endif /* OVL1 */
15 |
16 | #ifdef OVLB
17 | # ifdef SEDUCE
18 | STATIC_DCL void FDECL(mayberem, (struct obj *, const char *));
19 | # endif
20 | #endif /* OVLB */
21 |
22 | STATIC_DCL boolean FDECL(diseasemu, (struct permonst *));
23 | STATIC_DCL int FDECL(hitmu, (struct monst *,struct attack *));
24 | STATIC_DCL int FDECL(gulpmu, (struct monst *,struct attack *));
25 | STATIC_DCL int FDECL(explmu, (struct monst *,struct attack *,BOOLEAN_P));
26 | STATIC_DCL void FDECL(missmu,(struct monst *,BOOLEAN_P,struct attack *));
27 | STATIC_DCL void FDECL(mswings,(struct monst *,struct obj *));
28 | STATIC_DCL void FDECL(wildmiss, (struct monst *,struct attack *));
29 |
30 | STATIC_DCL void FDECL(hurtarmor,(int));
31 | STATIC_DCL void FDECL(hitmsg,(struct monst *,struct attack *));
32 |
33 | /* See comment in mhitm.c. If we use this a lot it probably should be */
34 | /* changed to a parameter to mhitu. */
35 | static int dieroll;
36 |
37 | #ifdef OVL1
38 |
39 |
40 | STATIC_OVL void
41 | hitmsg(mtmp, mattk)
42 | register struct monst *mtmp;
43 | register struct attack *mattk;
44 | {
45 | int compat;
46 |
47 | /* Note: if opposite gender, "seductively" */
48 | /* If same gender, "engagingly" for nymph, normal msg for others */
49 | if((compat = could_seduce(mtmp, &youmonst, mattk))
50 | && !mtmp->mcan && !mtmp->mspec_used) {
51 | pline("%s %s you %s.", Monnam(mtmp),
52 | Blind ? "talks to" : "smiles at",
53 | compat == 2 ? "engagingly" : "seductively");
54 | } else
55 | switch (mattk->aatyp) {
56 | case AT_BITE:
57 | pline("%s bites!", Monnam(mtmp));
58 | break;
59 | case AT_KICK:
60 | pline("%s kicks%c", Monnam(mtmp),
61 | thick_skinned(youmonst.data) ? '.' : '!');
62 | break;
63 | case AT_STNG:
64 | pline("%s stings!", Monnam(mtmp));
65 | break;
66 | case AT_BUTT:
67 | pline("%s butts!", Monnam(mtmp));
68 | break;
69 | case AT_TUCH:
70 | pline("%s touches you!", Monnam(mtmp));
71 | break;
72 | case AT_TENT:
73 | pline("%s tentacles suck you!",
74 | s_suffix(Monnam(mtmp)));
75 | break;
76 | case AT_EXPL:
77 | case AT_BOOM:
78 | pline("%s explodes!", Monnam(mtmp));
79 | break;
80 | default:
81 | pline("%s hits!", Monnam(mtmp));
82 | }
83 | }
84 |
85 | STATIC_OVL void
86 | missmu(mtmp, nearmiss, mattk) /* monster missed you */
87 | register struct monst *mtmp;
88 | register boolean nearmiss;
89 | register struct attack *mattk;
90 | {
91 | if (!canspotmon(mtmp))
92 | map_invisible(mtmp->mx, mtmp->my);
93 |
94 | if(could_seduce(mtmp, &youmonst, mattk) && !mtmp->mcan)
95 | pline("%s pretends to be friendly.", Monnam(mtmp));
96 | else {
97 | if (!flags.verbose || !nearmiss)
98 | pline("%s misses.", Monnam(mtmp));
99 | else
100 | pline("%s just misses!", Monnam(mtmp));
101 | }
102 | }
103 |
104 | STATIC_OVL void
105 | mswings(mtmp, otemp) /* monster swings obj */
106 | register struct monst *mtmp;
107 | register struct obj *otemp;
108 | {
109 | if (!flags.verbose || Blind || !mon_visible(mtmp))
110 | return;
111 | pline("%s %s %s %s.", Monnam(mtmp),
112 | (objects[otemp->otyp].oc_dir & PIERCE) ? "thrusts" : "swings",
113 | his[pronoun_gender(mtmp)], xname(otemp));
114 | }
115 |
116 | /* return how a poison attack was delivered */
117 | const char *
118 | mpoisons_subj(mtmp, mattk)
119 | struct monst *mtmp;
120 | struct attack *mattk;
121 | {
122 | if (mattk->aatyp == AT_WEAP) {
123 | struct obj *mwep = (mtmp == &youmonst) ? uwep : MON_WEP(mtmp);
124 | /* "Foo's attack was poisoned." is pretty lame, but at least
125 | it's better than "sting" when not a stinging attack... */
126 | return (!mwep || !mwep->opoisoned) ? "attack" : "weapon";
127 | } else {
128 | return (mattk->aatyp == AT_TUCH) ? "contact" :
129 | (mattk->aatyp == AT_GAZE) ? "gaze" :
130 | (mattk->aatyp == AT_BITE) ? "bite" : "sting";
131 | }
132 | }
133 |
134 | /* called when your intrinsic speed is taken away */
135 | void
136 | u_slow_down()
137 | {
138 | HFast = 0L;
139 | if (!Fast)
140 | You("slow down.");
141 | else /* speed boots */
142 | Your("quickness feels less natural.");
143 | exercise(A_DEX, FALSE);
144 | }
145 |
146 | #endif /* OVL1 */
147 | #ifdef OVLB
148 |
149 | STATIC_OVL void
150 | wildmiss(mtmp, mattk) /* monster attacked your displaced image */
151 | register struct monst *mtmp;
152 | register struct attack *mattk;
153 | {
154 | int compat;
155 |
156 | /* no map_invisible() -- no way to tell where _this_ is coming from */
157 |
158 | if (!flags.verbose) return;
159 | if (!cansee(mtmp->mx, mtmp->my)) return;
160 | /* maybe it's attacking an image around the corner? */
161 |
162 | compat = (mattk->adtyp == AD_SEDU || mattk->adtyp == AD_SSEX) &&
163 | could_seduce(mtmp, &youmonst, (struct attack *)0);
164 |
165 | if (!mtmp->mcansee || (Invis && !perceives(mtmp->data))) {
166 | const char *swings =
167 | mattk->aatyp == AT_BITE ? "snaps" :
168 | mattk->aatyp == AT_KICK ? "kicks" :
169 | (mattk->aatyp == AT_STNG ||
170 | mattk->aatyp == AT_BUTT ||
171 | nolimbs(mtmp->data)) ? "lunges" : "swings";
172 |
173 | if (compat)
174 | pline("%s tries to touch you and misses!", Monnam(mtmp));
175 | else
176 | switch(rn2(3)) {
177 | case 0: pline("%s %s wildly and misses!", Monnam(mtmp),
178 | swings);
179 | break;
180 | case 1: pline("%s attacks a spot beside you.", Monnam(mtmp));
181 | break;
182 | case 2: pline("%s strikes at %s!", Monnam(mtmp),
183 | levl[mtmp->mux][mtmp->muy].typ == WATER
184 | ? "empty water" : "thin air");
185 | break;
186 | default:pline("%s %s wildly!", Monnam(mtmp), swings);
187 | break;
188 | }
189 |
190 | } else if (Displaced) {
191 | if (compat)
192 | pline("%s smiles %s at your %sdisplaced image...",
193 | Monnam(mtmp),
194 | compat == 2 ? "engagingly" : "seductively",
195 | Invis ? "invisible " : "");
196 | else
197 | pline("%s strikes at your %sdisplaced image and misses you!",
198 | /* Note: if you're both invisible and displaced,
199 | * only monsters which see invisible will attack your
200 | * displaced image, since the displaced image is also
201 | * invisible.
202 | */
203 | Monnam(mtmp),
204 | Invis ? "invisible " : "");
205 |
206 | } else if (Underwater) {
207 | /* monsters may miss especially on water level where
208 | bubbles shake the player here and there */
209 | if (compat)
210 | pline("%s reaches towards your distorted image.",Monnam(mtmp));
211 | else
212 | pline("%s is fooled by water reflections and misses!",Monnam(mtmp));
213 |
214 | } else impossible("%s attacks you without knowing your location?",
215 | Monnam(mtmp));
216 | }
217 |
218 | void
219 | expels(mtmp, mdat, message)
220 | register struct monst *mtmp;
221 | register struct permonst *mdat; /* if mtmp is polymorphed, mdat != mtmp->data */
222 | boolean message;
223 | {
224 | if (message) {
225 | if (is_animal(mdat))
226 | You("get regurgitated!");
227 | else {
228 | char blast[40];
229 | register int i;
230 |
231 | blast[0] = '\0';
232 | for(i = 0; i < NATTK; i++)
233 | if(mdat->mattk[i].aatyp == AT_ENGL)
234 | break;
235 | if (mdat->mattk[i].aatyp != AT_ENGL)
236 | impossible("Swallower has no engulfing attack?");
237 | else {
238 | if (is_whirly(mdat)) {
239 | switch (mdat->mattk[i].adtyp) {
240 | case AD_ELEC:
241 | Strcpy(blast,
242 | " in a shower of sparks");
243 | break;
244 | case AD_COLD:
245 | Strcpy(blast,
246 | " in a blast of frost");
247 | break;
248 | }
249 | } else
250 | Strcpy(blast, " with a squelch");
251 | You("get expelled from %s%s!",
252 | mon_nam(mtmp), blast);
253 | }
254 | }
255 | }
256 | unstuck(mtmp); /* ball&chain returned in unstuck() */
257 | mnexto(mtmp);
258 | newsym(u.ux,u.uy);
259 | spoteffects(TRUE);
260 | /* to cover for a case where mtmp is not in a next square */
261 | if(um_dist(mtmp->mx,mtmp->my,1))
262 | pline("Brrooaa... You land hard at some distance.");
263 | }
264 |
265 | #endif /* OVLB */
266 | #ifdef OVL0
267 |
268 | /*
269 | * mattacku: monster attacks you
270 | * returns 1 if monster dies (e.g. "yellow light"), 0 otherwise
271 | * Note: if you're displaced or invisible the monster might attack the
272 | * wrong position...
273 | * Assumption: it's attacking you or an empty square; if there's another
274 | * monster which it attacks by mistake, the caller had better
275 | * take care of it...
276 | */
277 | int
278 | mattacku(mtmp)
279 | register struct monst *mtmp;
280 | {
281 | struct attack *mattk;
282 | int i, j, tmp, sum[NATTK];
283 | struct permonst *mdat = mtmp->data;
284 | boolean ranged = (distu(mtmp->mx, mtmp->my) > 3);
285 | /* Is it near you? Affects your actions */
286 | boolean range2 = !monnear(mtmp, mtmp->mux, mtmp->muy);
287 | /* Does it think it's near you? Affects its actions */
288 | boolean foundyou = (mtmp->mux==u.ux && mtmp->muy==u.uy);
289 | /* Is it attacking you or your image? */
290 | boolean youseeit = canseemon(mtmp);
291 | /* Might be attacking your image around the corner, or
292 | * invisible, or you might be blind....
293 | */
294 |
295 | if(!ranged) nomul(0);
296 | if(mtmp->mhp <= 0 || (Underwater && !is_swimmer(mtmp->data)))
297 | return(0);
298 |
299 | /* If swallowed, can only be affected by u.ustuck */
300 | if(u.uswallow) {
301 | if(mtmp != u.ustuck)
302 | return(0);
303 | u.ustuck->mux = u.ux;
304 | u.ustuck->muy = u.uy;
305 | range2 = 0;
306 | foundyou = 1;
307 | if(u.uinvulnerable) return (0); /* stomachs can't hurt you! */
308 | }
309 |
310 | #ifdef STEED
311 | else if (u.usteed) {
312 | if (mtmp == u.usteed)
313 | /* Your steed won't attack you */
314 | return (0);
315 | /* Orcs like to steal and eat horses and the like */
316 | if (!rn2(is_orc(mtmp->data) ? 2 : 4) &&
317 | distu(mtmp->mx, mtmp->my) <= 2) {
318 | /* Attack your steed instead */
319 | i = mattackm(mtmp, u.usteed);
320 | if ((i & MM_AGR_DIED))
321 | return (1);
322 | if (i & MM_DEF_DIED || u.ux != u.ux0 || u.uy != u.uy0)
323 | return (0);
324 | /* Let your steed retaliate */
325 | return (!!(mattackm(u.usteed, mtmp) & MM_DEF_DIED));
326 | }
327 | }
328 | #endif
329 |
330 | if (u.uundetected && !range2 && foundyou && !u.uswallow) {
331 | u.uundetected = 0;
332 | if (is_hider(youmonst.data)) {
333 | coord cc; /* maybe we need a unexto() function? */
334 |
335 | You("fall from the %s!", ceiling(u.ux,u.uy));
336 | if (enexto(&cc, u.ux, u.uy, youmonst.data)) {
337 | remove_monster(mtmp->mx, mtmp->my);
338 | newsym(mtmp->mx,mtmp->my);
339 | place_monster(mtmp, u.ux, u.uy);
340 | if(mtmp->wormno) worm_move(mtmp);
341 | teleds(cc.x, cc.y);
342 | set_apparxy(mtmp);
343 | newsym(u.ux,u.uy);
344 | } else {
345 | pline("%s is killed by a falling %s (you)!",
346 | Monnam(mtmp), youmonst.data->mname);
347 | killed(mtmp);
348 | newsym(u.ux,u.uy);
349 | if (mtmp->mhp > 0) return 0;
350 | else return 1;
351 | }
352 | if (youmonst.data->mlet != S_PIERCER)
353 | return(0); /* trappers don't attack */
354 |
355 | if (which_armor(mtmp, WORN_HELMET)) {
356 | Your("blow glances off %s helmet.",
357 | s_suffix(mon_nam(mtmp)));
358 | } else {
359 | if (3 + find_mac(mtmp) <= rnd(20)) {
360 | pline("%s is hit by a falling piercer (you)!",
361 | Monnam(mtmp));
362 | if ((mtmp->mhp -= d(3,6)) < 1)
363 | killed(mtmp);
364 | } else
365 | pline("%s is almost hit by a falling piercer (you)!",
366 | Monnam(mtmp));
367 | }
368 | } else {
369 | if (!youseeit)
370 | pline("It tries to move where you are hiding.");
371 | else {
372 | /* Ugly kludge for eggs. The message is phrased so as
373 | * to be directed at the monster, not the player,
374 | * which makes "laid by you" wrong. For the
375 | * parallelism to work, we can't rephrase it, so we
376 | * zap the "laid by you" momentarily instead.
377 | */
378 | struct obj *obj = level.objects[u.ux][u.uy];
379 |
380 | if (obj ||
381 | (youmonst.data->mlet == S_EEL && is_pool(u.ux, u.uy))) {
382 | int save_spe = 0; /* suppress warning */
383 | if (obj) {
384 | save_spe = obj->spe;
385 | if (obj->otyp == EGG) obj->spe = 0;
386 | }
387 | if (youmonst.data->mlet == S_EEL)
388 | pline("Wait, %s! There's a hidden %s named %s there!",
389 | m_monnam(mtmp), youmonst.data->mname, plname);
390 | else
391 | pline("Wait, %s! There's a %s named %s hiding under %s!",
392 | m_monnam(mtmp), youmonst.data->mname, plname,
393 | doname(level.objects[u.ux][u.uy]));
394 | if (obj) obj->spe = save_spe;
395 | } else
396 | impossible("hiding under nothing?");
397 | }
398 | newsym(u.ux,u.uy);
399 | }
400 | return(0);
401 | }
402 | if (youmonst.data->mlet == S_MIMIC && youmonst.m_ap_type && !range2 && foundyou && !u.uswallow) {
403 | if (!youseeit) pline("It gets stuck on you.");
404 | else pline("Wait, %s! That's a %s named %s!",
405 | m_monnam(mtmp), youmonst.data->mname, plname);
406 | u.ustuck = mtmp;
407 | youmonst.m_ap_type = M_AP_NOTHING;
408 | youmonst.mappearance = 0;
409 | newsym(u.ux,u.uy);
410 | return(0);
411 | }
412 |
413 | /* player might be mimicking gold */
414 | if (youmonst.m_ap_type == M_AP_OBJECT
415 | && youmonst.mappearance == GOLD_PIECE
416 | && !range2 && foundyou && !u.uswallow) {
417 | if (!youseeit)
418 | pline("%s %s!", Something, likes_gold(mtmp->data) ?
419 | "tries to pick you up" : "disturbs you");
420 | else pline("Wait, %s! That gold is really %s named %s!",
421 | m_monnam(mtmp),
422 | an(mons[u.umonnum].mname),
423 | plname);
424 | if (multi < 0) { /* this should always be the case */
425 | char buf[BUFSZ];
426 | Sprintf(buf, "You appear to be %s again.",
427 | Upolyd ? (const char *) an(youmonst.data->mname) :
428 | (const char *) "yourself");
429 | unmul(buf); /* immediately stop mimicking gold */
430 | }
431 | return 0;
432 | }
433 |
434 | /* Work out the armor class differential */
435 | tmp = AC_VALUE(u.uac) + 10; /* tmp ~= 0 - 20 */
436 | tmp += mtmp->m_lev;
437 | if(multi < 0) tmp += 4;
438 | if((Invis && !perceives(mdat)) || !mtmp->mcansee)
439 | tmp -= 2;
440 | if(mtmp->mtrapped) tmp -= 2;
441 | if(tmp <= 0) tmp = 1;
442 |
443 | /* make eels visible the moment they hit/miss us */
444 | if(mdat->mlet == S_EEL && mtmp->minvis && cansee(mtmp->mx,mtmp->my)) {
445 | mtmp->minvis = 0;
446 | newsym(mtmp->mx,mtmp->my);
447 | }
448 |
449 | /* Special demon handling code */
450 | if(!mtmp->cham && is_demon(mdat) && !range2
451 | && mtmp->data != &mons[PM_BALROG]
452 | && mtmp->data != &mons[PM_SUCCUBUS]
453 | && mtmp->data != &mons[PM_INCUBUS])
454 | if(!mtmp->mcan && !rn2(13)) msummon(mdat);
455 |
456 | /* Special lycanthrope handling code */
457 | if(!mtmp->cham && is_were(mdat) && !range2) {
458 |
459 | if(is_human(mdat)) {
460 | if(!rn2(5 - (night() * 2)) && !mtmp->mcan) new_were(mtmp);
461 | } else if(!rn2(30) && !mtmp->mcan) new_were(mtmp);
462 | mdat = mtmp->data;
463 |
464 | if(!rn2(10) && !mtmp->mcan) {
465 | if(youseeit) {
466 | pline("%s summons help!", Monnam(mtmp));
467 | } else
468 | You_feel("hemmed in.");
469 | /* Technically wrong; we really should check if you can see the
470 | * help, but close enough...
471 | */
472 | if (!were_summon(mdat,FALSE) && youseeit)
473 | pline("But none comes.");
474 | }
475 | }
476 |
477 | if(u.uinvulnerable) {
478 | /* monster's won't attack you */
479 | if(mtmp == u.ustuck)
480 | pline("%s loosens its grip slightly.", Monnam(mtmp));
481 | else if(!range2) {
482 | if(youseeit)
483 | pline("%s starts to attack you, but pulls back.",
484 | Monnam(mtmp));
485 | else
486 | You_feel("%s move nearby.", something);
487 | }
488 | return (0);
489 | }
490 |
491 | /* Unlike defensive stuff, don't let them use item _and_ attack. */
492 | if(find_offensive(mtmp)) {
493 | int foo = use_offensive(mtmp);
494 |
495 | if (foo != 0) return(foo==1);
496 | }
497 |
498 | for(i = 0; i < NATTK; i++) {
499 |
500 | sum[i] = 0;
501 | mattk = &(mdat->mattk[i]);
502 | if (u.uswallow && (mattk->aatyp != AT_ENGL))
503 | continue;
504 | switch(mattk->aatyp) {
505 | case AT_CLAW: /* "hand to hand" attacks */
506 | case AT_KICK:
507 | case AT_BITE:
508 | case AT_STNG:
509 | case AT_TUCH:
510 | case AT_BUTT:
511 | case AT_TENT:
512 | if(!range2 && (!MON_WEP(mtmp) || mtmp->mconf || Conflict ||
513 | !touch_petrifies(youmonst.data))) {
514 | if (foundyou) {
515 | if(tmp > (j = rnd(20+i))) {
516 | if (mattk->aatyp != AT_KICK ||
517 | !thick_skinned(youmonst.data))
518 | sum[i] = hitmu(mtmp, mattk);
519 | } else
520 | missmu(mtmp, (tmp == j), mattk);
521 | } else
522 | wildmiss(mtmp, mattk);
523 | }
524 | break;
525 |
526 | case AT_HUGS: /* automatic if prev two attacks succeed */
527 | /* Note: if displaced, prev attacks never succeeded */
528 | if((!range2 && i>=2 && sum[i-1] && sum[i-2])
529 | || mtmp == u.ustuck)
530 | sum[i]= hitmu(mtmp, mattk);
531 | break;
532 |
533 | case AT_GAZE: /* can affect you either ranged or not */
534 | /* Medusa gaze already operated through m_respond in
535 | * dochug(); don't gaze more than once per round.
536 | */
537 | if (mdat != &mons[PM_MEDUSA])
538 | sum[i] = gazemu(mtmp, mattk);
539 | break;
540 |
541 | case AT_EXPL: /* automatic hit if next to, and aimed at you */
542 | if(!range2) sum[i] = explmu(mtmp, mattk, foundyou);
543 | break;
544 |
545 | case AT_ENGL:
546 | if (!range2) {
547 | if(foundyou) {
548 | if(u.uswallow || tmp > (j = rnd(20+i))) {
549 | /* Force swallowing monster to be
550 | * displayed even when player is
551 | * moving away */
552 | flush_screen(1);
553 | sum[i] = gulpmu(mtmp, mattk);
554 | } else {
555 | missmu(mtmp, (tmp == j), mattk);
556 | }
557 | } else if (is_animal(mtmp->data))
558 | pline("%s gulps some air!", youseeit ?
559 | Monnam(mtmp) : "It");
560 | else
561 | if (youseeit)
562 | pline("%s lunges forward and recoils!",
563 | Monnam(mtmp));
564 | else
565 | You_hear("a %s nearby.",
566 | is_whirly(mtmp->data)?
567 | "rushing noise" :
568 | "splat");
569 | }
570 | break;
571 | case AT_BREA:
572 | if(range2) sum[i] = breamu(mtmp, mattk);
573 | /* Note: breamu takes care of displacement */
574 | break;
575 | case AT_SPIT:
576 | if(range2) sum[i] = spitmu(mtmp, mattk);
577 | /* Note: spitmu takes care of displacement */
578 | break;
579 | case AT_WEAP:
580 | if(range2) {
581 | #ifdef REINCARNATION
582 | if (!Is_rogue_level(&u.uz))
583 | #endif
584 | thrwmu(mtmp);
585 | } else {
586 | int hittmp = 0;
587 |
588 | /* Rare but not impossible. Normally the monster
589 | * wields when 2 spaces away, but it can be
590 | * teleported or whatever....
591 | */
592 | if (mtmp->weapon_check == NEED_WEAPON ||
593 | !MON_WEP(mtmp)) {
594 | mtmp->weapon_check = NEED_HTH_WEAPON;
595 | /* mon_wield_item resets weapon_check as
596 | * appropriate */
597 | if (mon_wield_item(mtmp) != 0) break;
598 | }
599 | if (foundyou) {
600 | possibly_unwield(mtmp);
601 | otmp = MON_WEP(mtmp);
602 | if(otmp) {
603 | hittmp = hitval(otmp, &youmonst);
604 | tmp += hittmp;
605 | mswings(mtmp, otmp);
606 | }
607 | if(tmp > (j = dieroll = rnd(20+i)))
608 | sum[i] = hitmu(mtmp, mattk);
609 | else
610 | missmu(mtmp, (tmp == j), mattk);
611 | /* KMH -- Don't accumulate to-hit bonuses */
612 | if (otmp)
613 | tmp -= hittmp;
614 | } else
615 | wildmiss(mtmp, mattk);
616 | }
617 | break;
618 | case AT_MAGC:
619 | if (range2)
620 | sum[i] = buzzmu(mtmp, mattk);
621 | else
622 | if (foundyou)
623 | sum[i] = castmu(mtmp, mattk);
624 | else
625 | pline("%s casts a spell at %s!",
626 | youseeit ? Monnam(mtmp) : "It",
627 | levl[mtmp->mux][mtmp->muy].typ == WATER
628 | ? "empty water" : "thin air");
629 | /* FIXME: castmu includes spells that are not
630 | * cast at the player and thus should be
631 | * possible whether the monster knows your
632 | * location or not.
633 | * --KAA
634 | */
635 | break;
636 |
637 | default: /* no attack */
638 | break;
639 | }
640 | if(flags.botl) bot();
641 | /* give player a chance of waking up before dying -kaa */
642 | if(sum[i] == 1) { /* successful attack */
643 | if (u.usleep && u.usleep < monstermoves && !rn2(10)) {
644 | multi = -1;
645 | nomovemsg = "The combat suddenly awakens you.";
646 | }
647 | }
648 | if(sum[i] == 2) return 1; /* attacker dead */
649 | if(sum[i] == 3) break; /* attacker teleported, no more attacks */
650 | /* sum[i] == 0: unsuccessful attack */
651 | }
652 | return(0);
653 | }
654 |
655 | #endif /* OVL0 */
656 | #ifdef OVLB
657 |
658 | /*
659 | * helper function for some compilers that have trouble with hitmu
660 | */
661 |
662 | STATIC_OVL void
663 | hurtarmor(attk)
664 | int attk;
665 | {
666 | int hurt;
667 |
668 | switch(attk) {
669 | /* 0 is burning, which we should never be called with */
670 | case AD_RUST: hurt = 1; break;
671 | case AD_CORRODE: hurt = 3; break;
672 | default: hurt = 2; break;
673 | }
674 |
675 | /* What the following code does: it keeps looping until it
676 | * finds a target for the rust monster.
677 | * Head, feet, etc... not covered by metal, or covered by
678 | * rusty metal, are not targets. However, your body always
679 | * is, no matter what covers it.
680 | */
681 | while (1) {
682 | switch(rn2(5)) {
683 | case 0:
684 | if (!uarmh || !rust_dmg(uarmh, xname(uarmh), hurt, FALSE, &youmonst))
685 | continue;
686 | break;
687 | case 1:
688 | if (uarmc) {
689 | (void)rust_dmg(uarmc, xname(uarmc), hurt, TRUE, &youmonst);
690 | break;
691 | }
692 | /* Note the difference between break and continue;
693 | * break means it was hit and didn't rust; continue
694 | * means it wasn't a target and though it didn't rust
695 | * something else did.
696 | */
697 | if (uarm)
698 | (void)rust_dmg(uarm, xname(uarm), hurt, TRUE, &youmonst);
699 | #ifdef TOURIST
700 | else if (uarmu)
701 | (void)rust_dmg(uarmu, xname(uarmu), hurt, TRUE, &youmonst);
702 | #endif
703 | break;
704 | case 2:
705 | if (!uarms || !rust_dmg(uarms, xname(uarms), hurt, FALSE, &youmonst))
706 | continue;
707 | break;
708 | case 3:
709 | if (!uarmg || !rust_dmg(uarmg, xname(uarmg), hurt, FALSE, &youmonst))
710 | continue;
711 | break;
712 | case 4:
713 | if (!uarmf || !rust_dmg(uarmf, xname(uarmf), hurt, FALSE, &youmonst))
714 | continue;
715 | break;
716 | }
717 | break; /* Out of while loop */
718 | }
719 | }
720 |
721 | #endif /* OVLB */
722 | #ifdef OVL1
723 |
724 | STATIC_OVL boolean
725 | diseasemu(mdat)
726 | struct permonst *mdat;
727 | {
728 | if (Sick_resistance) {
729 | You_feel("a slight illness.");
730 | return FALSE;
731 | } else {
732 | make_sick(Sick ? Sick/3L + 1L : (long)rn1(ACURR(A_CON), 20),
733 | mdat->mname, TRUE, SICK_NONVOMITABLE);
734 | return TRUE;
735 | }
736 | }
737 |
738 | /* check whether slippery clothing protects from hug or wrap attack */
739 | STATIC_OVL boolean
740 | u_slip_free(mtmp, mattk)
741 | struct monst *mtmp;
742 | struct attack *mattk;
743 | {
744 | struct obj *obj = (uarmc ? uarmc : uarm);
745 |
746 | #ifdef TOURIST
747 | if (!obj) obj = uarmu;
748 | #endif
749 | if (mattk->adtyp == AD_DRIN) obj = uarmh;
750 |
751 | /* if your cloak/armor is greased, monster slips off; this
752 | protection might fail (33% chance) when the armor is cursed */
753 | if (obj && (obj->greased || obj->otyp == OILSKIN_CLOAK) &&
754 | (!obj->cursed || rn2(3))) {
755 | pline("%s %s your %s %s!",
756 | Monnam(mtmp),
757 | (mattk->adtyp == AD_WRAP) ?
758 | "slips off of" : "grabs you, but cannot hold onto",
759 | obj->greased ? "greased" : "slippery",
760 | /* avoid "slippery slippery cloak"
761 | for undiscovered oilskin cloak */
762 | (obj->greased || objects[obj->otyp].oc_name_known) ?
763 | xname(obj) : "cloak");
764 |
765 | if (obj->greased && !rn2(2)) {
766 | pline_The("grease wears off.");
767 | obj->greased = 0;
768 | }
769 | return TRUE;
770 | }
771 | return FALSE;
772 | }
773 |
774 | /*
775 | * hitmu: monster hits you
776 | * returns 2 if monster dies (e.g. "yellow light"), 1 otherwise
777 | * 3 if the monster lives but teleported/paralyzed, so it can't keep
778 | * attacking you
779 | */
780 | STATIC_OVL int
781 | hitmu(mtmp, mattk)
782 | register struct monst *mtmp;
783 | register struct attack *mattk;
784 | {
785 | register struct permonst *mdat = mtmp->data;
786 | register int uncancelled, ptmp;
787 | int dmg, armpro;
788 | char buf[BUFSZ];
789 | struct permonst *olduasmon = youmonst.data;
790 | int res;
791 |
792 | if (!canspotmon(mtmp))
793 | map_invisible(mtmp->mx, mtmp->my);
794 |
795 | /* If the monster is undetected & hits you, you should know where
796 | * the attack came from.
797 | */
798 | if(mtmp->mundetected && (hides_under(mdat) || mdat->mlet == S_EEL)) {
799 | mtmp->mundetected = 0;
800 | if (!(Blind ? Blind_telepat : Unblind_telepat)) {
801 | struct obj *obj;
802 | const char *what;
803 |
804 | if ((obj = level.objects[mtmp->mx][mtmp->my]) != 0) {
805 | if (Blind && !obj->dknown)
806 | what = something;
807 | else if (is_pool(mtmp->mx, mtmp->my) && !Underwater)
808 | what = "the water";
809 | else
810 | what = doname(obj);
811 |
812 | pline("%s was hidden under %s!", Amonnam(mtmp), what);
813 | }
814 | newsym(mtmp->mx, mtmp->my);
815 | }
816 | }
817 |
818 | /* First determine the base damage done */
819 | dmg = d((int)mattk->damn, (int)mattk->damd);
820 | if(is_undead(mdat) && midnight())
821 | dmg += d((int)mattk->damn, (int)mattk->damd); /* extra damage */
822 |
823 | /* Next a cancellation factor */
824 | /* Use uncancelled when the cancellation factor takes into account certain
825 | * armor's special magic protection. Otherwise just use !mtmp->mcan.
826 | */
827 | armpro = 0;
828 | if (uarm && armpro < objects[uarm->otyp].a_can)
829 | armpro = objects[uarm->otyp].a_can;
830 | if (uarmc && armpro < objects[uarmc->otyp].a_can)
831 | armpro = objects[uarmc->otyp].a_can;
832 | if (uarmh && armpro < objects[uarmh->otyp].a_can)
833 | armpro = objects[uarmh->otyp].a_can;
834 | uncancelled = !mtmp->mcan && ((rn2(3) >= armpro) || !rn2(50));
835 |
836 | /* Now, adjust damages via resistances or specific attacks */
837 | switch(mattk->adtyp) {
838 | case AD_PHYS:
839 | if (mattk->aatyp == AT_HUGS && !sticks(youmonst.data)) {
840 | if(!u.ustuck && rn2(2)) {
841 | if (u_slip_free(mtmp, mattk)) {
842 | dmg = 0;
843 | } else {
844 | u.ustuck = mtmp;
845 | pline("%s grabs you!", Monnam(mtmp));
846 | }
847 | } else if(u.ustuck == mtmp) {
848 | exercise(A_STR, FALSE);
849 | You("are being %s.",
850 | (mtmp->data == &mons[PM_ROPE_GOLEM])
851 | ? "choked" : "crushed");
852 | }
853 | } else { /* hand to hand weapon */
854 | if(mattk->aatyp == AT_WEAP && otmp) {
855 | if (otmp->otyp == CORPSE
856 | && touch_petrifies(&mons[otmp->corpsenm])) {
857 | dmg = 1;
858 | pline("%s hits you with the %s corpse.",
859 | Monnam(mtmp), mons[otmp->corpsenm].mname);
860 | if (!Stoned)
861 | goto do_stone;
862 | }
863 | dmg += dmgval(otmp, &youmonst);
864 | if (dmg <= 0) dmg = 1;
865 | if (!(otmp->oartifact &&
866 | artifact_hit(mtmp, &youmonst, otmp, &dmg,dieroll)))
867 | hitmsg(mtmp, mattk);
868 | if (!dmg) break;
869 | if (u.mh > 1 && u.mh > ((u.uac>0) ? dmg : dmg+u.uac) &&
870 | (u.umonnum==PM_BLACK_PUDDING
871 | || u.umonnum==PM_BROWN_PUDDING)) {
872 | /* This redundancy necessary because you have to
873 | * take the damage _before_ being cloned.
874 | */
875 | if (u.uac < 0) dmg += u.uac;
876 | if (dmg < 1) dmg = 1;
877 | if (dmg > 1) exercise(A_STR, FALSE);
878 | u.mh -= dmg;
879 | flags.botl = 1;
880 | dmg = 0;
881 | if(cloneu())
882 | You("divide as %s hits you!",mon_nam(mtmp));
883 | }
884 | urustm(mtmp, otmp);
885 | } else if (mattk->aatyp != AT_TUCH || dmg != 0 ||
886 | mtmp != u.ustuck)
887 | hitmsg(mtmp, mattk);
888 | }
889 | break;
890 | case AD_DISE:
891 | hitmsg(mtmp, mattk);
892 | if (!diseasemu(mdat)) dmg = 0;
893 | break;
894 | case AD_FIRE:
895 | hitmsg(mtmp, mattk);
896 | if (uncancelled) {
897 | pline("You're %s!",
898 | mattk->aatyp == AT_HUGS ? "being roasted" :
899 | "on fire");
900 | if (youmonst.data == &mons[PM_STRAW_GOLEM] ||
901 | youmonst.data == &mons[PM_PAPER_GOLEM]) {
902 | You("roast!");
903 | /* KMH -- this is okay with unchanging */
904 | rehumanize();
905 | break;
906 | } else if (Fire_resistance) {
907 | pline_The("fire doesn't feel hot!");
908 | dmg = 0;
909 | }
910 | if((int) mtmp->m_lev > rn2(20))
911 | destroy_item(SCROLL_CLASS, AD_FIRE);
912 | if((int) mtmp->m_lev > rn2(20))
913 | destroy_item(POTION_CLASS, AD_FIRE);
914 | if((int) mtmp->m_lev > rn2(25))
915 | destroy_item(SPBOOK_CLASS, AD_FIRE);
916 | burn_away_slime();
917 | } else dmg = 0;
918 | break;
919 | case AD_COLD:
920 | hitmsg(mtmp, mattk);
921 | if (uncancelled) {
922 | pline("You're covered in frost!");
923 | if (Cold_resistance) {
924 | pline_The("frost doesn't seem cold!");
925 | dmg = 0;
926 | }
927 | if((int) mtmp->m_lev > rn2(20))
928 | destroy_item(POTION_CLASS, AD_COLD);
929 | } else dmg = 0;
930 | break;
931 | case AD_ELEC:
932 | hitmsg(mtmp, mattk);
933 | if (uncancelled) {
934 | You("get zapped!");
935 | if (Shock_resistance) {
936 | pline_The("zap doesn't shock you!");
937 | dmg = 0;
938 | }
939 | if((int) mtmp->m_lev > rn2(20))
940 | destroy_item(WAND_CLASS, AD_ELEC);
941 | if((int) mtmp->m_lev > rn2(20))
942 | destroy_item(RING_CLASS, AD_ELEC);
943 | } else dmg = 0;
944 | break;
945 | case AD_SLEE:
946 | hitmsg(mtmp, mattk);
947 | if (uncancelled && multi >= 0 && !rn2(5)) {
948 | if (Sleep_resistance) break;
949 | fall_asleep(-rnd(10), TRUE);
950 | if (Blind) You("are put to sleep!");
951 | else You("are put to sleep by %s!", mon_nam(mtmp));
952 | }
953 | break;
954 | case AD_BLND:
955 | if (can_blnd(mtmp, &youmonst, mattk->aatyp, (struct obj*)0)) {
956 | if (!Blind) pline("%s blinds you!", Monnam(mtmp));
957 | make_blinded(Blinded+(long)dmg,FALSE);
958 | }
959 | dmg = 0;
960 | break;
961 | case AD_DRST:
962 | ptmp = A_STR;
963 | goto dopois;
964 | case AD_DRDX:
965 | ptmp = A_DEX;
966 | goto dopois;
967 | case AD_DRCO:
968 | ptmp = A_CON;
969 | dopois:
970 | hitmsg(mtmp, mattk);
971 | if (uncancelled && !rn2(8)) {
972 | Sprintf(buf, "%s %s",
973 | !canspotmon(mtmp) ? "Its" :
974 | Hallucination ? s_suffix(rndmonnam()) :
975 | s_suffix(mdat->mname),
976 | mpoisons_subj(mtmp, mattk));
977 | poisoned(buf, ptmp, mdat->mname, 30);
978 | }
979 | break;
980 | case AD_DRIN:
981 | hitmsg(mtmp, mattk);
982 | if (defends(AD_DRIN, uwep) || !has_head(youmonst.data)) {
983 | You("don't seem harmed.");
984 | break;
985 | }
986 | if (u_slip_free(mtmp,mattk)) break;
987 |
988 | if (uarmh && rn2(8)) {
989 | /* not body_part(HEAD) */
990 | Your("helmet blocks the attack to your head.");
991 | break;
992 | }
993 | if (Half_physical_damage) dmg = (dmg+1) / 2;
994 | mdamageu(mtmp, dmg);
995 |
996 | if (!uarmh || uarmh->otyp != DUNCE_CAP) {
997 | Your("brain is eaten!");
998 | /* No such thing as mindless players... */
999 | if (ABASE(A_INT) <= ATTRMIN(A_INT)) {
1000 | int lifesaved = 0;
1001 | struct obj *wore_amulet = uamul;
1002 |
1003 | while(1) {
1004 | /* avoid looping on "die(y/n)?" */
1005 | if (lifesaved && (discover || wizard)) {
1006 | if (wore_amulet && !uamul) {
1007 | /* used up AMULET_OF_LIFE_SAVING; still
1008 | subject to dying from brainlessness */
1009 | wore_amulet = 0;
1010 | } else {
1011 | /* explicitly chose not to die;
1012 | arbitrarily boost intelligence */
1013 | ABASE(A_INT) = ATTRMIN(A_INT) + 2;
1014 | You_feel("like a scarecrow.");
1015 | break;
1016 | }
1017 | }
1018 |
1019 | if (lifesaved)
1020 | pline("Unfortunately your brain is still gone.");
1021 | else
1022 | Your("last thought fades away.");
1023 | killer = "brainlessness";
1024 | killer_format = KILLED_BY;
1025 | done(DIED);
1026 | lifesaved++;
1027 | }
1028 | }
1029 | }
1030 | /* adjattrib gives dunce cap message when appropriate */
1031 | (void) adjattrib(A_INT, -rnd(2), FALSE);
1032 | forget_levels(25); /* lose memory of 25% of levels */
1033 | forget_objects(25); /* lose memory of 25% of objects */
1034 | exercise(A_WIS, FALSE);
1035 | break;
1036 | case AD_PLYS:
1037 | hitmsg(mtmp, mattk);
1038 | if (uncancelled && multi >= 0 && !rn2(3)) {
1039 | if (Free_action) You("momentarily stiffen.");
1040 | else {
1041 | if (Blind) You("are frozen!");
1042 | else You("are frozen by %s!", mon_nam(mtmp));
1043 | nomovemsg = 0; /* default: "you can move again" */
1044 | nomul(-rnd(10));
1045 | exercise(A_DEX, FALSE);
1046 | }
1047 | }
1048 | break;
1049 | case AD_DRLI:
1050 | hitmsg(mtmp, mattk);
1051 | if (uncancelled && !rn2(3) && !Drain_resistance) {
1052 | losexp("life drainage");
1053 | }
1054 | break;
1055 | case AD_LEGS:
1056 | { register long side = rn2(2) ? RIGHT_SIDE : LEFT_SIDE;
1057 | const char *sidestr = (side == RIGHT_SIDE) ? "right" : "left";
1058 |
1059 | /* This case is too obvious to ignore, but Nethack is not in
1060 | * general very good at considering height--most short monsters
1061 | * still _can_ attack you when you're flying or mounted.
1062 | */
1063 | if (
1064 | #ifdef STEED
1065 | u.usteed ||
1066 | #endif
1067 | Levitation || Flying) {
1068 | pline("%s tries to reach your %s %s!", Monnam(mtmp),
1069 | sidestr, body_part(LEG));
1070 | } else if (mtmp->mcan) {
1071 | pline("%s nuzzles against your %s %s!", Monnam(mtmp),
1072 | sidestr, body_part(LEG));
1073 | } else {
1074 | if (uarmf) {
1075 | if (rn2(2) && (uarmf->otyp == LOW_BOOTS ||
1076 | uarmf->otyp == IRON_SHOES))
1077 | pline("%s pricks the exposed part of your %s %s!",
1078 | Monnam(mtmp), sidestr, body_part(LEG));
1079 | else if (!rn2(5))
1080 | pline("%s pricks through your %s boot!",
1081 | Monnam(mtmp), sidestr);
1082 | else {
1083 | pline("%s scratches your %s boot!", Monnam(mtmp),
1084 | sidestr);
1085 | break;
1086 | }
1087 | } else pline("%s pricks your %s %s!", Monnam(mtmp),
1088 | sidestr, body_part(LEG));
1089 | set_wounded_legs(side, rnd(60-ACURR(A_DEX)));
1090 | exercise(A_STR, FALSE);
1091 | exercise(A_DEX, FALSE);
1092 | }
1093 | break;
1094 | }
1095 | case AD_STON: /* cockatrice */
1096 | hitmsg(mtmp, mattk);
1097 | if(!rn2(3) && !Stoned) {
1098 | if (mtmp->mcan) {
1099 | if (flags.soundok)
1100 | You_hear("a cough from %s!", mon_nam(mtmp));
1101 | } else {
1102 | if (flags.soundok)
1103 | You_hear("%s hissing!", s_suffix(mon_nam(mtmp)));
1104 | if(!rn2(10) ||
1105 | (flags.moonphase == NEW_MOON && !have_lizard())) {
1106 | do_stone:
1107 | if (!Stone_resistance
1108 | && !(poly_when_stoned(youmonst.data) &&
1109 | polymon(PM_STONE_GOLEM))) {
1110 | Stoned = 5;
1111 | killer_format = KILLED_BY_AN;
1112 | delayed_killer = mtmp->data->mname;
1113 | return(1);
1114 | /* You("turn to stone..."); */
1115 | /* done_in_by(mtmp); */
1116 | }
1117 | }
1118 | }
1119 | }
1120 | break;
1121 | case AD_STCK:
1122 | hitmsg(mtmp, mattk);
1123 | if (uncancelled && !u.ustuck && !sticks(youmonst.data))
1124 | u.ustuck = mtmp;
1125 | break;
1126 | case AD_WRAP:
1127 | if ((!mtmp->mcan || u.ustuck == mtmp) && !sticks(youmonst.data)) {
1128 | if (!u.ustuck && !rn2(10)) {
1129 | if (u_slip_free(mtmp, mattk)) {
1130 | dmg = 0;
1131 | } else {
1132 | pline("%s swings itself around you!",
1133 | Monnam(mtmp));
1134 | u.ustuck = mtmp;
1135 | }
1136 | } else if(u.ustuck == mtmp) {
1137 | if (is_pool(mtmp->mx,mtmp->my) && !Swimming
1138 | && !Amphibious) {
1139 | boolean moat = (levl[u.ux][u.uy].typ != POOL) &&
1140 | (levl[u.ux][u.uy].typ != WATER) &&
1141 | !Is_medusa_level(&u.uz) &&
1142 | !Is_waterlevel(&u.uz);
1143 |
1144 | pline("%s drowns you...", Monnam(mtmp));
1145 | killer_format = KILLED_BY_AN;
1146 | Sprintf(buf, "%s by %s",
1147 | moat ? "moat" : "pool of water",
1148 | a_monnam(mtmp));
1149 | killer = buf;
1150 | done(DROWNING);
1151 | } else if(mattk->aatyp == AT_HUGS)
1152 | You("are being crushed.");
1153 | } else {
1154 | dmg = 0;
1155 | if(flags.verbose)
1156 | pline("%s brushes against your %s.", Monnam(mtmp),
1157 | body_part(LEG));
1158 | }
1159 | } else dmg = 0;
1160 | break;
1161 | case AD_WERE:
1162 | hitmsg(mtmp, mattk);
1163 | if (uncancelled && !rn2(4) && u.ulycn == NON_PM &&
1164 | !Protection_from_shape_changers &&
1165 | !defends(AD_WERE,uwep)) {
1166 | You_feel("feverish.");
1167 | exercise(A_CON, FALSE);
1168 | u.ulycn = monsndx(mdat);
1169 | }
1170 | break;
1171 | case AD_SGLD:
1172 | hitmsg(mtmp, mattk);
1173 | if (youmonst.data->mlet == mdat->mlet) break;
1174 | if(!mtmp->mcan) stealgold(mtmp);
1175 | break;
1176 |
1177 | case AD_SITM: /* for now these are the same */
1178 | case AD_SEDU:
1179 | if (is_animal(mtmp->data)) {
1180 | hitmsg(mtmp, mattk);
1181 | if (mtmp->mcan) break;
1182 | /* Continue below */
1183 | } else if (dmgtype(youmonst.data, AD_SEDU)
1184 | #ifdef SEDUCE
1185 | || dmgtype(youmonst.data, AD_SSEX)
1186 | #endif
1187 | ) {
1188 | pline("%s %s.", Monnam(mtmp), mtmp->minvent ?
1189 | "brags about the goods some dungeon explorer provided" :
1190 | "makes some remarks about how difficult theft is lately");
1191 | if (!tele_restrict(mtmp)) rloc(mtmp);
1192 | return 3;
1193 | } else if (mtmp->mcan) {
1194 | if (!Blind) {
1195 | pline("%s tries to %s you, but you seem %s.",
1196 | Adjmonnam(mtmp, "plain"),
1197 | flags.female ? "charm" : "seduce",
1198 | flags.female ? "unaffected" : "uninterested");
1199 | }
1200 | if(rn2(3)) {
1201 | if (!tele_restrict(mtmp)) rloc(mtmp);
1202 | return 3;
1203 | }
1204 | break;
1205 | }
1206 | switch (steal(mtmp)) {
1207 | case -1:
1208 | return 2;
1209 | case 0:
1210 | break;
1211 | default:
1212 | if (!is_animal(mtmp->data) && !tele_restrict(mtmp))
1213 | rloc(mtmp);
1214 | mtmp->mflee = 1;
1215 | return 3;
1216 | }
1217 | break;
1218 | #ifdef SEDUCE
1219 | case AD_SSEX:
1220 | if(could_seduce(mtmp, &youmonst, mattk) == 1
1221 | && !mtmp->mcan)
1222 | if (doseduce(mtmp))
1223 | return 3;
1224 | break;
1225 | #endif
1226 | case AD_SAMU:
1227 | hitmsg(mtmp, mattk);
1228 | /* when the Wiz hits, 1/20 steals the amulet */
1229 | if (u.uhave.amulet ||
1230 | u.uhave.bell || u.uhave.book || u.uhave.menorah
1231 | || u.uhave.questart) /* carrying the Quest Artifact */
1232 | if (!rn2(20)) stealamulet(mtmp);
1233 | break;
1234 |
1235 | case AD_TLPT:
1236 | hitmsg(mtmp, mattk);
1237 | if (uncancelled) {
1238 | if(flags.verbose)
1239 | Your("position suddenly seems very uncertain!");
1240 | tele();
1241 | }
1242 | break;
1243 | case AD_RUST:
1244 | hitmsg(mtmp, mattk);
1245 | if (mtmp->mcan) break;
1246 | if (u.umonnum == PM_IRON_GOLEM) {
1247 | You("rust!");
1248 | /* KMH -- this is okay with unchanging */
1249 | rehumanize();
1250 | break;
1251 | }
1252 | hurtarmor(AD_RUST);
1253 | break;
1254 | case AD_CORRODE:
1255 | hitmsg(mtmp, mattk);
1256 | if (mtmp->mcan) break;
1257 | hurtarmor(AD_CORRODE);
1258 | break;
1259 | case AD_DCAY:
1260 | hitmsg(mtmp, mattk);
1261 | if (mtmp->mcan) break;
1262 | if (u.umonnum == PM_WOOD_GOLEM ||
1263 | u.umonnum == PM_LEATHER_GOLEM) {
1264 | You("rot!");
1265 | /* KMH -- this is okay with unchanging */
1266 | rehumanize();
1267 | break;
1268 | }
1269 | hurtarmor(AD_DCAY);
1270 | break;
1271 | case AD_HEAL:
1272 | if(!uwep
1273 | #ifdef TOURIST
1274 | && !uarmu
1275 | #endif
1276 | && !uarm && !uarmh && !uarms && !uarmg && !uarmc && !uarmf) {
1277 | boolean goaway = FALSE;
1278 | pline("%s hits! (I hope you don't mind.)", Monnam(mtmp));
1279 | if (Upolyd) {
1280 | u.mh += rnd(7);
1281 | if (!rn2(7)) {
1282 | /* no upper limit necessary; effect is temporary */
1283 | u.mhmax++;
1284 | if (!rn2(13)) goaway = TRUE;
1285 | }
1286 | if (u.mh > u.mhmax) u.mh = u.mhmax;
1287 | } else {
1288 | u.uhp += rnd(7);
1289 | if (!rn2(7)) {
1290 | /* hard upper limit via nurse care: 25 * ulevel */
1291 | if (u.uhpmax < 5 * u.ulevel + d(2 * u.ulevel, 10))
1292 | u.uhpmax++;
1293 | if (!rn2(13)) goaway = TRUE;
1294 | }
1295 | if (u.uhp > u.uhpmax) u.uhp = u.uhpmax;
1296 | }
1297 | if (!rn2(3)) exercise(A_STR, TRUE);
1298 | if (!rn2(3)) exercise(A_CON, TRUE);
1299 | if (Sick) make_sick(0L, (char *) 0, FALSE, SICK_ALL);
1300 | flags.botl = 1;
1301 | if (goaway) {
1302 | mongone(mtmp);
1303 | return 2;
1304 | } else if (!rn2(33)) {
1305 | if (!tele_restrict(mtmp)) rloc(mtmp);
1306 | if (!mtmp->mflee) {
1307 | mtmp->mflee = 1;
1308 | mtmp->mfleetim = d(3,6);
1309 | }
1310 | return 3;
1311 | }
1312 | dmg = 0;
1313 | } else {
1314 | if (Role_if(PM_HEALER)) {
1315 | if (flags.soundok && !(moves % 5))
1316 | verbalize("Doc, I can't help you unless you cooperate.");
1317 | dmg = 0;
1318 | } else hitmsg(mtmp, mattk);
1319 | }
1320 | break;
1321 | case AD_CURS:
1322 | hitmsg(mtmp, mattk);
1323 | if(!night() && mdat == &mons[PM_GREMLIN]) break;
1324 | if(!mtmp->mcan && !rn2(10)) {
1325 | if (flags.soundok) {
1326 | if (Blind) You_hear("laughter.");
1327 | else pline("%s chuckles.", Monnam(mtmp));
1328 | }
1329 | if (u.umonnum == PM_CLAY_GOLEM) {
1330 | pline("Some writing vanishes from your head!");
1331 | /* KMH -- this is okay with unchanging */
1332 | rehumanize();
1333 | break;
1334 | }
1335 | attrcurse();
1336 | }
1337 | break;
1338 | case AD_STUN:
1339 | hitmsg(mtmp, mattk);
1340 | if(!mtmp->mcan && !rn2(4)) {
1341 | make_stunned(HStun + dmg, TRUE);
1342 | dmg /= 2;
1343 | }
1344 | break;
1345 | case AD_ACID:
1346 | hitmsg(mtmp, mattk);
1347 | if (!mtmp->mcan && !rn2(3))
1348 | if (Acid_resistance) {
1349 | pline("You're covered in acid, but it seems harmless.");
1350 | dmg = 0;
1351 | } else {
1352 | pline("You're covered in acid! It burns!");
1353 | exercise(A_STR, FALSE);
1354 | }
1355 | else dmg = 0;
1356 | break;
1357 | case AD_SLOW:
1358 | hitmsg(mtmp, mattk);
1359 | if (uncancelled && HFast &&
1360 | !defends(AD_SLOW, uwep) && !rn2(4))
1361 | u_slow_down();
1362 | break;
1363 | case AD_DREN:
1364 | hitmsg(mtmp, mattk);
1365 | if (uncancelled && !rn2(4))
1366 | drain_en(dmg);
1367 | dmg = 0;
1368 | break;
1369 | case AD_CONF:
1370 | hitmsg(mtmp, mattk);
1371 | if(!mtmp->mcan && !rn2(4) && !mtmp->mspec_used) {
1372 | mtmp->mspec_used = mtmp->mspec_used + (dmg + rn2(6));
1373 | if(Confusion)
1374 | You("are getting even more confused.");
1375 | else You("are getting confused.");
1376 | make_confused(HConfusion + dmg, FALSE);
1377 | }
1378 | /* fall through to next case */
1379 | case AD_DETH:
1380 | pline("%s reaches out with its deadly touch.", Monnam(mtmp));
1381 | if (is_undead(youmonst.data)) {
1382 | /* Still does normal damage */
1383 | pline("Was that the touch of death?");
1384 | break;
1385 | }
1386 | if(!Antimagic && rn2(20) > 16) {
1387 | killer_format = KILLED_BY_AN;
1388 | killer = "touch of death";
1389 | done(DIED);
1390 | } else {
1391 | if(!rn2(5)) {
1392 | if(Antimagic) shieldeff(u.ux, u.uy);
1393 | pline("Lucky for you, it didn't work!");
1394 | dmg = 0;
1395 | } else You_feel("your life force draining away...");
1396 | }
1397 | break;
1398 | case AD_PEST:
1399 | pline("%s reaches out, and you feel fever and chills.",
1400 | Monnam(mtmp));
1401 | (void) diseasemu(mdat); /* plus the normal damage */
1402 | break;
1403 | case AD_FAMN:
1404 | pline("%s reaches out, and your body shrivels.",
1405 | Monnam(mtmp));
1406 | exercise(A_CON, FALSE);
1407 | if (!is_fainted()) morehungry(rn1(40,40));
1408 | /* plus the normal damage */
1409 | break;
1410 | case AD_SLIM:
1411 | hitmsg(mtmp, mattk);
1412 | if (!uncancelled) break;
1413 | if (youmonst.data == &mons[PM_FIRE_VORTEX] ||
1414 | youmonst.data == &mons[PM_FIRE_ELEMENTAL]) {
1415 | pline_The("slime burns away!");
1416 | dmg = 0;
1417 | } else if (Unchanging ||
1418 | youmonst.data == &mons[PM_GREEN_SLIME]) {
1419 | You("are unaffected.");
1420 | dmg = 0;
1421 | } else if (!Slimed) {
1422 | You("don't feel very well.");
1423 | Slimed = 10L;
1424 | killer_format = KILLED_BY_AN;
1425 | delayed_killer = mtmp->data->mname;
1426 | } else
1427 | pline("Yuck!");
1428 | break;
1429 | case AD_ENCH: /* KMH -- remove enchantment (disenchanter) */
1430 | hitmsg(mtmp, mattk);
1431 | /* uncancelled is sufficient enough; please
1432 | don't make this attack less frequent */
1433 | if (uncancelled) {
1434 | struct obj *obj = some_armor(&youmonst);
1435 |
1436 | if (drain_item(obj)) {
1437 | Your("%s less effective.", aobjnam(obj, "seem"));
1438 | }
1439 | }
1440 | break;
1441 | default: dmg = 0;
1442 | break;
1443 | }
1444 | if(u.uhp < 1) done_in_by(mtmp);
1445 |
1446 | /* Negative armor class reduces damage done instead of fully protecting
1447 | * against hits.
1448 | */
1449 | if (dmg && u.uac < 0) {
1450 | dmg -= rnd(-u.uac);
1451 | if (dmg < 1) dmg = 1;
1452 | }
1453 |
1454 | if(dmg) {
1455 | if (Half_physical_damage
1456 | /* Mitre of Holiness */
1457 | || (Role_if(PM_PRIEST) && uarmh && is_quest_artifact(uarmh) &&
1458 | (is_undead(mtmp->data) || is_demon(mtmp->data))))
1459 | dmg = (dmg+1) / 2;
1460 | mdamageu(mtmp, dmg);
1461 | }
1462 |
1463 | if (dmg) {
1464 | res = passiveum(olduasmon, mtmp, mattk);
1465 | stop_occupation();
1466 | return res;
1467 | } else
1468 | return 1;
1469 | }
1470 |
1471 | #endif /* OVL1 */
1472 | #ifdef OVLB
1473 |
1474 | STATIC_OVL int
1475 | gulpmu(mtmp, mattk) /* monster swallows you, or damage if u.uswallow */
1476 | register struct monst *mtmp;
1477 | register struct attack *mattk;
1478 | {
1479 | struct trap *t = t_at(u.ux, u.uy);
1480 | int tmp = d((int)mattk->damn, (int)mattk->damd);
1481 | int tim_tmp;
1482 | register struct obj *otmp2;
1483 | int i;
1484 |
1485 | if (!u.uswallow) { /* swallows you */
1486 | if (youmonst.data->msize >= MZ_HUGE) return(0);
1487 | if ((t && ((t->ttyp == PIT) || (t->ttyp == SPIKED_PIT))) &&
1488 | sobj_at(BOULDER, u.ux, u.uy))
1489 | return(0);
1490 |
1491 | if (Punished) unplacebc(); /* ball&chain go away */
1492 | remove_monster(mtmp->mx, mtmp->my);
1493 | mtmp->mtrapped = 0; /* no longer on old trap */
1494 | place_monster(mtmp, u.ux, u.uy);
1495 | u.ustuck = mtmp;
1496 | newsym(mtmp->mx,mtmp->my);
1497 | #ifdef STEED
1498 | if (is_animal(mtmp->data) && u.usteed) {
1499 | char buf[BUFSZ];
1500 | /* Too many quirks presently if hero and steed
1501 | * are swallowed. Pretend purple worms don't
1502 | * like horses for now :-)
1503 | */
1504 | Strcpy(buf, mon_nam(u.usteed));
1505 | pline ("%s lunges forward and plucks you off %s!",
1506 | Monnam(mtmp), buf);
1507 | dismount_steed(DISMOUNT_ENGULFED);
1508 | } else
1509 | #endif
1510 | pline("%s engulfs you!", Monnam(mtmp));
1511 | stop_occupation();
1512 | reset_occupations(); /* behave as if you had moved */
1513 |
1514 | if (u.utrap) {
1515 | You("are released from the %s!",
1516 | u.utraptype==TT_WEB ? "web" : "trap");
1517 | u.utrap = 0;
1518 | }
1519 |
1520 | i = number_leashed();
1521 | if (i > 0) {
1522 | pline_The("leash%s snap%s loose.",
1523 | (i > 1) ? "es" : "",
1524 | (i > 1) ? "" : "s");
1525 | unleash_all();
1526 | }
1527 |
1528 | if (touch_petrifies(youmonst.data) && !resists_ston(mtmp)) {
1529 | minstapetrify(mtmp, TRUE);
1530 | if (mtmp->mhp > 0) return 0;
1531 | else return 2;
1532 | }
1533 |
1534 | display_nhwindow(WIN_MESSAGE, FALSE);
1535 | vision_recalc(2); /* hero can't see anything */
1536 | u.uswallow = 1;
1537 | /* u.uswldtim always set > 1 */
1538 | tim_tmp = 25 - (int)mtmp->m_lev;
1539 | if (tim_tmp > 0) tim_tmp = rnd(tim_tmp) / 2;
1540 | else if (tim_tmp < 0) tim_tmp = -(rnd(-tim_tmp) / 2);
1541 | tim_tmp += -u.uac + 10;
1542 | u.uswldtim = (unsigned)((tim_tmp < 2) ? 2 : tim_tmp);
1543 | swallowed(1);
1544 | for (otmp2 = invent; otmp2; otmp2 = otmp2->nobj)
1545 | (void) snuff_lit(otmp2);
1546 | }
1547 |
1548 | if (mtmp != u.ustuck) return(0);
1549 | if (u.uswldtim > 0) u.uswldtim -= 1;
1550 |
1551 | switch(mattk->adtyp) {
1552 |
1553 | case AD_DGST:
1554 | if (Slow_digestion) {
1555 | /* Messages are handled below */
1556 | u.uswldtim = 0;
1557 | tmp = 0;
1558 | } else if (u.uswldtim == 0) {
1559 | pline("%s totally digests you!", Monnam(mtmp));
1560 | tmp = u.uhp;
1561 | if (Half_physical_damage) tmp *= 2; /* sorry */
1562 | } else {
1563 | pline("%s%s digests you!", Monnam(mtmp),
1564 | (u.uswldtim == 2) ? " thoroughly" :
1565 | (u.uswldtim == 1) ? " utterly" : "");
1566 | exercise(A_STR, FALSE);
1567 | }
1568 | break;
1569 | case AD_PHYS:
1570 | You("are pummeled with debris!");
1571 | exercise(A_STR, FALSE);
1572 | break;
1573 | case AD_ACID:
1574 | if (Acid_resistance) {
1575 | You("are covered with a seemingly harmless goo.");
1576 | tmp = 0;
1577 | } else {
1578 | if (Hallucination) pline("Ouch! You've been slimed!");
1579 | else You("are covered in slime! It burns!");
1580 | exercise(A_STR, FALSE);
1581 | }
1582 | break;
1583 | case AD_BLND:
1584 | if (can_blnd(mtmp, &youmonst, mattk->aatyp, (struct obj*)0)) {
1585 | if(!Blind) {
1586 | You_cant("see in here!");
1587 | make_blinded((long)tmp,FALSE);
1588 | } else
1589 | /* keep him blind until disgorged */
1590 | make_blinded(Blinded+1,FALSE);
1591 | }
1592 | tmp = 0;
1593 | break;
1594 | case AD_ELEC:
1595 | if(!mtmp->mcan && rn2(2)) {
1596 | pline_The("air around you crackles with electricity.");
1597 | if (Shock_resistance) {
1598 | shieldeff(u.ux, u.uy);
1599 | You("seem unhurt.");
1600 | ugolemeffects(AD_ELEC,tmp);
1601 | tmp = 0;
1602 | }
1603 | } else tmp = 0;
1604 | break;
1605 | case AD_COLD:
1606 | if(!mtmp->mcan && rn2(2)) {
1607 | if (Cold_resistance) {
1608 | shieldeff(u.ux, u.uy);
1609 | You_feel("mildly chilly.");
1610 | ugolemeffects(AD_COLD,tmp);
1611 | tmp = 0;
1612 | } else You("are freezing to death!");
1613 | } else tmp = 0;
1614 | break;
1615 | case AD_FIRE:
1616 | if(!mtmp->mcan && rn2(2)) {
1617 | if (Fire_resistance) {
1618 | shieldeff(u.ux, u.uy);
1619 | You_feel("mildly hot.");
1620 | ugolemeffects(AD_FIRE,tmp);
1621 | tmp = 0;
1622 | } else You("are burning to a crisp!");
1623 | burn_away_slime();
1624 | } else tmp = 0;
1625 | break;
1626 | case AD_DISE:
1627 | if (!diseasemu(mtmp->data)) tmp = 0;
1628 | break;
1629 | default:
1630 | tmp = 0;
1631 | break;
1632 | }
1633 |
1634 | if (Half_physical_damage) tmp = (tmp+1) / 2;
1635 |
1636 | mdamageu(mtmp, tmp);
1637 | if (tmp) stop_occupation();
1638 |
1639 | if (touch_petrifies(youmonst.data) && !resists_ston(mtmp)) {
1640 | pline("%s very hurriedly %s you!", Monnam(mtmp),
1641 | is_animal(mtmp->data)? "regurgitates" : "expels");
1642 | expels(mtmp, mtmp->data, FALSE);
1643 | } else if (!u.uswldtim || youmonst.data->msize >= MZ_HUGE) {
1644 | You("get %s!", is_animal(mtmp->data)? "regurgitated" : "expelled");
1645 | if (flags.verbose && (is_animal(mtmp->data) ||
1646 | (dmgtype(mtmp->data, AD_DGST) && Slow_digestion)))
1647 | pline("Obviously %s doesn't like your taste.", mon_nam(mtmp));
1648 | expels(mtmp, mtmp->data, FALSE);
1649 | }
1650 | return(1);
1651 | }
1652 |
1653 | STATIC_OVL int
1654 | explmu(mtmp, mattk, ufound) /* monster explodes in your face */
1655 | register struct monst *mtmp;
1656 | register struct attack *mattk;
1657 | boolean ufound;
1658 | {
1659 | if (mtmp->mcan) return(0);
1660 |
1661 | if (!ufound)
1662 | pline("%s explodes at a spot in %s!",
1663 | canseemon(mtmp) ? Monnam(mtmp) : "It",
1664 | levl[mtmp->mux][mtmp->muy].typ == WATER
1665 | ? "empty water" : "thin air");
1666 | else {
1667 | register int tmp = d((int)mattk->damn, (int)mattk->damd);
1668 | register boolean not_affected = defends((int)mattk->adtyp, uwep);
1669 |
1670 | hitmsg(mtmp, mattk);
1671 |
1672 | switch (mattk->adtyp) {
1673 | case AD_COLD:
1674 | not_affected |= Cold_resistance;
1675 | goto common;
1676 | case AD_FIRE:
1677 | not_affected |= Fire_resistance;
1678 | goto common;
1679 | case AD_ELEC:
1680 | not_affected |= Shock_resistance;
1681 | common:
1682 |
1683 | if (!not_affected) {
1684 | if (ACURR(A_DEX) > rnd(20)) {
1685 | You("duck some of the blast.");
1686 | tmp = (tmp+1) / 2;
1687 | } else {
1688 | if (flags.verbose) You("get blasted!");
1689 | }
1690 | if (mattk->adtyp == AD_FIRE) burn_away_slime();
1691 | if (Half_physical_damage) tmp = (tmp+1) / 2;
1692 | mdamageu(mtmp, tmp);
1693 | }
1694 | break;
1695 |
1696 | case AD_BLND:
1697 | not_affected = resists_blnd(&youmonst);
1698 | if (!not_affected) {
1699 | /* sometimes you're affected even if it's invisible */
1700 | if (mon_visible(mtmp) || (rnd(tmp /= 2) > u.ulevel)) {
1701 | You("are blinded by a blast of light!");
1702 | make_blinded((long)tmp, FALSE);
1703 | } else if (flags.verbose)
1704 | You("get the impression it was not terribly bright.");
1705 | }
1706 | break;
1707 |
1708 | case AD_HALU:
1709 | not_affected |= Blind ||
1710 | (u.umonnum == PM_BLACK_LIGHT ||
1711 | u.umonnum == PM_VIOLET_FUNGUS ||
1712 | dmgtype(youmonst.data, AD_STUN));
1713 | if (!not_affected) {
1714 | if (!Hallucination)
1715 | You("are freaked by a blast of kaleidoscopic light!");
1716 | make_hallucinated(HHallucination + (long)tmp,FALSE,0L);
1717 | }
1718 | break;
1719 |
1720 | default:
1721 | break;
1722 | }
1723 | if (not_affected) {
1724 | You("seem unaffected by it.");
1725 | ugolemeffects((int)mattk->adtyp, tmp);
1726 | }
1727 | }
1728 | mondead(mtmp);
1729 | if (mtmp->mhp > 0) return(0);
1730 | return(2); /* it dies */
1731 | }
1732 |
1733 | int
1734 | gazemu(mtmp, mattk) /* monster gazes at you */
1735 | register struct monst *mtmp;
1736 | register struct attack *mattk;
1737 | {
1738 | switch(mattk->adtyp) {
1739 | case AD_STON:
1740 | if (mtmp->mcan) {
1741 | if (mtmp->data == &mons[PM_MEDUSA] && canseemon(mtmp))
1742 | pline("%s doesn't look all that ugly.", Monnam(mtmp));
1743 | break;
1744 | }
1745 | if(Reflecting && mtmp->mcansee &&
1746 | !mtmp->mcan && mtmp->data == &mons[PM_MEDUSA]) {
1747 | if(!Blind) {
1748 | (void) ureflects("%s gaze is reflected by your %s.",
1749 | s_suffix(Monnam(mtmp)));
1750 | if (mon_reflects(mtmp,
1751 | "The gaze is reflected away by %s %s!"))
1752 | break;
1753 | if (!m_canseeu(mtmp)) { /* probably you're invisible */
1754 | pline("%s doesn't seem to notice that %s gaze was reflected.",
1755 | Monnam(mtmp),
1756 | his[pronoun_gender(mtmp)]);
1757 | break;
1758 | }
1759 | pline("%s is turned to stone!", Monnam(mtmp));
1760 | }
1761 | stoned = TRUE;
1762 | killed(mtmp);
1763 |
1764 | if (mtmp->mhp > 0) break;
1765 | return 2;
1766 | }
1767 | if (canseemon(mtmp) && !Stone_resistance) {
1768 | You("meet %s gaze.", s_suffix(mon_nam(mtmp)));
1769 | if(poly_when_stoned(youmonst.data) && polymon(PM_STONE_GOLEM))
1770 | break;
1771 | You("turn to stone...");
1772 | killer_format = KILLED_BY;
1773 | killer = mons[PM_MEDUSA].mname;
1774 | done(STONING);
1775 | }
1776 | break;
1777 | case AD_CONF:
1778 | if(!mtmp->mcan && canseemon(mtmp) && mtmp->mcansee &&
1779 | !mtmp->mspec_used && rn2(5)) {
1780 | int conf = d(3,4);
1781 |
1782 | mtmp->mspec_used = mtmp->mspec_used + (conf + rn2(6));
1783 | if(!Confusion)
1784 | pline("%s gaze confuses you!",
1785 | s_suffix(Monnam(mtmp)));
1786 | else
1787 | You("are getting more and more confused.");
1788 | make_confused(HConfusion + conf, FALSE);
1789 | }
1790 | break;
1791 | case AD_STUN:
1792 | if(!mtmp->mcan && canseemon(mtmp) && mtmp->mcansee &&
1793 | !mtmp->mspec_used && rn2(5)) {
1794 | int stun = d(2,6);
1795 |
1796 | mtmp->mspec_used = mtmp->mspec_used + (stun + rn2(6));
1797 | make_stunned(HStun + stun, TRUE);
1798 | pline("%s stares piercingly at you!", Monnam(mtmp));
1799 | }
1800 | break;
1801 | case AD_BLND:
1802 | if (!mtmp->mcan && canseemon(mtmp) && !resists_blnd(&youmonst)
1803 | && distu(mtmp->mx,mtmp->my) <= BOLT_LIM*BOLT_LIM) {
1804 | int blnd = d((int)mattk->damn, (int)mattk->damd);
1805 | make_blinded((long)blnd,FALSE);
1806 | make_stunned((long)d(1,3),TRUE);
1807 | You("are blinded by %s radiance!",
1808 | s_suffix(mon_nam(mtmp)));
1809 | }
1810 | break;
1811 | case AD_FIRE:
1812 | if(!mtmp->mcan && canseemon(mtmp) && mtmp->mcansee &&
1813 | !mtmp->mspec_used && rn2(5)) {
1814 | int dmg = d(2,6);
1815 | pline("%s attacks you with a fiery gaze!",
1816 | Monnam(mtmp));
1817 | if (Fire_resistance) {
1818 | pline_The("fire doesn't feel hot!");
1819 | dmg = 0;
1820 | }
1821 | burn_away_slime();
1822 | if((int) mtmp->m_lev > rn2(20))
1823 | destroy_item(SCROLL_CLASS, AD_FIRE);
1824 | if((int) mtmp->m_lev > rn2(20))
1825 | destroy_item(POTION_CLASS, AD_FIRE);
1826 | if((int) mtmp->m_lev > rn2(25))
1827 | destroy_item(SPBOOK_CLASS, AD_FIRE);
1828 | if (dmg) mdamageu(mtmp, dmg);
1829 | }
1830 | break;
1831 | #ifdef PM_BEHOLDER /* work in progress */
1832 | case AD_SLEE:
1833 | if(!mtmp->mcan && canseemon(mtmp) && mtmp->mcansee &&
1834 | multi >= 0 && !rn2(5) && !Sleep_resistance) {
1835 | fall_asleep(-rnd(10), TRUE);
1836 | pline("%s gaze makes you very sleepy...",
1837 | s_suffix(Monnam(mtmp)));
1838 | }
1839 | break;
1840 | case AD_SLOW:
1841 | if(!mtmp->mcan && canseemon(mtmp) && mtmp->mcansee &&
1842 | (HFast & (INTRINSIC|TIMEOUT)) &&
1843 | !defends(AD_SLOW, uwep) && !rn2(4))
1844 | u_slow_down();
1845 | break;
1846 | #endif
1847 | default: impossible("Gaze attack %d?", mattk->adtyp);
1848 | break;
1849 | }
1850 | return(0);
1851 | }
1852 |
1853 | #endif /* OVLB */
1854 | #ifdef OVL1
1855 |
1856 | void
1857 | mdamageu(mtmp, n) /* mtmp hits you for n points damage */
1858 | register struct monst *mtmp;
1859 | register int n;
1860 | {
1861 | flags.botl = 1;
1862 | if (Upolyd) {
1863 | u.mh -= n;
1864 | if (u.mh < 1) rehumanize();
1865 | } else {
1866 | u.uhp -= n;
1867 | if(u.uhp < 1) done_in_by(mtmp);
1868 | }
1869 | }
1870 |
1871 | #endif /* OVL1 */
1872 | #ifdef OVLB
1873 |
1874 | STATIC_OVL void
1875 | urustm(mon, obj)
1876 | register struct monst *mon;
1877 | register struct obj *obj;
1878 | {
1879 | boolean vis;
1880 | boolean is_acid;
1881 |
1882 | if (!mon || !obj) return; /* just in case */
1883 | if (dmgtype(youmonst.data, AD_CORRODE))
1884 | is_acid = TRUE;
1885 | else if (dmgtype(youmonst.data, AD_RUST))
1886 | is_acid = FALSE;
1887 | else
1888 | return;
1889 |
1890 | vis = cansee(mon->mx, mon->my);
1891 |
1892 | if ((is_acid ? is_corrodeable(obj) : is_rustprone(obj)) &&
1893 | (is_acid ? obj->oeroded2 : obj->oeroded) < MAX_ERODE) {
1894 | if (obj->greased || obj->oerodeproof || (obj->blessed && rn2(3))) {
1895 | if (vis)
1896 | pline("Somehow, %s weapon is not affected.",
1897 | s_suffix(mon_nam(mon)));
1898 | if (obj->greased && !rn2(2)) obj->greased = 0;
1899 | } else {
1900 | if (vis)
1901 | pline("%s %s%s!",
1902 | s_suffix(Monnam(mon)),
1903 | aobjnam(obj, (is_acid ? "corrode" : "rust")),
1904 | (is_acid ? obj->oeroded2 : obj->oeroded)
1905 | ? " further" : "");
1906 | if (is_acid) obj->oeroded2++;
1907 | else obj->oeroded++;
1908 | }
1909 | }
1910 | }
1911 |
1912 | #endif /* OVLB */
1913 | #ifdef OVL1
1914 |
1915 | int
1916 | could_seduce(magr,mdef,mattk)
1917 | struct monst *magr, *mdef;
1918 | struct attack *mattk;
1919 | /* returns 0 if seduction impossible,
1920 | * 1 if fine,
1921 | * 2 if wrong gender for nymph */
1922 | {
1923 | register struct permonst *pagr;
1924 | boolean agrinvis, defperc;
1925 | xchar genagr, gendef;
1926 |
1927 | if (is_animal(magr->data)) return (0);
1928 | if(magr == &youmonst) {
1929 | pagr = youmonst.data;
1930 | agrinvis = (Invis != 0);
1931 | genagr = poly_gender();
1932 | } else {
1933 | pagr = magr->data;
1934 | agrinvis = magr->minvis;
1935 | genagr = gender(magr);
1936 | }
1937 | if(mdef == &youmonst) {
1938 | defperc = (See_invisible != 0);
1939 | gendef = poly_gender();
1940 | } else {
1941 | defperc = perceives(mdef->data);
1942 | gendef = gender(mdef);
1943 | }
1944 |
1945 | if(agrinvis && !defperc
1946 | #ifdef SEDUCE
1947 | && mattk && mattk->adtyp != AD_SSEX
1948 | #endif
1949 | )
1950 | return 0;
1951 |
1952 | if(pagr->mlet != S_NYMPH
1953 | && ((pagr != &mons[PM_INCUBUS] && pagr != &mons[PM_SUCCUBUS])
1954 | #ifdef SEDUCE
1955 | || (mattk && mattk->adtyp != AD_SSEX)
1956 | #endif
1957 | ))
1958 | return 0;
1959 |
1960 | if(genagr == 1 - gendef)
1961 | return 1;
1962 | else
1963 | return (pagr->mlet == S_NYMPH) ? 2 : 0;
1964 | }
1965 |
1966 | #endif /* OVL1 */
1967 | #ifdef OVLB
1968 |
1969 | #ifdef SEDUCE
1970 | /* Returns 1 if monster teleported */
1971 | int
1972 | doseduce(mon)
1973 | register struct monst *mon;
1974 | {
1975 | register struct obj *ring, *nring;
1976 | boolean fem = (mon->data == &mons[PM_SUCCUBUS]); /* otherwise incubus */
1977 | char qbuf[QBUFSZ];
1978 |
1979 | if (mon->mcan || mon->mspec_used) {
1980 | pline("%s acts as though %s has got a %sheadache.",
1981 | Monnam(mon), he[pronoun_gender(mon)],
1982 | mon->mcan ? "severe " : "");
1983 | return 0;
1984 | }
1985 |
1986 | if (unconscious()) {
1987 | pline("%s seems dismayed at your lack of response.",
1988 | Monnam(mon));
1989 | return 0;
1990 | }
1991 |
1992 | if (Blind) pline("It caresses you...");
1993 | else You_feel("very attracted to %s.", mon_nam(mon));
1994 |
1995 | for(ring = invent; ring; ring = nring) {
1996 | nring = ring->nobj;
1997 | if (ring->otyp != RIN_ADORNMENT) continue;
1998 | if (fem) {
1999 | if (rn2(20) < ACURR(A_CHA)) {
2000 | Sprintf(qbuf, "\"That %s looks pretty. May I have it?\"",
2001 | xname(ring));
2002 | makeknown(RIN_ADORNMENT);
2003 | if (yn(qbuf) == 'n') continue;
2004 | } else pline("%s decides she'd like your %s, and takes it.",
2005 | Blind ? "She" : Monnam(mon), xname(ring));
2006 | makeknown(RIN_ADORNMENT);
2007 | if (ring==uleft || ring==uright) Ring_gone(ring);
2008 | if (ring==uwep) setuwep((struct obj *)0);
2009 | if (ring==uswapwep) setuswapwep((struct obj *)0);
2010 | if (ring==uquiver) setuqwep((struct obj *)0);
2011 | freeinv(ring);
2012 | (void) mpickobj(mon,ring);
2013 | } else {
2014 | char buf[BUFSZ];
2015 |
2016 | if (uleft && uright && uleft->otyp == RIN_ADORNMENT
2017 | && uright->otyp==RIN_ADORNMENT)
2018 | break;
2019 | if (ring==uleft || ring==uright) continue;
2020 | if (rn2(20) < ACURR(A_CHA)) {
2021 | Sprintf(qbuf,"\"That %s looks pretty. Would you wear it for me?\"",
2022 | xname(ring));
2023 | makeknown(RIN_ADORNMENT);
2024 | if (yn(qbuf) == 'n') continue;
2025 | } else {
2026 | pline("%s decides you'd look prettier wearing your %s,",
2027 | Blind ? "He" : Monnam(mon), xname(ring));
2028 | pline("and puts it on your finger.");
2029 | }
2030 | makeknown(RIN_ADORNMENT);
2031 | if (!uright) {
2032 | pline("%s puts %s on your right %s.",
2033 | Blind ? "He" : Monnam(mon), the(xname(ring)), body_part(HAND));
2034 | setworn(ring, RIGHT_RING);
2035 | } else if (!uleft) {
2036 | pline("%s puts %s on your left %s.",
2037 | Blind ? "He" : Monnam(mon), the(xname(ring)), body_part(HAND));
2038 | setworn(ring, LEFT_RING);
2039 | } else if (uright && uright->otyp != RIN_ADORNMENT) {
2040 | Strcpy(buf, xname(uright));
2041 | pline("%s replaces your %s with your %s.",
2042 | Blind ? "He" : Monnam(mon), buf, xname(ring));
2043 | Ring_gone(uright);
2044 | setworn(ring, RIGHT_RING);
2045 | } else if (uleft && uleft->otyp != RIN_ADORNMENT) {
2046 | Strcpy(buf, xname(uleft));
2047 | pline("%s replaces your %s with your %s.",
2048 | Blind ? "He" : Monnam(mon), buf, xname(ring));
2049 | Ring_gone(uleft);
2050 | setworn(ring, LEFT_RING);
2051 | } else impossible("ring replacement");
2052 | Ring_on(ring);
2053 | prinv((char *)0, ring, 0L);
2054 | }
2055 | }
2056 |
2057 | if (!uarmc && !uarmf && !uarmg && !uarms && !uarmh
2058 | #ifdef TOURIST
2059 | && !uarmu
2060 | #endif
2061 | )
2062 | pline("%s murmurs sweet nothings into your ear.",
2063 | Blind ? (fem ? "She" : "He") : Monnam(mon));
2064 | else
2065 | pline("%s murmurs in your ear, while helping you undress.",
2066 | Blind ? (fem ? "She" : "He") : Monnam(mon));
2067 | mayberem(uarmc, "cloak");
2068 | if(!uarmc)
2069 | mayberem(uarm, "suit");
2070 | mayberem(uarmf, "boots");
2071 | if(!uwep || !welded(uwep))
2072 | mayberem(uarmg, "gloves");
2073 | mayberem(uarms, "shield");
2074 | mayberem(uarmh, "helmet");
2075 | #ifdef TOURIST
2076 | if(!uarmc && !uarm)
2077 | mayberem(uarmu, "shirt");
2078 | #endif
2079 |
2080 | if (uarm || uarmc) {
2081 | verbalize("You're such a %s; I wish...",
2082 | flags.female ? "sweet lady" : "nice guy");
2083 | if (!tele_restrict(mon)) rloc(mon);
2084 | return 1;
2085 | }
2086 | if (u.ualign.type == A_CHAOTIC)
2087 | adjalign(1);
2088 |
2089 | /* by this point you have discovered mon's identity, blind or not... */
2090 | pline("Time stands still while you and %s lie in each other's arms...",
2091 | mon_nam(mon));
2092 | if (rn2(35) > ACURR(A_CHA) + ACURR(A_INT)) {
2093 | /* Don't bother with mspec_used here... it didn't get tired! */
2094 | pline("%s seems to have enjoyed it more than you...",
2095 | Monnam(mon));
2096 | switch (rn2(5)) {
2097 | case 0: You_feel("drained of energy.");
2098 | u.uen = 0;
2099 | u.uenmax -= rnd(Half_physical_damage ? 5 : 10);
2100 | exercise(A_CON, FALSE);
2101 | if (u.uenmax < 0) u.uenmax = 0;
2102 | break;
2103 | case 1: You("are down in the dumps.");
2104 | (void) adjattrib(A_CON, -1, TRUE);
2105 | exercise(A_CON, FALSE);
2106 | flags.botl = 1;
2107 | break;
2108 | case 2: Your("senses are dulled.");
2109 | (void) adjattrib(A_WIS, -1, TRUE);
2110 | exercise(A_WIS, FALSE);
2111 | flags.botl = 1;
2112 | break;
2113 | case 3:
2114 | if (!resists_drli(&youmonst)) {
2115 | You_feel("out of shape.");
2116 | losexp("overexertion");
2117 | } else {
2118 | You("have a curious feeling...");
2119 | }
2120 | break;
2121 | case 4: {
2122 | int tmp;
2123 | You_feel("exhausted.");
2124 | exercise(A_STR, FALSE);
2125 | tmp = rn1(10, 6);
2126 | if(Half_physical_damage) tmp = (tmp+1) / 2;
2127 | losehp(tmp, "exhaustion", KILLED_BY);
2128 | break;
2129 | }
2130 | }
2131 | } else {
2132 | mon->mspec_used = rnd(100); /* monster is worn out */
2133 | You("seem to have enjoyed it more than %s...", mon_nam(mon));
2134 | switch (rn2(5)) {
2135 | case 0: You_feel("raised to your full potential.");
2136 | exercise(A_CON, TRUE);
2137 | u.uen = (u.uenmax += rnd(5));
2138 | break;
2139 | case 1: You_feel("good enough to do it again.");
2140 | (void) adjattrib(A_CON, 1, TRUE);
2141 | exercise(A_CON, TRUE);
2142 | flags.botl = 1;
2143 | break;
2144 | case 2: You("will always remember %s...", mon_nam(mon));
2145 | (void) adjattrib(A_WIS, 1, TRUE);
2146 | exercise(A_WIS, TRUE);
2147 | flags.botl = 1;
2148 | break;
2149 | case 3: pline("That was a very educational experience.");
2150 | pluslvl(FALSE);
2151 | exercise(A_WIS, TRUE);
2152 | break;
2153 | case 4: You_feel("restored to health!");
2154 | u.uhp = u.uhpmax;
2155 | if (Upolyd) u.mh = u.mhmax;
2156 | exercise(A_STR, TRUE);
2157 | flags.botl = 1;
2158 | break;
2159 | }
2160 | }
2161 |
2162 | if (mon->mtame) /* don't charge */ ;
2163 | else if (rn2(20) < ACURR(A_CHA)) {
2164 | pline("%s demands that you pay %s, but you refuse...",
2165 | Monnam(mon), him[fem]);
2166 | } else if (u.umonnum == PM_LEPRECHAUN)
2167 | pline("%s tries to take your money, but fails...",
2168 | Monnam(mon));
2169 | else {
2170 | long cost;
2171 |
2172 | if (u.ugold > (long)LARGEST_INT - 10L)
2173 | cost = (long) rnd(LARGEST_INT) + 500L;
2174 | else
2175 | cost = (long) rnd((int)u.ugold + 10) + 500L;
2176 | if (mon->mpeaceful) {
2177 | cost /= 5L;
2178 | if (!cost) cost = 1L;
2179 | }
2180 | if (cost > u.ugold) cost = u.ugold;
2181 | if (!cost) verbalize("It's on the house!");
2182 | else {
2183 | pline("%s takes %ld zorkmid%s for services rendered!",
2184 | Monnam(mon), cost, plur(cost));
2185 | u.ugold -= cost;
2186 | mon->mgold += cost;
2187 | flags.botl = 1;
2188 | }
2189 | }
2190 | if (!rn2(25)) mon->mcan = 1; /* monster is worn out */
2191 | if (!tele_restrict(mon)) rloc(mon);
2192 | return 1;
2193 | }
2194 |
2195 | STATIC_OVL void
2196 | mayberem(obj, str)
2197 | register struct obj *obj;
2198 | const char *str;
2199 | {
2200 | char qbuf[QBUFSZ];
2201 |
2202 | if (!obj || !obj->owornmask) return;
2203 |
2204 | if (rn2(20) < ACURR(A_CHA)) {
2205 | Sprintf(qbuf,"\"Shall I remove your %s, %s?\"",
2206 | str,
2207 | (!rn2(2) ? "lover" : !rn2(2) ? "dear" : "sweetheart"));
2208 | if (yn(qbuf) == 'n') return;
2209 | } else {
2210 | char hairbuf[BUFSZ];
2211 |
2212 | Sprintf(hairbuf, "let me run my fingers through your %s",
2213 | body_part(HAIR));
2214 | verbalize("Take off your %s; %s.", str,
2215 | (obj == uarm) ? "let's get a little closer" :
2216 | (obj == uarmc || obj == uarms) ? "it's in the way" :
2217 | (obj == uarmf) ? "let me rub your feet" :
2218 | (obj == uarmg) ? "they're too clumsy" :
2219 | #ifdef TOURIST
2220 | (obj == uarmu) ? "let me massage you" :
2221 | #endif
2222 | /* obj == uarmh */
2223 | hairbuf);
2224 | }
2225 | remove_worn_item(obj);
2226 | }
2227 | #endif /* SEDUCE */
2228 |
2229 | #endif /* OVLB */
2230 |
2231 | #ifdef OVL1
2232 |
2233 | STATIC_OVL int
2234 | passiveum(olduasmon,mtmp,mattk)
2235 | struct permonst *olduasmon;
2236 | register struct monst *mtmp;
2237 | register struct attack *mattk;
2238 | {
2239 | int i, tmp;
2240 |
2241 | for (i = 0; ; i++) {
2242 | if (i >= NATTK) return 1;
2243 | if (olduasmon->mattk[i].aatyp == AT_NONE ||
2244 | olduasmon->mattk[i].aatyp == AT_BOOM) break;
2245 | }
2246 | if (olduasmon->mattk[i].damn)
2247 | tmp = d((int)olduasmon->mattk[i].damn,
2248 | (int)olduasmon->mattk[i].damd);
2249 | else if(olduasmon->mattk[i].damd)
2250 | tmp = d((int)olduasmon->mlevel+1, (int)olduasmon->mattk[i].damd);
2251 | else
2252 | tmp = 0;
2253 |
2254 | /* These affect the enemy even if you were "killed" (rehumanized) */
2255 | switch(olduasmon->mattk[i].adtyp) {
2256 | case AD_ACID:
2257 | if (!rn2(2)) {
2258 | pline("%s is splashed by your acid!", Monnam(mtmp));
2259 | if (resists_acid(mtmp)) {
2260 | pline("%s is not affected.", Monnam(mtmp));
2261 | tmp = 0;
2262 | }
2263 | } else tmp = 0;
2264 | if (!rn2(30)) erode_armor(mtmp, TRUE);
2265 | if (!rn2(6)) erode_weapon(MON_WEP(mtmp), TRUE);
2266 | goto assess_dmg;
2267 | case AD_STON: /* cockatrice */
2268 | if (!resists_ston(mtmp) &&
2269 | (mattk->aatyp != AT_WEAP || !MON_WEP(mtmp)) &&
2270 | mattk->aatyp != AT_GAZE && mattk->aatyp != AT_EXPL &&
2271 | mattk->aatyp != AT_MAGC &&
2272 | !(mtmp->misc_worn_check & W_ARMG)) {
2273 | if(poly_when_stoned(mtmp->data)) {
2274 | mon_to_stone(mtmp);
2275 | return (1);
2276 | }
2277 | pline("%s turns to stone!", Monnam(mtmp));
2278 | stoned = 1;
2279 | xkilled(mtmp, 0);
2280 | if (mtmp->mhp > 0) return 1;
2281 | return 2;
2282 | }
2283 | return 1;
2284 | case AD_ENCH: /* KMH -- remove enchantment (disenchanter) */
2285 | if (otmp) {
2286 | (void) drain_item(otmp);
2287 | /* No message */
2288 | }
2289 | return (1);
2290 | default:
2291 | break;
2292 | }
2293 | if (!Upolyd) return 1;
2294 |
2295 | /* These affect the enemy only if you are still a monster */
2296 | if (rn2(3)) switch(youmonst.data->mattk[i].adtyp) {
2297 | case AD_PHYS:
2298 | if (youmonst.data->mattk[i].aatyp == AT_BOOM) {
2299 | You("explode!");
2300 | /* KMH, balance patch -- this is okay with unchanging */
2301 | rehumanize();
2302 | goto assess_dmg;
2303 | }
2304 | break;
2305 | case AD_PLYS: /* Floating eye */
2306 | if (tmp > 127) tmp = 127;
2307 | if (u.umonnum == PM_FLOATING_EYE) {
2308 | if (!rn2(4)) tmp = 127;
2309 | if (mtmp->mcansee && haseyes(mtmp->data) && rn2(3) &&
2310 | (perceives(mtmp->data) || !Invis)) {
2311 | if (Blind)
2312 | pline("As a blind %s, you cannot defend yourself.",
2313 | youmonst.data->mname);
2314 | else {
2315 | if (mon_reflects(mtmp,
2316 | "Your gaze is reflected by %s %s."))
2317 | return 1;
2318 | pline("%s is frozen by your gaze!", Monnam(mtmp));
2319 | mtmp->mcanmove = 0;
2320 | mtmp->mfrozen = tmp;
2321 | return 3;
2322 | }
2323 | }
2324 | } else { /* gelatinous cube */
2325 | pline("%s is frozen by you.", Monnam(mtmp));
2326 | mtmp->mcanmove = 0;
2327 | mtmp->mfrozen = tmp;
2328 | return 3;
2329 | }
2330 | return 1;
2331 | case AD_COLD: /* Brown mold or blue jelly */
2332 | if (resists_cold(mtmp)) {
2333 | shieldeff(mtmp->mx, mtmp->my);
2334 | pline("%s is mildly chilly.", Monnam(mtmp));
2335 | golemeffects(mtmp, AD_COLD, tmp);
2336 | tmp = 0;
2337 | break;
2338 | }
2339 | pline("%s is suddenly very cold!", Monnam(mtmp));
2340 | u.mh += tmp / 2;
2341 | if (u.mhmax < u.mh) u.mhmax = u.mh;
2342 | if (u.mhmax > ((youmonst.data->mlevel+1) * 8))
2343 | (void)split_mon(&youmonst, mtmp);
2344 | break;
2345 | case AD_STUN: /* Yellow mold */
2346 | if (!mtmp->mstun) {
2347 | mtmp->mstun = 1;
2348 | pline("%s staggers.", Monnam(mtmp));
2349 | }
2350 | tmp = 0;
2351 | break;
2352 | case AD_FIRE: /* Red mold */
2353 | if (resists_fire(mtmp)) {
2354 | shieldeff(mtmp->mx, mtmp->my);
2355 | pline("%s is mildly warm.", Monnam(mtmp));
2356 | golemeffects(mtmp, AD_FIRE, tmp);
2357 | tmp = 0;
2358 | break;
2359 | }
2360 | pline("%s is suddenly very hot!", Monnam(mtmp));
2361 | break;
2362 | case AD_ELEC:
2363 | if (resists_elec(mtmp)) {
2364 | shieldeff(mtmp->mx, mtmp->my);
2365 | pline("%s is slightly tingled.", Monnam(mtmp));
2366 | golemeffects(mtmp, AD_ELEC, tmp);
2367 | tmp = 0;
2368 | break;
2369 | }
2370 | pline("%s is jolted with your electricity!", Monnam(mtmp));
2371 | break;
2372 | default: tmp = 0;
2373 | break;
2374 | }
2375 | else tmp = 0;
2376 |
2377 | assess_dmg:
2378 | if((mtmp->mhp -= tmp) <= 0) {
2379 | pline("%s dies!", Monnam(mtmp));
2380 | xkilled(mtmp,0);
2381 | if (mtmp->mhp > 0) return 1;
2382 | return 2;
2383 | }
2384 | return 1;
2385 | }
2386 |
2387 | #endif /* OVL1 */
2388 | #ifdef OVLB
2389 |
2390 | #include "edog.h"
2391 | struct monst *
2392 | cloneu()
2393 | {
2394 | register struct monst *mon;
2395 | int mndx = monsndx(youmonst.data);
2396 |
2397 | if (u.mh <= 1) return(struct monst *)0;
2398 | if (mvitals[mndx].mvflags & G_EXTINCT) return(struct monst *)0;
2399 | mon = makemon(youmonst.data, u.ux, u.uy, NO_MINVENT|MM_EDOG);
2400 | mon = christen_monst(mon, plname);
2401 | initedog(mon);
2402 | mon->m_lev = youmonst.data->mlevel;
2403 | mon->mhpmax = u.mhmax;
2404 | mon->mhp = u.mh / 2;
2405 | u.mh -= mon->mhp;
2406 | flags.botl = 1;
2407 | return(mon);
2408 | }
2409 |
2410 | #endif /* OVLB */
2411 |
2412 | /*mhitu.c*/