OSDN Git Service

Initial Import
[nethackexpress/trunk.git] / src / mthrowu.c
1 /*      SCCS Id: @(#)mthrowu.c  3.4     2003/05/09      */
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 int FDECL(drop_throw,(struct obj *,BOOLEAN_P,int,int));
8
9 #define URETREATING(x,y) (distmin(u.ux,u.uy,x,y) > distmin(u.ux0,u.uy0,x,y))
10
11 #define POLE_LIM 5      /* How far monsters can use pole-weapons */
12
13 #ifndef OVLB
14
15 STATIC_DCL const char *breathwep[];
16
17 #else /* OVLB */
18
19 /*
20  * Keep consistent with breath weapons in zap.c, and AD_* in monattk.h.
21  */
22 STATIC_OVL NEARDATA const char *breathwep[] = {
23                                 "fragments",
24                                 "fire",
25                                 "frost",
26                                 "sleep gas",
27                                 "a disintegration blast",
28                                 "lightning",
29                                 "poison gas",
30                                 "acid",
31                                 "strange breath #8",
32                                 "strange breath #9"
33 };
34
35 /* hero is hit by something other than a monster */
36 int
37 thitu(tlev, dam, obj, name)
38 int tlev, dam;
39 struct obj *obj;
40 const char *name;       /* if null, then format `obj' */
41 {
42         const char *onm, *knm;
43         boolean is_acid;
44         int kprefix = KILLED_BY_AN;
45         char onmbuf[BUFSZ], knmbuf[BUFSZ];
46
47         if (!name) {
48             if (!obj) panic("thitu: name & obj both null?");
49             name = strcpy(onmbuf,
50                          (obj->quan > 1L) ? doname(obj) : mshot_xname(obj));
51             knm = strcpy(knmbuf, killer_xname(obj));
52             kprefix = KILLED_BY;  /* killer_name supplies "an" if warranted */
53         } else {
54             knm = name;
55             /* [perhaps ought to check for plural here to] */
56             if (!strncmpi(name, "the ", 4) ||
57                     !strncmpi(name, "an ", 3) ||
58                     !strncmpi(name, "a ", 2)) kprefix = KILLED_BY;
59         }
60         onm = (obj && obj_is_pname(obj)) ? the(name) :
61                             (obj && obj->quan > 1L) ? name : an(name);
62         is_acid = (obj && obj->otyp == ACID_VENOM);
63
64         if(u.uac + tlev <= rnd(20)) {
65                 if(Blind || !flags.verbose) pline("It misses.");
66                 else You("are almost hit by %s.", onm);
67                 return(0);
68         } else {
69                 if(Blind || !flags.verbose) You("are hit!");
70                 else You("are hit by %s%s", onm, exclam(dam));
71
72                 if (obj && objects[obj->otyp].oc_material == SILVER
73                                 && hates_silver(youmonst.data)) {
74                         dam += rnd(20);
75                         pline_The("silver sears your flesh!");
76                         exercise(A_CON, FALSE);
77                 }
78                 if (is_acid && Acid_resistance)
79                         pline("It doesn't seem to hurt you.");
80                 else {
81                         if (is_acid) pline("It burns!");
82                         if (Half_physical_damage) dam = (dam+1) / 2;
83                         losehp(dam, knm, kprefix);
84                         exercise(A_STR, FALSE);
85                 }
86                 return(1);
87         }
88 }
89
90 /* Be sure this corresponds with what happens to player-thrown objects in
91  * dothrow.c (for consistency). --KAA
92  * Returns 0 if object still exists (not destroyed).
93  */
94
95 STATIC_OVL int
96 drop_throw(obj, ohit, x, y)
97 register struct obj *obj;
98 boolean ohit;
99 int x,y;
100 {
101         int retvalu = 1;
102         int create;
103         struct monst *mtmp;
104         struct trap *t;
105
106         if (obj->otyp == CREAM_PIE || obj->oclass == VENOM_CLASS ||
107                     (ohit && obj->otyp == EGG))
108                 create = 0;
109         else if (ohit && (is_multigen(obj) || obj->otyp == ROCK))
110                 create = !rn2(3);
111         else create = 1;
112
113         if (create && !((mtmp = m_at(x, y)) && (mtmp->mtrapped) &&
114                         (t = t_at(x, y)) && ((t->ttyp == PIT) ||
115                         (t->ttyp == SPIKED_PIT)))) {
116                 int objgone = 0;
117
118                 if (down_gate(x, y) != -1)
119                         objgone = ship_object(obj, x, y, FALSE);
120                 if (!objgone) {
121                         if (!flooreffects(obj,x,y,"fall")) { /* don't double-dip on damage */
122                             place_object(obj, x, y);
123                             if (!mtmp && x == u.ux && y == u.uy)
124                                 mtmp = &youmonst;
125                             if (mtmp && ohit)
126                                 passive_obj(mtmp, obj, (struct attack *)0);
127                             stackobj(obj);
128                             retvalu = 0;
129                         }
130                 }
131         } else obfree(obj, (struct obj*) 0);
132         return retvalu;
133 }
134
135 #endif /* OVLB */
136 #ifdef OVL1
137
138 /* an object launched by someone/thing other than player attacks a monster;
139    return 1 if the object has stopped moving (hit or its range used up) */
140 int
141 ohitmon(mtmp, otmp, range, verbose)
142 struct monst *mtmp;     /* accidental target */
143 struct obj *otmp;       /* missile; might be destroyed by drop_throw */
144 int range;              /* how much farther will object travel if it misses */
145                         /* Use -1 to signify to keep going even after hit, */
146                         /* unless its gone (used for rolling_boulder_traps) */
147 boolean verbose;  /* give message(s) even when you can't see what happened */
148 {
149         int damage, tmp;
150         boolean vis, ismimic;
151         int objgone = 1;
152
153         ismimic = mtmp->m_ap_type && mtmp->m_ap_type != M_AP_MONSTER;
154         vis = cansee(bhitpos.x, bhitpos.y);
155
156         tmp = 5 + find_mac(mtmp) + omon_adj(mtmp, otmp, FALSE);
157         if (tmp < rnd(20)) {
158             if (!ismimic) {
159                 if (vis) miss(distant_name(otmp, mshot_xname), mtmp);
160                 else if (verbose) pline("It is missed.");
161             }
162             if (!range) { /* Last position; object drops */
163                 (void) drop_throw(otmp, 0, mtmp->mx, mtmp->my);
164                 return 1;
165             }
166         } else if (otmp->oclass == POTION_CLASS) {
167             if (ismimic) seemimic(mtmp);
168             mtmp->msleeping = 0;
169             if (vis) otmp->dknown = 1;
170             potionhit(mtmp, otmp, FALSE);
171             return 1;
172         } else {
173             damage = dmgval(otmp, mtmp);
174             if (otmp->otyp == ACID_VENOM && resists_acid(mtmp))
175                 damage = 0;
176             if (ismimic) seemimic(mtmp);
177             mtmp->msleeping = 0;
178             if (vis) hit(distant_name(otmp,mshot_xname), mtmp, exclam(damage));
179             else if (verbose) pline("%s is hit%s", Monnam(mtmp), exclam(damage));
180
181             if (otmp->opoisoned && is_poisonable(otmp)) {
182                 if (resists_poison(mtmp)) {
183                     if (vis) pline_The("poison doesn't seem to affect %s.",
184                                    mon_nam(mtmp));
185                 } else {
186                     if (rn2(30)) {
187                         damage += rnd(6);
188                     } else {
189                         if (vis) pline_The("poison was deadly...");
190                         damage = mtmp->mhp;
191                     }
192                 }
193             }
194             if (objects[otmp->otyp].oc_material == SILVER &&
195                     hates_silver(mtmp->data)) {
196                 if (vis) pline_The("silver sears %s flesh!",
197                                 s_suffix(mon_nam(mtmp)));
198                 else if (verbose) pline("Its flesh is seared!");
199             }
200             if (otmp->otyp == ACID_VENOM && cansee(mtmp->mx,mtmp->my)) {
201                 if (resists_acid(mtmp)) {
202                     if (vis || verbose)
203                         pline("%s is unaffected.", Monnam(mtmp));
204                     damage = 0;
205                 } else {
206                     if (vis) pline_The("acid burns %s!", mon_nam(mtmp));
207                     else if (verbose) pline("It is burned!");
208                 }
209             }
210             mtmp->mhp -= damage;
211             if (mtmp->mhp < 1) {
212                 if (vis || verbose)
213                     pline("%s is %s!", Monnam(mtmp),
214                         (nonliving(mtmp->data) || !canspotmon(mtmp))
215                         ? "destroyed" : "killed");
216                 /* don't blame hero for unknown rolling boulder trap */
217                 if (!flags.mon_moving &&
218                     (otmp->otyp != BOULDER || range >= 0 || !otmp->otrapped))
219                     xkilled(mtmp,0);
220                 else mondied(mtmp);
221             }
222
223             if (can_blnd((struct monst*)0, mtmp,
224                     (uchar)(otmp->otyp == BLINDING_VENOM ? AT_SPIT : AT_WEAP),
225                     otmp)) {
226                 if (vis && mtmp->mcansee)
227                     pline("%s is blinded by %s.", Monnam(mtmp), the(xname(otmp)));
228                 mtmp->mcansee = 0;
229                 tmp = (int)mtmp->mblinded + rnd(25) + 20;
230                 if (tmp > 127) tmp = 127;
231                 mtmp->mblinded = tmp;
232             }
233
234             objgone = drop_throw(otmp, 1, bhitpos.x, bhitpos.y);
235             if (!objgone && range == -1) {  /* special case */
236                     obj_extract_self(otmp); /* free it for motion again */
237                     return 0;
238             }
239             return 1;
240         }
241         return 0;
242 }
243
244 void
245 m_throw(mon, x, y, dx, dy, range, obj)
246         register struct monst *mon;
247         register int x,y,dx,dy,range;           /* direction and range */
248         register struct obj *obj;
249 {
250         register struct monst *mtmp;
251         struct obj *singleobj;
252         char sym = obj->oclass;
253         int hitu, blindinc = 0;
254
255         bhitpos.x = x;
256         bhitpos.y = y;
257
258         if (obj->quan == 1L) {
259             /*
260              * Remove object from minvent.  This cannot be done later on;
261              * what if the player dies before then, leaving the monster
262              * with 0 daggers?  (This caused the infamous 2^32-1 orcish
263              * dagger bug).
264              *
265              * VENOM is not in minvent - it should already be OBJ_FREE.
266              * The extract below does nothing.
267              */
268
269             /* not possibly_unwield, which checks the object's */
270             /* location, not its existence */
271             if (MON_WEP(mon) == obj) {
272                     setmnotwielded(mon,obj);
273                     MON_NOWEP(mon);
274             }
275             obj_extract_self(obj);
276             singleobj = obj;
277             obj = (struct obj *) 0;
278         } else {
279             singleobj = splitobj(obj, 1L);
280             obj_extract_self(singleobj);
281         }
282
283         singleobj->owornmask = 0; /* threw one of multiple weapons in hand? */
284
285         if (singleobj->cursed && (dx || dy) && !rn2(7)) {
286             if(canseemon(mon) && flags.verbose) {
287                 if(is_ammo(singleobj))
288                     pline("%s misfires!", Monnam(mon));
289                 else
290                     pline("%s as %s throws it!",
291                           Tobjnam(singleobj, "slip"), mon_nam(mon));
292             }
293             dx = rn2(3)-1;
294             dy = rn2(3)-1;
295             /* check validity of new direction */
296             if (!dx && !dy) {
297                 (void) drop_throw(singleobj, 0, bhitpos.x, bhitpos.y);
298                 return;
299             }
300         }
301
302         /* pre-check for doors, walls and boundaries.
303            Also need to pre-check for bars regardless of direction;
304            the random chance for small objects hitting bars is
305            skipped when reaching them at point blank range */
306         if (!isok(bhitpos.x+dx,bhitpos.y+dy)
307             || IS_ROCK(levl[bhitpos.x+dx][bhitpos.y+dy].typ)
308             || closed_door(bhitpos.x+dx, bhitpos.y+dy)
309             || (levl[bhitpos.x + dx][bhitpos.y + dy].typ == IRONBARS &&
310                 hits_bars(&singleobj, bhitpos.x, bhitpos.y, 0, 0))) {
311             (void) drop_throw(singleobj, 0, bhitpos.x, bhitpos.y);
312             return;
313         }
314
315         /* Note: drop_throw may destroy singleobj.  Since obj must be destroyed
316          * early to avoid the dagger bug, anyone who modifies this code should
317          * be careful not to use either one after it's been freed.
318          */
319         if (sym) tmp_at(DISP_FLASH, obj_to_glyph(singleobj));
320         while(range-- > 0) { /* Actually the loop is always exited by break */
321                 bhitpos.x += dx;
322                 bhitpos.y += dy;
323                 if ((mtmp = m_at(bhitpos.x, bhitpos.y)) != 0) {
324                     if (ohitmon(mtmp, singleobj, range, TRUE))
325                         break;
326                 } else if (bhitpos.x == u.ux && bhitpos.y == u.uy) {
327                     if (multi) nomul(0);
328
329                     if (singleobj->oclass == GEM_CLASS &&
330                             singleobj->otyp <= LAST_GEM+9 /* 9 glass colors */
331                             && is_unicorn(youmonst.data)) {
332                         if (singleobj->otyp > LAST_GEM) {
333                             You("catch the %s.", xname(singleobj));
334                             You("are not interested in %s junk.",
335                                 s_suffix(mon_nam(mon)));
336                             makeknown(singleobj->otyp);
337                             dropy(singleobj);
338                         } else {
339                             You("accept %s gift in the spirit in which it was intended.",
340                                 s_suffix(mon_nam(mon)));
341                             (void)hold_another_object(singleobj,
342                                 "You catch, but drop, %s.", xname(singleobj),
343                                 "You catch:");
344                         }
345                         break;
346                     }
347                     if (singleobj->oclass == POTION_CLASS) {
348                         if (!Blind) singleobj->dknown = 1;
349                         potionhit(&youmonst, singleobj, FALSE);
350                         break;
351                     }
352                     switch(singleobj->otyp) {
353                         int dam, hitv;
354                         case EGG:
355                             if (!touch_petrifies(&mons[singleobj->corpsenm])) {
356                                 impossible("monster throwing egg type %d",
357                                         singleobj->corpsenm);
358                                 hitu = 0;
359                                 break;
360                             }
361                             /* fall through */
362                         case CREAM_PIE:
363                         case BLINDING_VENOM:
364                             hitu = thitu(8, 0, singleobj, (char *)0);
365                             break;
366                         default:
367                             dam = dmgval(singleobj, &youmonst);
368                             hitv = 3 - distmin(u.ux,u.uy, mon->mx,mon->my);
369                             if (hitv < -4) hitv = -4;
370                             if (is_elf(mon->data) &&
371                                 objects[singleobj->otyp].oc_skill == P_BOW) {
372                                 hitv++;
373                                 if (MON_WEP(mon) &&
374                                     MON_WEP(mon)->otyp == ELVEN_BOW)
375                                     hitv++;
376                                 if(singleobj->otyp == ELVEN_ARROW) dam++;
377                             }
378                             if (bigmonst(youmonst.data)) hitv++;
379                             hitv += 8 + singleobj->spe;
380                             if (dam < 1) dam = 1;
381                             hitu = thitu(hitv, dam, singleobj, (char *)0);
382                     }
383                     if (hitu && singleobj->opoisoned &&
384                         is_poisonable(singleobj)) {
385                         char onmbuf[BUFSZ], knmbuf[BUFSZ];
386
387                         Strcpy(onmbuf, xname(singleobj));
388                         Strcpy(knmbuf, killer_xname(singleobj));
389                         poisoned(onmbuf, A_STR, knmbuf, -10);
390                     }
391                     if(hitu &&
392                        can_blnd((struct monst*)0, &youmonst,
393                                 (uchar)(singleobj->otyp == BLINDING_VENOM ?
394                                         AT_SPIT : AT_WEAP), singleobj)) {
395                         blindinc = rnd(25);
396                         if(singleobj->otyp == CREAM_PIE) {
397                             if(!Blind) pline("Yecch!  You've been creamed.");
398                             else pline("There's %s sticky all over your %s.",
399                                        something,
400                                        body_part(FACE));
401                         } else if(singleobj->otyp == BLINDING_VENOM) {
402                             int num_eyes = eyecount(youmonst.data);
403                             /* venom in the eyes */
404                             if(!Blind) pline_The("venom blinds you.");
405                             else Your("%s sting%s.",
406                                       (num_eyes == 1) ? body_part(EYE) :
407                                                 makeplural(body_part(EYE)),
408                                       (num_eyes == 1) ? "s" : "");
409                         }
410                     }
411                     if (hitu && singleobj->otyp == EGG) {
412                         if (!Stone_resistance
413                             && !(poly_when_stoned(youmonst.data) &&
414                                  polymon(PM_STONE_GOLEM))) {
415                             Stoned = 5;
416                             killer = (char *) 0;
417                         }
418                     }
419                     stop_occupation();
420                     if (hitu || !range) {
421                         (void) drop_throw(singleobj, hitu, u.ux, u.uy);
422                         break;
423                     }
424                 } else if (!range       /* reached end of path */
425                         /* missile hits edge of screen */
426                         || !isok(bhitpos.x+dx,bhitpos.y+dy)
427                         /* missile hits the wall */
428                         || IS_ROCK(levl[bhitpos.x+dx][bhitpos.y+dy].typ)
429                         /* missile hit closed door */
430                         || closed_door(bhitpos.x+dx, bhitpos.y+dy)
431                         /* missile might hit iron bars */
432                         || (levl[bhitpos.x+dx][bhitpos.y+dy].typ == IRONBARS &&
433                         hits_bars(&singleobj, bhitpos.x, bhitpos.y, !rn2(5), 0))
434 #ifdef SINKS
435                         /* Thrown objects "sink" */
436                         || IS_SINK(levl[bhitpos.x][bhitpos.y].typ)
437 #endif
438                                                                 ) {
439                     if (singleobj) /* hits_bars might have destroyed it */
440                         (void) drop_throw(singleobj, 0, bhitpos.x, bhitpos.y);
441                     break;
442                 }
443                 tmp_at(bhitpos.x, bhitpos.y);
444                 delay_output();
445         }
446         tmp_at(bhitpos.x, bhitpos.y);
447         delay_output();
448         tmp_at(DISP_END, 0);
449
450         if (blindinc) {
451                 u.ucreamed += blindinc;
452                 make_blinded(Blinded + (long)blindinc, FALSE);
453                 if (!Blind) Your(vision_clears);
454         }
455 }
456
457 #endif /* OVL1 */
458 #ifdef OVLB
459
460 /* Remove an item from the monster's inventory and destroy it. */
461 void
462 m_useup(mon, obj)
463 struct monst *mon;
464 struct obj *obj;
465 {
466         if (obj->quan > 1L) {
467                 obj->quan--;
468                 obj->owt = weight(obj);
469         } else {
470                 obj_extract_self(obj);
471                 possibly_unwield(mon, FALSE);
472                 if (obj->owornmask) {
473                     mon->misc_worn_check &= ~obj->owornmask;
474                     update_mon_intrinsics(mon, obj, FALSE, FALSE);
475                 }
476                 obfree(obj, (struct obj*) 0);
477         }
478 }
479
480 #endif /* OVLB */
481 #ifdef OVL1
482
483 /* monster attempts ranged weapon attack against player */
484 void
485 thrwmu(mtmp)
486 struct monst *mtmp;
487 {
488         struct obj *otmp, *mwep;
489         xchar x, y;
490         schar skill;
491         int multishot;
492         const char *onm;
493
494         /* Rearranged beginning so monsters can use polearms not in a line */
495         if (mtmp->weapon_check == NEED_WEAPON || !MON_WEP(mtmp)) {
496             mtmp->weapon_check = NEED_RANGED_WEAPON;
497             /* mon_wield_item resets weapon_check as appropriate */
498             if(mon_wield_item(mtmp) != 0) return;
499         }
500
501         /* Pick a weapon */
502         otmp = select_rwep(mtmp);
503         if (!otmp) return;
504
505         if (is_pole(otmp)) {
506             int dam, hitv;
507
508             if (dist2(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy) > POLE_LIM ||
509                     !couldsee(mtmp->mx, mtmp->my))
510                 return; /* Out of range, or intervening wall */
511
512             if (canseemon(mtmp)) {
513                 onm = xname(otmp);
514                 pline("%s thrusts %s.", Monnam(mtmp),
515                       obj_is_pname(otmp) ? the(onm) : an(onm));
516             }
517
518             dam = dmgval(otmp, &youmonst);
519             hitv = 3 - distmin(u.ux,u.uy, mtmp->mx,mtmp->my);
520             if (hitv < -4) hitv = -4;
521             if (bigmonst(youmonst.data)) hitv++;
522             hitv += 8 + otmp->spe;
523             if (dam < 1) dam = 1;
524
525             (void) thitu(hitv, dam, otmp, (char *)0);
526             stop_occupation();
527             return;
528         }
529
530         x = mtmp->mx;
531         y = mtmp->my;
532         /* If you are coming toward the monster, the monster
533          * should try to soften you up with missiles.  If you are
534          * going away, you are probably hurt or running.  Give
535          * chase, but if you are getting too far away, throw.
536          */
537         if (!lined_up(mtmp) ||
538                 (URETREATING(x,y) &&
539                         rn2(BOLT_LIM - distmin(x,y,mtmp->mux,mtmp->muy))))
540             return;
541
542         skill = objects[otmp->otyp].oc_skill;
543         mwep = MON_WEP(mtmp);           /* wielded weapon */
544
545         /* Multishot calculations */
546         multishot = 1;
547         if ((ammo_and_launcher(otmp, mwep) || skill == P_DAGGER ||
548                 skill == -P_DART || skill == -P_SHURIKEN) && !mtmp->mconf) {
549             /* Assumes lords are skilled, princes are expert */
550             if (is_prince(mtmp->data)) multishot += 2;
551             else if (is_lord(mtmp->data)) multishot++;
552
553             switch (monsndx(mtmp->data)) {
554             case PM_RANGER:
555                     multishot++;
556                     break;
557             case PM_ROGUE:
558                     if (skill == P_DAGGER) multishot++;
559                     break;
560             case PM_NINJA:
561             case PM_SAMURAI:
562                     if (otmp->otyp == YA && mwep &&
563                         mwep->otyp == YUMI) multishot++;
564                     break;
565             default:
566                 break;
567             }
568             /* racial bonus */
569             if ((is_elf(mtmp->data) &&
570                     otmp->otyp == ELVEN_ARROW &&
571                     mwep && mwep->otyp == ELVEN_BOW) ||
572                 (is_orc(mtmp->data) &&
573                     otmp->otyp == ORCISH_ARROW &&
574                     mwep && mwep->otyp == ORCISH_BOW))
575                 multishot++;
576
577             if ((long)multishot > otmp->quan) multishot = (int)otmp->quan;
578             if (multishot < 1) multishot = 1;
579             else multishot = rnd(multishot);
580         }
581
582         if (canseemon(mtmp)) {
583             char onmbuf[BUFSZ];
584
585             if (multishot > 1) {
586                 /* "N arrows"; multishot > 1 implies otmp->quan > 1, so
587                    xname()'s result will already be pluralized */
588                 Sprintf(onmbuf, "%d %s", multishot, xname(otmp));
589                 onm = onmbuf;
590             } else {
591                 /* "an arrow" */
592                 onm = singular(otmp, xname);
593                 onm = obj_is_pname(otmp) ? the(onm) : an(onm);
594             }
595             m_shot.s = ammo_and_launcher(otmp,mwep) ? TRUE : FALSE;
596             pline("%s %s %s!", Monnam(mtmp),
597                   m_shot.s ? "shoots" : "throws", onm);
598             m_shot.o = otmp->otyp;
599         } else {
600             m_shot.o = STRANGE_OBJECT;  /* don't give multishot feedback */
601         }
602
603         m_shot.n = multishot;
604         for (m_shot.i = 1; m_shot.i <= m_shot.n; m_shot.i++)
605             m_throw(mtmp, mtmp->mx, mtmp->my, sgn(tbx), sgn(tby),
606                     distmin(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy), otmp);
607         m_shot.n = m_shot.i = 0;
608         m_shot.o = STRANGE_OBJECT;
609         m_shot.s = FALSE;
610
611         nomul(0);
612 }
613
614 #endif /* OVL1 */
615 #ifdef OVLB
616
617 int
618 spitmu(mtmp, mattk)             /* monster spits substance at you */
619 register struct monst *mtmp;
620 register struct attack *mattk;
621 {
622         register struct obj *otmp;
623
624         if(mtmp->mcan) {
625
626             if(flags.soundok)
627                 pline("A dry rattle comes from %s throat.",
628                                       s_suffix(mon_nam(mtmp)));
629             return 0;
630         }
631         if(lined_up(mtmp)) {
632                 switch (mattk->adtyp) {
633                     case AD_BLND:
634                     case AD_DRST:
635                         otmp = mksobj(BLINDING_VENOM, TRUE, FALSE);
636                         break;
637                     default:
638                         impossible("bad attack type in spitmu");
639                                 /* fall through */
640                     case AD_ACID:
641                         otmp = mksobj(ACID_VENOM, TRUE, FALSE);
642                         break;
643                 }
644                 if(!rn2(BOLT_LIM-distmin(mtmp->mx,mtmp->my,mtmp->mux,mtmp->muy))) {
645                     if (canseemon(mtmp))
646                         pline("%s spits venom!", Monnam(mtmp));
647                     m_throw(mtmp, mtmp->mx, mtmp->my, sgn(tbx), sgn(tby),
648                         distmin(mtmp->mx,mtmp->my,mtmp->mux,mtmp->muy), otmp);
649                     nomul(0);
650                     return 0;
651                 }
652         }
653         return 0;
654 }
655
656 #endif /* OVLB */
657 #ifdef OVL1
658
659 int
660 breamu(mtmp, mattk)                     /* monster breathes at you (ranged) */
661         register struct monst *mtmp;
662         register struct attack  *mattk;
663 {
664         /* if new breath types are added, change AD_ACID to max type */
665         int typ = (mattk->adtyp == AD_RBRE) ? rnd(AD_ACID) : mattk->adtyp ;
666
667         if(lined_up(mtmp)) {
668
669             if(mtmp->mcan) {
670                 if(flags.soundok) {
671                     if(canseemon(mtmp))
672                         pline("%s coughs.", Monnam(mtmp));
673                     else
674                         You_hear("a cough.");
675                 }
676                 return(0);
677             }
678             if(!mtmp->mspec_used && rn2(3)) {
679
680                 if((typ >= AD_MAGM) && (typ <= AD_ACID)) {
681
682                     if(canseemon(mtmp))
683                         pline("%s breathes %s!", Monnam(mtmp),
684                               breathwep[typ-1]);
685                     buzz((int) (-20 - (typ-1)), (int)mattk->damn,
686                          mtmp->mx, mtmp->my, sgn(tbx), sgn(tby));
687                     nomul(0);
688                     /* breath runs out sometimes. Also, give monster some
689                      * cunning; don't breath if the player fell asleep.
690                      */
691                     if(!rn2(3))
692                         mtmp->mspec_used = 10+rn2(20);
693                     if(typ == AD_SLEE && !Sleep_resistance)
694                         mtmp->mspec_used += rnd(20);
695                 } else impossible("Breath weapon %d used", typ-1);
696             }
697         }
698         return(1);
699 }
700
701 boolean
702 linedup(ax, ay, bx, by)
703 register xchar ax, ay, bx, by;
704 {
705         tbx = ax - bx;  /* These two values are set for use */
706         tby = ay - by;  /* after successful return.         */
707
708         /* sometimes displacement makes a monster think that you're at its
709            own location; prevent it from throwing and zapping in that case */
710         if (!tbx && !tby) return FALSE;
711
712         if((!tbx || !tby || abs(tbx) == abs(tby)) /* straight line or diagonal */
713            && distmin(tbx, tby, 0, 0) < BOLT_LIM) {
714             if(ax == u.ux && ay == u.uy) return((boolean)(couldsee(bx,by)));
715             else if(clear_path(ax,ay,bx,by)) return TRUE;
716         }
717         return FALSE;
718 }
719
720 boolean
721 lined_up(mtmp)          /* is mtmp in position to use ranged attack? */
722         register struct monst *mtmp;
723 {
724         return(linedup(mtmp->mux,mtmp->muy,mtmp->mx,mtmp->my));
725 }
726
727 #endif /* OVL1 */
728 #ifdef OVL0
729
730 /* Check if a monster is carrying a particular item.
731  */
732 struct obj *
733 m_carrying(mtmp, type)
734 struct monst *mtmp;
735 int type;
736 {
737         register struct obj *otmp;
738
739         for(otmp = mtmp->minvent; otmp; otmp = otmp->nobj)
740                 if(otmp->otyp == type)
741                         return(otmp);
742         return((struct obj *) 0);
743 }
744
745 /* TRUE iff thrown/kicked/rolled object doesn't pass through iron bars */
746 boolean
747 hits_bars(obj_p, x, y, always_hit, whodidit)
748 struct obj **obj_p;     /* *obj_p will be set to NULL if object breaks */
749 int x, y;
750 int always_hit; /* caller can force a hit for items which would fit through */
751 int whodidit;   /* 1==hero, 0=other, -1==just check whether it'll pass thru */
752 {
753     struct obj *otmp = *obj_p;
754     int obj_type = otmp->otyp;
755     boolean hits = always_hit;
756
757     if (!hits)
758         switch (otmp->oclass) {
759         case WEAPON_CLASS:
760             {
761                 int oskill = objects[obj_type].oc_skill;
762
763                 hits = (oskill != -P_BOW  && oskill != -P_CROSSBOW &&
764                         oskill != -P_DART && oskill != -P_SHURIKEN &&
765                         oskill != P_SPEAR && oskill != P_JAVELIN &&
766                         oskill != P_KNIFE);     /* but not dagger */
767                 break;
768             }
769         case ARMOR_CLASS:
770                 hits = (objects[obj_type].oc_armcat != ARM_GLOVES);
771                 break;
772         case TOOL_CLASS:
773                 hits = (obj_type != SKELETON_KEY &&
774                         obj_type != LOCK_PICK &&
775 #ifdef TOURIST
776                         obj_type != CREDIT_CARD &&
777 #endif
778                         obj_type != TALLOW_CANDLE &&
779                         obj_type != WAX_CANDLE &&
780                         obj_type != LENSES &&
781                         obj_type != TIN_WHISTLE &&
782                         obj_type != MAGIC_WHISTLE);
783                 break;
784         case ROCK_CLASS:        /* includes boulder */
785                 if (obj_type != STATUE ||
786                         mons[otmp->corpsenm].msize > MZ_TINY) hits = TRUE;
787                 break;
788         case FOOD_CLASS:
789                 if (obj_type == CORPSE &&
790                         mons[otmp->corpsenm].msize > MZ_TINY) hits = TRUE;
791                 else
792                     hits = (obj_type == MEAT_STICK ||
793                             obj_type == HUGE_CHUNK_OF_MEAT);
794                 break;
795         case SPBOOK_CLASS:
796         case WAND_CLASS:
797         case BALL_CLASS:
798         case CHAIN_CLASS:
799                 hits = TRUE;
800                 break;
801         default:
802                 break;
803         }
804
805     if (hits && whodidit != -1) {
806         if (whodidit ? hero_breaks(otmp, x, y, FALSE) : breaks(otmp, x, y))
807             *obj_p = otmp = 0;          /* object is now gone */
808             /* breakage makes its own noises */
809         else if (obj_type == BOULDER || obj_type == HEAVY_IRON_BALL)
810             pline("Whang!");
811         else if (otmp->oclass == COIN_CLASS ||
812                 objects[obj_type].oc_material == GOLD ||
813                 objects[obj_type].oc_material == SILVER)
814             pline("Clink!");
815         else
816             pline("Clonk!");
817     }
818
819     return hits;
820 }
821
822 #endif /* OVL0 */
823
824 /*mthrowu.c*/