OSDN Git Service

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