]> granicus.if.org Git - nethack/commitdiff
multishot by monsters, plus reformatting
authorPatR <rankin@nethack.org>
Tue, 7 Jun 2016 00:42:58 +0000 (17:42 -0700)
committerPatR <rankin@nethack.org>
Tue, 7 Jun 2016 00:42:58 +0000 (17:42 -0700)
Some reformatting of the recently added pet ranged attack code.

The redundant--but different--multishot volley code has been replaced
so that there are only two versions (hero and monster) instead of
three (hero and monster vs hero and pet vs other monster).  The monst
version was out of date relative to post-3.4.3 changes to the hero one.
The pet version was way out of date and had some bugs:  wielding an
elven bow gave a +1 multishot increment to volley count for fast weapon
even when throwing something rather than shooting arrows, wielding any
weapon which had at least +2 enchantment gave 1/3 enchantment bonus to
volley count when throwing instead of shooting shoot ammo, and a pet
which got killed in the midst of a multishot volley--perhaps by a gas
spore explosion or some other passive counterattack--would keep on
shooting/throwing until the volley count was exhausted.

Pet use of ranged weapons is not ready for prime-time.  Pets don't
hang on to missiles or launchers+ammo, they just drop them if there is
no target immediately available.

src/dogmove.c
src/end.c
src/mhitm.c
src/mhitu.c
src/mthrowu.c

index e59e7e8015c8145d8f328b241a10955bd8e84aa0..321deb0afd91b255dd7c27c4d9170ab7c47577b9 100644 (file)
@@ -11,9 +11,9 @@ extern boolean notonhead;
 STATIC_DCL boolean FDECL(dog_hunger, (struct monst *, struct edog *));
 STATIC_DCL int FDECL(dog_invent, (struct monst *, struct edog *, int));
 STATIC_DCL int FDECL(dog_goal, (struct monst *, struct edog *, int, int, int));
-STATIC_DCL struct monstFDECL(find_targ, (struct monst *, int, int, int));
+STATIC_DCL struct monst *FDECL(find_targ, (struct monst *, int, int, int));
 STATIC_OVL int FDECL(find_friends, (struct monst *, struct monst *, int));
-STATIC_DCL struct monstFDECL(best_target, (struct monst *));
+STATIC_DCL struct monst *FDECL(best_target, (struct monst *));
 STATIC_DCL long FDECL(score_targ, (struct monst *, struct monst *));
 STATIC_DCL boolean FDECL(can_reach_location, (struct monst *, XCHAR_P,
                                               XCHAR_P, XCHAR_P, XCHAR_P));
@@ -284,8 +284,8 @@ boolean devour;
         Strcpy(objnambuf, xname(obj));
         iflags.suppress_price--;
     }
-    /* It's a reward if it's DOGFOOD and the player dropped/threw it. */
-    /* We know the player had it if invlet is set -dlc */
+    /* It's a reward if it's DOGFOOD and the player dropped/threw it.
+       We know the player had it if invlet is set. -dlc */
     if (dogfood(mtmp, obj) == DOGFOOD && obj->invlet)
 #ifdef LINT
         edog->apport = 0;
@@ -351,8 +351,8 @@ boolean devour;
 /* hunger effects -- returns TRUE on starvation */
 STATIC_OVL boolean
 dog_hunger(mtmp, edog)
-register struct monst *mtmp;
-register struct edog *edog;
+struct monst *mtmp;
+struct edog *edog;
 {
     if (monstermoves > edog->hungrytime + 500) {
         if (!carnivorous(mtmp->data) && !herbivorous(mtmp->data)) {
@@ -423,7 +423,8 @@ int udist;
                 edog->droptime = monstermoves;
             }
     } else {
-        if ((obj = level.objects[omx][omy]) && !index(nofetch, obj->oclass)
+        if ((obj = level.objects[omx][omy]) != 0
+            && !index(nofetch, obj->oclass)
 #ifdef MAIL
             && obj->otyp != SCR_MAIL
 #endif
@@ -561,16 +562,12 @@ int after, udist, whappr;
                 appr = 1;
         }
         /* if you have dog food it'll follow you more closely */
-        if (appr == 0) {
-            obj = invent;
-            while (obj) {
+        if (appr == 0)
+            for (obj = invent; obj; obj = obj->nobj)
                 if (dogfood(mtmp, obj) == DOGFOOD) {
                     appr = 1;
                     break;
                 }
-                obj = obj->nobj;
-            }
-        }
     } else
         appr = 1; /* gtyp != UNDEF */
     if (mtmp->mconf)
@@ -589,7 +586,7 @@ int after, udist, whappr;
         } else {
             /* assume master hasn't moved far, and reuse previous goal */
             if (edog && edog->ogoal.x
-                && ((edog->ogoal.x != omx) || (edog->ogoal.y != omy))) {
+                && (edog->ogoal.x != omx || edog->ogoal.y != omy)) {
                 gx = edog->ogoal.x;
                 gy = edog->ogoal.y;
                 edog->ogoal.x = 0;
@@ -707,7 +704,6 @@ int    maxdist;
     return 0;
 }
 
-
 STATIC_OVL long
 score_targ(mtmp, mtarg)
 struct monst *mtmp, *mtarg;
@@ -721,86 +717,74 @@ struct monst *mtmp, *mtarg;
     /* Give 1 in 3 chance of safe breathing even if pet is confused or
      * if you're on the quest start level */
     if (!mtmp->mconf || !rn2(3) || Is_qstart(&u.uz)) {
-        aligntyp align1, align2; /* For priests, minions */
+        aligntyp align1 = A_NONE, align2 = A_NONE; /* For priests, minions */
         boolean faith1 = TRUE,  faith2 = TRUE;
 
-        if (mtmp->isminion) align1 = EMIN(mtmp)->min_align;
-        else if (mtmp->ispriest) align1 = EPRI(mtmp)->shralign;
-        else faith1 = FALSE;
-        if (mtarg->isminion) align2 = EMIN(mtarg)->min_align; /* MAR */
-        else if (mtarg->ispriest) align2 = EPRI(mtarg)->shralign; /* MAR */
-        else faith2 = FALSE;
+        if (mtmp->isminion)
+            align1 = EMIN(mtmp)->min_align;
+        else if (mtmp->ispriest)
+            align1 = EPRI(mtmp)->shralign;
+        else
+            faith1 = FALSE;
+        if (mtarg->isminion)
+            align2 = EMIN(mtarg)->min_align; /* MAR */
+        else if (mtarg->ispriest)
+            align2 = EPRI(mtarg)->shralign; /* MAR */
+        else
+            faith2 = FALSE;
 
         /* Never target quest friendlies */
         if (mtarg->data->msound == MS_LEADER
             || mtarg->data->msound == MS_GUARDIAN)
             return -5000L;
-
         /* D: Fixed angelic beings using gaze attacks on coaligned priests */
         if (faith1 && faith2 && align1 == align2 && mtarg->mpeaceful) {
             score -= 5000L;
             return score;
         }
-
         /* Is monster adjacent? */
         if (distmin(mtmp->mx, mtmp->my, mtarg->mx, mtarg->my) <= 1) {
             score -= 3000L;
             return score;
         }
-
         /* Is the monster peaceful or tame? */
         if (/*mtarg->mpeaceful ||*/ mtarg->mtame || mtarg == &youmonst) {
             /* Pets will never be targeted */
             score -= 3000L;
             return score;
         }
-
-        /* Is master/pet behind monster? Check up to 15 squares beyond
-         * pet.
-         */
+        /* Is master/pet behind monster? Check up to 15 squares beyond pet. */
         if (find_friends(mtmp, mtarg, 15)) {
             score -= 3000L;
             return score;
         }
-
         /* Target hostile monsters in preference to peaceful ones */
         if (!mtarg->mpeaceful)
             score += 10;
-
         /* Is the monster passive? Don't waste energy on it, if so */
         if (mtarg->data->mattk[0].aatyp == AT_NONE)
             score -= 1000;
-
         /* Even weak pets with breath attacks shouldn't take on very
-         * low-level monsters. Wasting breath on lichens is ridiculous.
-         */
+           low-level monsters. Wasting breath on lichens is ridiculous. */
         if ((mtarg->m_lev < 2 && mtmp->m_lev > 5)
             || (mtmp->m_lev > 12 && mtarg->m_lev < mtmp->m_lev - 9
                 && u.ulevel > 8 && mtarg->m_lev < u.ulevel - 7))
             score -= 25;
-
         /* And pets will hesitate to attack vastly stronger foes.
-         * This penalty will be discarded if master's in trouble.
-         */
+           This penalty will be discarded if master's in trouble. */
         if (mtarg->m_lev > mtmp->m_lev + 4L)
             score -= (mtarg->m_lev - mtmp->m_lev) * 20L;
-
         /* All things being the same, go for the beefiest monster. This
-         * bonus should not be large enough to override the pet's aversion
-         * to attacking much stronger monsters.
-         */
+           bonus should not be large enough to override the pet's aversion
+           to attacking much stronger monsters. */
         score += mtarg->m_lev * 2 + mtarg->mhp / 3;
     }
-
     /* Fuzz factor to make things less predictable when very
-     * similar targets are abundant
-     */
+       similar targets are abundant. */
     score += rnd(5);
-
     /* Pet may decide not to use ranged attack when confused */
     if (mtmp->mconf && !rn2(3))
         score -= 1000;
-
     return score;
 }
 
@@ -1121,9 +1105,8 @@ int after; /* this is extra fast monster movement */
     }
 
     /* Pet hasn't attacked anything but is considering moving -
-     * now's the time for ranged attacks. Note that the pet can
-     * move after it performs its ranged attack. Should this be
-     * changed?
+     * now's the time for ranged attacks. Note that the pet can move
+     * after it performs its ranged attack. Should this be changed?
      */
     {
         struct monst *mtarg;
@@ -1152,7 +1135,8 @@ int after; /* this is extra fast monster movement */
                 mstatus = mattackm(mtmp, mtarg);
 
                 /* Shouldn't happen, really */
-                if (mstatus & MM_AGR_DIED) return 2;
+                if (mstatus & MM_AGR_DIED)
+                    return 2;
 
                 /* Allow the targeted nasty to strike back - if
                  * the targeted beast doesn't have a ranged attack,
@@ -1168,7 +1152,8 @@ int after; /* this is extra fast monster movement */
                      */
                     if (mtarg->mcansee && haseyes(mtarg->data)) {
                         mstatus = mattackm(mtarg, mtmp);
-                        if (mstatus & MM_DEF_DIED) return 2;
+                        if (mstatus & MM_DEF_DIED)
+                            return 2;
                     }
                 }
             }
@@ -1201,7 +1186,7 @@ newdogpos:
             /* describe top item of pile, not necessarily cursed item itself;
                don't use glyph_at() here--it would return the pet but we want
                to know whether an object is remembered at this map location */
-            struct obj *o = (!Hallucination
+            struct obj *o = (!Hallucination && level.flags.hero_memory
                              && glyph_is_object(levl[nix][niy].glyph))
                                ? vobj_at(nix, niy) : 0;
             const char *what = o ? distant_name(o, doname) : something;
@@ -1319,12 +1304,12 @@ wantdoor(x, y, distance)
 int x, y;
 genericptr_t distance;
 {
-    int ndist;
+    int ndist, *dist_ptr = (int *) distance;
 
-    if (*(int *) distance > (ndist = distu(x, y))) {
+    if (*dist_ptr > (ndist = distu(x, y))) {
         gx = x;
         gy = y;
-        *(int *) distance = ndist;
+        *dist_ptr = ndist;
     }
 }
 
index 57f8425bc87dd1b3daf6d14808f5c99e18a9e037..d097292fbf6a0eece058c111e722f260578cac6e 100644 (file)
--- a/src/end.c
+++ b/src/end.c
@@ -781,6 +781,8 @@ int how;
     u.ugrave_arise = NON_PM;
     HUnchanging = 0L;
     curs_on_u();
+    if (!context.mon_moving)
+        endmultishot(FALSE);
 }
 
 /*
index e4c05897b745d120390604fde91dc4272ad3d48c..3c6965fd070f9980e961fa8367951b83db557301 100644 (file)
@@ -356,15 +356,13 @@ register struct monst *magr, *mdef;
         attk = 1;
         switch (mattk->aatyp) {
         case AT_WEAP: /* "hand to hand" attacks */
-            if (distmin(magr->mx,magr->my,mdef->mx,mdef->my) > 1) {
+            if (distmin(magr->mx, magr->my, mdef->mx, mdef->my) > 1) {
                 /* D: Do a ranged attack here! */
                 strike = thrwmm(magr, mdef);
                 if (DEADMONSTER(mdef))
                     res[i] = MM_DEF_DIED;
-
                 if (DEADMONSTER(magr))
                     res[i] |= MM_AGR_DIED;
-
                 break;
             }
             if (magr->weapon_check == NEED_WEAPON || !MON_WEP(magr)) {
@@ -390,8 +388,7 @@ register struct monst *magr, *mdef;
         case AT_TENT:
             /* Nymph that teleported away on first attack? */
             if (distmin(magr->mx, magr->my, mdef->mx, mdef->my) > 1)
-                /* Continue because the monster may have a ranged
-                 * attack */
+                /* Continue because the monster may have a ranged attack. */
                 continue;
             /* Monsters won't attack cockatrices physically if they
              * have a weapon instead.  This instinct doesn't work for
@@ -454,50 +451,52 @@ register struct monst *magr, *mdef;
             break;
 
         case AT_ENGL:
-            if (u.usteed && (mdef == u.usteed)) {
+            if (u.usteed && mdef == u.usteed) {
                 strike = 0;
                 break;
             }
             /* D: Prevent engulf from a distance */
-            if (distmin(magr->mx,magr->my,mdef->mx,mdef->my) > 1)
+            if (distmin(magr->mx, magr->my, mdef->mx, mdef->my) > 1)
                 continue;
-            /* Engulfing attacks are directed at the hero if
-             * possible. -dlc
-             */
+            /* Engulfing attacks are directed at the hero if possible. -dlc */
             if (u.uswallow && magr == u.ustuck)
                 strike = 0;
-            else {
-                if ((strike = (tmp > rnd(20 + i))))
-                    res[i] = gulpmm(magr, mdef, mattk);
-                else
-                    missmm(magr, mdef, mattk);
-            }
+            else if ((strike = (tmp > rnd(20 + i))) != 0)
+                res[i] = gulpmm(magr, mdef, mattk);
+            else
+                missmm(magr, mdef, mattk);
             break;
 
         case AT_BREA:
             if (!monnear(magr, mdef->mx, mdef->my)) {
                 strike = breamm(magr, mattk, mdef);
 
-                /* We don't really know if we hit or not, but pretend
-                 * we did */
-                if (strike) res[i] |= MM_HIT;
-                if (DEADMONSTER(mdef)) res[i] = MM_DEF_DIED;
-                if (DEADMONSTER(magr)) res[i] |= MM_AGR_DIED;
+                /* We don't really know if we hit or not; pretend we did. */
+                if (strike)
+                    res[i] |= MM_HIT;
+                if (DEADMONSTER(mdef))
+                    res[i] = MM_DEF_DIED;
+                if (DEADMONSTER(magr))
+                    res[i] |= MM_AGR_DIED;
             }
             else
                 strike = 0;
             break;
+
         case AT_SPIT:
             if (!monnear(magr, mdef->mx, mdef->my)) {
                 strike = spitmm(magr, mattk, mdef);
 
-                /* We don't really know if we hit or not, but pretend
-                 * we did */
-                if (strike) res[i] |= MM_HIT;
-                if (DEADMONSTER(mdef)) res[i] = MM_DEF_DIED;
-                if (DEADMONSTER(magr)) res[i] |= MM_AGR_DIED;
+                /* We don't really know if we hit or not; pretend we did. */
+                if (strike)
+                    res[i] |= MM_HIT;
+                if (DEADMONSTER(mdef))
+                    res[i] = MM_DEF_DIED;
+                if (DEADMONSTER(magr))
+                    res[i] |= MM_AGR_DIED;
             }
             break;
+
         default: /* no attack */
             strike = 0;
             attk = 0;
@@ -505,12 +504,11 @@ register struct monst *magr, *mdef;
         }
 
         if (attk && !(res[i] & MM_AGR_DIED)
-            && distmin(magr->mx,magr->my,mdef->mx,mdef->my) <= 1)
+            && distmin(magr->mx, magr->my, mdef->mx, mdef->my) <= 1)
             res[i] = passivemm(magr, mdef, strike, res[i] & MM_DEF_DIED);
 
         if (res[i] & MM_DEF_DIED)
             return res[i];
-
         if (res[i] & MM_AGR_DIED)
             return res[i];
         /* return if aggressor can no longer attack */
@@ -595,9 +593,8 @@ struct attack *mattk;
         pline("%s %s...", buf, mon_nam(mdef));
     }
 
-    if (magr->mcan || !magr->mcansee
-        || (magr->minvis && !perceives(mdef->data)) || !mdef->mcansee
-        || mdef->msleeping) {
+    if (magr->mcan || !magr->mcansee || !mdef->mcansee
+        || (magr->minvis && !perceives(mdef->data)) || mdef->msleeping) {
         if (vis)
             pline("but nothing happens.");
         return MM_MISS;
@@ -609,8 +606,8 @@ struct attack *mattk;
         if (mdef->mcansee) {
             if (mon_reflects(magr, (char *) 0)) {
                 if (canseemon(magr))
-                    (void) mon_reflects(
-                        magr, "The gaze is reflected away by %s %s.");
+                    (void) mon_reflects(magr,
+                                      "The gaze is reflected away by %s %s.");
                 return MM_MISS;
             }
             if (mdef->minvis && !perceives(magr->data)) {
@@ -723,7 +720,7 @@ register struct attack *mattk;
     } else if (status & MM_AGR_DIED) { /* aggressor died */
         place_monster(mdef, dx, dy);
         newsym(dx, dy);
-    } else { /* both alive, put them back */
+    } else {                           /* both alive, put them back */
         if (cansee(dx, dy))
             pline("%s is regurgitated!", Monnam(mdef));
 
@@ -1174,6 +1171,7 @@ register struct attack *mattk;
          */
         {
             struct obj *gold = findgold(mdef->minvent);
+
             if (!gold)
                 break;
             obj_extract_self(gold);
index 6ab8481cc83f79ed367323b7f47747e0106a8504..e9ec5a9e027229ae31fa31a49374e2a6213e8e93 100644 (file)
@@ -8,11 +8,9 @@
 STATIC_VAR NEARDATA struct obj *otmp;
 
 STATIC_DCL boolean FDECL(u_slip_free, (struct monst *, struct attack *));
-STATIC_DCL int FDECL(passiveum,
-                     (struct permonst *, struct monst *, struct attack *));
-
+STATIC_DCL int FDECL(passiveum, (struct permonst *, struct monst *,
+                                 struct attack *));
 STATIC_DCL void FDECL(mayberem, (struct obj *, const char *));
-
 STATIC_DCL boolean FDECL(diseasemu, (struct permonst *));
 STATIC_DCL int FDECL(hitmu, (struct monst *, struct attack *));
 STATIC_DCL int FDECL(gulpmu, (struct monst *, struct attack *));
@@ -20,7 +18,6 @@ STATIC_DCL int FDECL(explmu, (struct monst *, struct attack *, BOOLEAN_P));
 STATIC_DCL void FDECL(missmu, (struct monst *, BOOLEAN_P, struct attack *));
 STATIC_DCL void FDECL(mswings, (struct monst *, struct obj *));
 STATIC_DCL void FDECL(wildmiss, (struct monst *, struct attack *));
-
 STATIC_DCL void FDECL(hitmsg, (struct monst *, struct attack *));
 
 /* See comment in mhitm.c.  If we use this a lot it probably should be */
@@ -33,41 +30,48 @@ register struct monst *mtmp;
 register struct attack *mattk;
 {
     int compat;
+    const char *pfmt = 0;
+    char *Monst_name = Monnam(mtmp);
 
     /* Note: if opposite gender, "seductively" */
     /* If same gender, "engagingly" for nymph, normal msg for others */
-    if ((compat = could_seduce(mtmp, &youmonst, mattk)) && !mtmp->mcan
-        && !mtmp->mspec_used) {
-        pline("%s %s you %s.", Monnam(mtmp), Blind ? "talks to" : "smiles at",
-              compat == 2 ? "engagingly" : "seductively");
-    } else
+    if ((compat = could_seduce(mtmp, &youmonst, mattk)) != 0
+        && !mtmp->mcan && !mtmp->mspec_used) {
+        pline("%s %s you %s.", Monst_name,
+              Blind ? "talks to" : "smiles at",
+              (compat == 2) ? "engagingly" : "seductively");
+    } else {
         switch (mattk->aatyp) {
         case AT_BITE:
-            pline("%s bites!", Monnam(mtmp));
+            pfmt = "%s bites!";
             break;
         case AT_KICK:
-            pline("%s kicks%c", Monnam(mtmp),
+            pline("%s kicks%c", Monst_name,
                   thick_skinned(youmonst.data) ? '.' : '!');
             break;
         case AT_STNG:
-            pline("%s stings!", Monnam(mtmp));
+            pfmt = "%s stings!";
             break;
         case AT_BUTT:
-            pline("%s butts!", Monnam(mtmp));
+            pfmt = "%s butts!";
             break;
         case AT_TUCH:
-            pline("%s touches you!", Monnam(mtmp));
+            pfmt = "%s touches you!";
             break;
         case AT_TENT:
-            pline("%s tentacles suck you!", s_suffix(Monnam(mtmp)));
+            pfmt = "%s tentacles suck you!";
+            Monst_name = s_suffix(Monst_name);
             break;
         case AT_EXPL:
         case AT_BOOM:
-            pline("%s explodes!", Monnam(mtmp));
+            pfmt = "%s explodes!";
             break;
         default:
-            pline("%s hits!", Monnam(mtmp));
+            pfmt = "%s hits!";
         }
+        if (pfmt)
+            pline(pfmt, Monst_name);
+    }
 }
 
 /* monster missed you */
@@ -139,6 +143,7 @@ register struct monst *mtmp;
 register struct attack *mattk;
 {
     int compat;
+    const char *Monst_name; /* Monnam(mtmp) */
 
     /* no map_invisible() -- no way to tell where _this_ is coming from */
 
@@ -150,42 +155,41 @@ register struct attack *mattk;
 
     compat = ((mattk->adtyp == AD_SEDU || mattk->adtyp == AD_SSEX)
               && could_seduce(mtmp, &youmonst, (struct attack *) 0));
+    Monst_name = Monnam(mtmp);
 
     if (!mtmp->mcansee || (Invis && !perceives(mtmp->data))) {
-        const char *swings =
-            mattk->aatyp == AT_BITE
-                ? "snaps"
-                : mattk->aatyp == AT_KICK
-                      ? "kicks"
-                      : (mattk->aatyp == AT_STNG || mattk->aatyp == AT_BUTT
-                         || nolimbs(mtmp->data))
-                            ? "lunges"
-                            : "swings";
+        const char *swings = (mattk->aatyp == AT_BITE) ? "snaps"
+                             : (mattk->aatyp == AT_KICK) ? "kicks"
+                               : (mattk->aatyp == AT_STNG
+                                  || mattk->aatyp == AT_BUTT
+                                  || nolimbs(mtmp->data)) ? "lunges"
+                                 : "swings";
 
         if (compat)
-            pline("%s tries to touch you and misses!", Monnam(mtmp));
+            pline("%s tries to touch you and misses!", Monst_name);
         else
             switch (rn2(3)) {
             case 0:
-                pline("%s %s wildly and misses!", Monnam(mtmp), swings);
+                pline("%s %s wildly and misses!", Monst_name, swings);
                 break;
             case 1:
-                pline("%s attacks a spot beside you.", Monnam(mtmp));
+                pline("%s attacks a spot beside you.", Monst_name);
                 break;
             case 2:
-                pline("%s strikes at %s!", Monnam(mtmp),
-                      levl[mtmp->mux][mtmp->muy].typ == WATER ? "empty water"
-                                                              : "thin air");
+                pline("%s strikes at %s!", Monst_name,
+                      (levl[mtmp->mux][mtmp->muy].typ == WATER)
+                        ? "empty water"
+                        : "thin air");
                 break;
             default:
-                pline("%s %s wildly!", Monnam(mtmp), swings);
+                pline("%s %s wildly!", Monst_name, swings);
                 break;
             }
 
     } else if (Displaced) {
         if (compat)
-            pline("%s smiles %s at your %sdisplaced image...", Monnam(mtmp),
-                  compat == 2 ? "engagingly" : "seductively",
+            pline("%s smiles %s at your %sdisplaced image...", Monst_name,
+                  (compat == 2) ? "engagingly" : "seductively",
                   Invis ? "invisible " : "");
         else
             pline("%s strikes at your %sdisplaced image and misses you!",
@@ -194,20 +198,20 @@ register struct attack *mattk;
                    * displaced image, since the displaced image is also
                    * invisible.
                    */
-                  Monnam(mtmp), Invis ? "invisible " : "");
+                  Monst_name, Invis ? "invisible " : "");
 
     } else if (Underwater) {
         /* monsters may miss especially on water level where
            bubbles shake the player here and there */
         if (compat)
-            pline("%s reaches towards your distorted image.", Monnam(mtmp));
+            pline("%s reaches towards your distorted image.", Monst_name);
         else
             pline("%s is fooled by water reflections and misses!",
-                  Monnam(mtmp));
+                  Monst_name);
 
     } else
         impossible("%s attacks you without knowing your location?",
-                   Monnam(mtmp));
+                   Monst_name);
 }
 
 void
@@ -217,9 +221,9 @@ struct permonst *mdat; /* if mtmp is polymorphed, mdat != mtmp->data */
 boolean message;
 {
     if (message) {
-        if (is_animal(mdat))
+        if (is_animal(mdat)) {
             You("get regurgitated!");
-        else {
+        else {
             char blast[40];
             register int i;
 
@@ -227,9 +231,9 @@ boolean message;
             for (i = 0; i < NATTK; i++)
                 if (mdat->mattk[i].aatyp == AT_ENGL)
                     break;
-            if (mdat->mattk[i].aatyp != AT_ENGL)
+            if (mdat->mattk[i].aatyp != AT_ENGL) {
                 impossible("Swallower has no engulfing attack?");
-            else {
+            else {
                 if (is_whirly(mdat)) {
                     switch (mdat->mattk[i].adtyp) {
                     case AD_ELEC:
@@ -306,11 +310,10 @@ struct attack *alt_attk_buf;
                && !(mptr->mattk[1].aatyp == AT_WEAP
                     && mptr->mattk[1].adtyp == AD_PHYS)
                && (magr->mcan
-                   || (weap
-                       && ((weap->otyp == CORPSE
-                            && touch_petrifies(&mons[weap->corpsenm]))
-                           || weap->oartifact == ART_STORMBRINGER
-                           || weap->oartifact == ART_VORPAL_BLADE)))) {
+                   || (weap && ((weap->otyp == CORPSE
+                                 && touch_petrifies(&mons[weap->corpsenm]))
+                                || weap->oartifact == ART_STORMBRINGER
+                                || weap->oartifact == ART_VORPAL_BLADE)))) {
         *alt_attk_buf = *attk;
         attk = alt_attk_buf;
         attk->adtyp = AD_PHYS;
@@ -1157,7 +1160,8 @@ register struct attack *mattk;
         break;
     case AD_LEGS: {
         register long side = rn2(2) ? RIGHT_SIDE : LEFT_SIDE;
-        const char *sidestr = (side == RIGHT_SIDE) ? "right" : "left";
+        const char *sidestr = (side == RIGHT_SIDE) ? "right" : "left",
+                   *Monst_name = Monnam(mtmp), *leg = body_part(LEG);
 
         /* This case is too obvious to ignore, but Nethack is not in
          * general very good at considering height--most short monsters
@@ -1165,31 +1169,30 @@ register struct attack *mattk;
          * [FIXME: why can't a flying attacker overcome this?]
          */
         if (u.usteed || Levitation || Flying) {
-            pline("%s tries to reach your %s %s!", Monnam(mtmp), sidestr,
-                  body_part(LEG));
+            pline("%s tries to reach your %s %s!", Monst_name, sidestr, leg);
             dmg = 0;
         } else if (mtmp->mcan) {
-            pline("%s nuzzles against your %s %s!", Monnam(mtmp), sidestr,
-                  body_part(LEG));
+            pline("%s nuzzles against your %s %s!", Monnam(mtmp),
+                  sidestr, leg);
             dmg = 0;
         } else {
             if (uarmf) {
                 if (rn2(2) && (uarmf->otyp == LOW_BOOTS
-                               || uarmf->otyp == IRON_SHOES))
+                               || uarmf->otyp == IRON_SHOES)) {
                     pline("%s pricks the exposed part of your %s %s!",
-                          Monnam(mtmp), sidestr, body_part(LEG));
-                else if (!rn2(5))
-                    pline("%s pricks through your %s boot!", Monnam(mtmp),
+                          Monst_name, sidestr, leg);
+                } else if (!rn2(5)) {
+                    pline("%s pricks through your %s boot!", Monst_name,
                           sidestr);
-                else {
-                    pline("%s scratches your %s boot!", Monnam(mtmp),
+                else {
+                    pline("%s scratches your %s boot!", Monst_name,
                           sidestr);
                     dmg = 0;
                     break;
                 }
             } else
-                pline("%s pricks your %s %s!", Monnam(mtmp), sidestr,
-                      body_part(LEG));
+                pline("%s pricks your %s %s!", Monst_name, sidestr, leg);
+
             set_wounded_legs(side, rnd(60 - ACURR(A_DEX)));
             exercise(A_STR, FALSE);
             exercise(A_DEX, FALSE);
@@ -1308,12 +1311,11 @@ register struct attack *mattk;
                 (void) rloc(mtmp, TRUE);
             return 3;
         } else if (mtmp->mcan) {
-            if (!Blind) {
+            if (!Blind)
                 pline("%s tries to %s you, but you seem %s.",
                       Adjmonnam(mtmp, "plain"),
                       flags.female ? "charm" : "seduce",
                       flags.female ? "unaffected" : "uninterested");
-            }
             if (rn2(3)) {
                 if (!tele_restrict(mtmp))
                     (void) rloc(mtmp, TRUE);
@@ -1705,6 +1707,7 @@ register struct attack *mattk;
         newsym(mtmp->mx, mtmp->my);
         if (is_animal(mtmp->data) && u.usteed) {
             char buf[BUFSZ];
+
             /* Too many quirks presently if hero and steed
              * are swallowed. Pretend purple worms don't
              * like horses for now :-)
index 1d9477f7c06057e62b9a335a610b07e68d3b8dce..7c5f297d7893d43343cbdcb0601d6ad1ed212b10 100644 (file)
@@ -4,6 +4,8 @@
 
 #include "hack.h"
 
+STATIC_DCL int FDECL(monmulti, (struct monst *, struct obj *, struct obj *));
+STATIC_DCL void FDECL(monshoot, (struct monst *, struct obj *, struct obj *));
 STATIC_DCL int FDECL(drop_throw, (struct obj *, BOOLEAN_P, int, int));
 STATIC_DCL boolean FDECL(m_lined_up, (struct monst *, struct monst *));
 
@@ -51,9 +53,9 @@ const char *name; /* if null, then format `obj' */
             || !strncmpi(name, "a ", 2))
             kprefix = KILLED_BY;
     }
-    onm = (obj && obj_is_pname(obj)) ? the(name) : (obj && obj->quan > 1L)
-                                                       ? name
-                                                       : an(name);
+    onm = (obj && obj_is_pname(obj)) ? the(name)
+          : (obj && obj->quan > 1L) ? name
+            : an(name);
     is_acid = (obj && obj->otyp == ACID_VENOM);
 
     if (u.uac + tlev <= rnd(20)) {
@@ -73,9 +75,9 @@ const char *name; /* if null, then format `obj' */
             pline_The("silver sears your flesh!");
             exercise(A_CON, FALSE);
         }
-        if (is_acid && Acid_resistance)
+        if (is_acid && Acid_resistance) {
             pline("It doesn't seem to hurt you.");
-        else {
+        else {
             if (is_acid)
                 pline("It burns!");
             losehp(dam, knm, kprefix); /* acid damage */
@@ -116,8 +118,7 @@ int x, y;
         if (down_gate(x, y) != -1)
             objgone = ship_object(obj, x, y, FALSE);
         if (!objgone) {
-            if (!flooreffects(obj, x, y,
-                              "fall")) { /* don't double-dip on damage */
+            if (!flooreffects(obj, x, y, "fall")) {
                 place_object(obj, x, y);
                 if (!mtmp && x == u.ux && y == u.uy)
                     mtmp = &youmonst;
@@ -137,6 +138,150 @@ STATIC_OVL struct monst *target = 0;
 /* The monster that's doing the shooting/throwing */
 STATIC_OVL struct monst *archer = 0;
 
+/* calculate multishot volley count for mtmp throwing otmp (if not ammo) or
+   shooting otmp with mwep (if otmp is ammo and mwep appropriate launcher) */
+STATIC_OVL int
+monmulti(mtmp, otmp, mwep)
+struct monst *mtmp;
+struct obj *otmp, *mwep;
+{
+    int skill = (int) objects[otmp->otyp].oc_skill;
+    int multishot = 1;
+
+    if (otmp->quan > 1L /* no point checking if there's only 1 */
+        /* ammo requires corresponding launcher be wielded */
+        && (is_ammo(otmp)
+               ? matching_launcher(otmp, mwep)
+               /* otherwise any stackable (non-ammo) weapon */
+               : otmp->oclass == WEAPON_CLASS)
+        && !mtmp->mconf) {
+        /* Assumes lords are skilled, princes are expert */
+        if (is_prince(mtmp->data))
+            multishot += 2;
+        else if (is_lord(mtmp->data))
+            multishot++;
+        /* fake players treated as skilled (regardless of role limits) */
+        else if (is_mplayer(mtmp->data))
+            multishot++;
+
+        /* this portion is different from hero multishot; from slash'em?
+         */
+        /* Elven Craftsmanship makes for light, quick bows */
+        if (otmp->otyp == ELVEN_ARROW && !otmp->cursed)
+            multishot++;
+        if (ammo_and_launcher(otmp, uwep) && mwep->otyp == ELVEN_BOW
+            && !mwep->cursed)
+            multishot++;
+        /* 1/3 of launcher enchantment */
+        if (ammo_and_launcher(otmp, mwep) && mwep->spe > 1)
+            multishot += (long) rounddiv(mwep->spe, 3);
+        /* Some randomness */
+        multishot = (long) rnd((int) multishot);
+
+        /* class bonus */
+        switch (monsndx(mtmp->data)) {
+        case PM_CAVEMAN: /* give bonus for low-tech gear */
+            if (skill == -P_SLING || skill == P_SPEAR)
+                multishot++;
+            break;
+        case PM_MONK: /* allow higher volley count */
+            if (skill == -P_SHURIKEN)
+                multishot++;
+            break;
+        case PM_RANGER:
+            if (skill != P_DAGGER)
+                multishot++;
+            break;
+        case PM_ROGUE:
+            if (skill == P_DAGGER)
+                multishot++;
+            break;
+        case PM_NINJA:
+            if (skill == -P_SHURIKEN || skill == -P_DART)
+                multishot++;
+            /*FALLTHRU*/
+        case PM_SAMURAI:
+            if (otmp->otyp == YA && mwep->otyp == YUMI)
+                multishot++;
+            break;
+        default:
+            break;
+        }
+        /* racial bonus */
+        if ((is_elf(mtmp->data) && otmp->otyp == ELVEN_ARROW
+            && mwep->otyp == ELVEN_BOW)
+            || (is_orc(mtmp->data) && otmp->otyp == ORCISH_ARROW
+                && mwep->otyp == ORCISH_BOW)
+            || (is_gnome(mtmp->data) && otmp->otyp == CROSSBOW_BOLT
+                && mwep->otyp == CROSSBOW))
+            multishot++;
+    }
+
+    if (otmp->quan < multishot)
+        multishot = (int) otmp->quan;
+    if (multishot < 1)
+        multishot = 1;
+    return multishot;
+}
+
+/* mtmp throws otmp, or shoots otmp with mwep, at hero or at monster mtarg */
+STATIC_OVL void
+monshoot(mtmp, otmp, mwep)
+struct monst *mtmp;
+struct obj *otmp, *mwep;
+{
+    struct monst *mtarg = target;
+    int dm = distmin(mtmp->mx, mtmp->my,
+                     mtarg ? mtarg->mx : mtmp->mux,
+                     mtarg ? mtarg->my : mtmp->muy),
+        multishot = monmulti(mtmp, otmp, mwep);
+        /*
+         * Caller must have called linedup() to set up tbx, tby.
+         */
+
+    if (canseemon(mtmp)) {
+        const char *onm;
+        char onmbuf[BUFSZ], trgbuf[BUFSZ];
+
+        if (multishot > 1) {
+            /* "N arrows"; multishot > 1 implies otmp->quan > 1, so
+               xname()'s result will already be pluralized */
+            Sprintf(onmbuf, "%d %s", multishot, xname(otmp));
+            onm = onmbuf;
+        } else {
+            /* "an arrow" */
+            onm = singular(otmp, xname);
+            onm = obj_is_pname(otmp) ? the(onm) : an(onm);
+        }
+        m_shot.s = ammo_and_launcher(otmp, mwep) ? TRUE : FALSE;
+        Strcpy(trgbuf, mtarg ? mon_nam(mtarg) : "");
+        if (!strcmp(trgbuf, "it"))
+            Strcpy(trgbuf, humanoid(mtmp->data) ? "someone" : something);
+        pline("%s %s %s%s%s!", Monnam(mtmp),
+              m_shot.s ? "shoots" : "throws", onm,
+              mtarg ? " at " : "", trgbuf);
+        m_shot.o = otmp->otyp;
+    } else {
+        m_shot.o = STRANGE_OBJECT; /* don't give multishot feedback */
+    }
+    m_shot.n = multishot;
+    for (m_shot.i = 1; m_shot.i <= m_shot.n; m_shot.i++) {
+        m_throw(mtmp, mtmp->mx, mtmp->my, sgn(tbx), sgn(tby), dm, otmp);
+        /* conceptually all N missiles are in flight at once, but
+           if mtmp gets killed (shot kills adjacent gas spore and
+           triggers explosion, perhaps), inventory will be dropped
+           and otmp might go away via merging into another stack */
+        if (mtmp->mhp <= 0 && m_shot.i < m_shot.n)
+            /* cancel pending shots (perhaps ought to give a message here
+               since we gave one above about throwing/shooting N missiles) */
+            break; /* endmultishot(FALSE); */
+    }
+    /* reset 'm_shot' */
+    m_shot.n = m_shot.i = 0;
+    m_shot.o = STRANGE_OBJECT;
+    m_shot.s = FALSE;
+}
+
 /* an object launched by someone/thing other than player attacks a monster;
    return 1 if the object has stopped moving (hit or its range used up) */
 int
@@ -237,9 +382,7 @@ boolean verbose;    /* give message(s) even when you can't see what happened */
             if (vis || (verbose && !target))
                 pline("%s is %s!", Monnam(mtmp),
                       (nonliving(mtmp->data) || is_vampshifter(mtmp)
-                       || !canspotmon(mtmp))
-                          ? "destroyed"
-                          : "killed");
+                       || !canspotmon(mtmp)) ? "destroyed" : "killed");
             /* don't blame hero for unknown rolling boulder trap */
             if (!context.mon_moving
                 && (otmp->otyp != BOULDER || range >= 0 || otmp->otrapped))
@@ -249,8 +392,8 @@ boolean verbose;    /* give message(s) even when you can't see what happened */
         }
 
         if (can_blnd((struct monst *) 0, mtmp,
-                     (uchar) (otmp->otyp == BLINDING_VENOM ? AT_SPIT
-                                                           : AT_WEAP),
+                     (uchar) ((otmp->otyp == BLINDING_VENOM) ? AT_SPIT
+                                                             : AT_WEAP),
                      otmp)) {
             if (vis && mtmp->mcansee)
                 pline("%s is blinded by %s.", Monnam(mtmp), the(xname(otmp)));
@@ -280,7 +423,7 @@ struct obj *obj;         /* missile (or stack providing it) */
     struct monst *mtmp;
     struct obj *singleobj;
     char sym = obj->oclass;
-    int hitu, oldumort, blindinc = 0;
+    int hitu = 0, oldumort, blindinc = 0;
 
     bhitpos.x = x;
     bhitpos.y = y;
@@ -372,9 +515,10 @@ struct obj *obj;         /* missile (or stack providing it) */
                     You(
                      "accept %s gift in the spirit in which it was intended.",
                         s_suffix(mon_nam(mon)));
-                    (void) hold_another_object(
-                        singleobj, "You catch, but drop, %s.",
-                        xname(singleobj), "You catch:");
+                    (void) hold_another_object(singleobj,
+                                               "You catch, but drop, %s.",
+                                               xname(singleobj),
+                                               "You catch:");
                 }
                 break;
             }
@@ -430,7 +574,7 @@ struct obj *obj;         /* missile (or stack providing it) */
                          (u.umortality > oldumort) ? 0 : 10, TRUE);
             }
             if (hitu && can_blnd((struct monst *) 0, &youmonst,
-                                 (uchar) (singleobj->otyp == BLINDING_VENOM
+                                 (uchar) ((singleobj->otyp == BLINDING_VENOM)
                                              ? AT_SPIT
                                              : AT_WEAP),
                                  singleobj)) {
@@ -461,8 +605,8 @@ struct obj *obj;         /* missile (or stack providing it) */
                 }
             }
             stop_occupation();
-            if (hitu || !range) {
-                (void) drop_throw(singleobj, hitu, u.ux, u.uy);
+            if (hitu) {
+                range = 0;
                 break;
             }
         }
@@ -481,8 +625,12 @@ struct obj *obj;         /* missile (or stack providing it) */
                              !rn2(5), 0))
             /* Thrown objects "sink" */
             || IS_SINK(levl[bhitpos.x][bhitpos.y].typ)) {
-            if (singleobj) /* hits_bars might have destroyed it */
+            if (singleobj) { /* hits_bars might have destroyed it */
+                if (m_shot.n > 1 && (cansee(bhitpos.x, bhitpos.y)
+                                     || (archer && canseemon(archer))))
+                    pline("%s misses.", The(mshot_xname(singleobj)));
                 (void) drop_throw(singleobj, 0, bhitpos.x, bhitpos.y);
+            }
             break;
         }
         tmp_at(bhitpos.x, bhitpos.y);
@@ -500,121 +648,47 @@ struct obj *obj;         /* missile (or stack providing it) */
     }
 }
 
+/* Monster throws item at another monster */
 int
-thrwmm(mtmp, mtarg)             /* Monster throws item at monster */
+thrwmm(mtmp, mtarg)
 struct monst *mtmp, *mtarg;
 {
     struct obj *otmp, *mwep;
     register xchar x, y;
     boolean ispole;
-    schar skill;
-    int multishot = 1;
 
     /* Polearms won't be applied by monsters against other monsters */
     if (mtmp->weapon_check == NEED_WEAPON || !MON_WEP(mtmp)) {
         mtmp->weapon_check = NEED_RANGED_WEAPON;
         /* mon_wield_item resets weapon_check as appropriate */
-        if(mon_wield_item(mtmp) != 0) return 0;
+        if (mon_wield_item(mtmp) != 0)
+            return 0;
     }
 
     /* Pick a weapon */
     otmp = select_rwep(mtmp);
-    if (!otmp) return 0;
+    if (!otmp)
+        return 0;
     ispole = is_pole(otmp);
-    skill = objects[otmp->otyp].oc_skill;
 
     x = mtmp->mx;
     y = mtmp->my;
 
     mwep = MON_WEP(mtmp); /* wielded weapon */
 
-    if(!ispole && m_lined_up(mtarg, mtmp)) {
-        /* WAC Catch this since rn2(0) is illegal */
-        int chance = (BOLT_LIM-distmin(x,y,mtarg->mx,mtarg->my) > 0) ?
-            BOLT_LIM-distmin(x,y,mtarg->mx,mtarg->my) : 1;
-
-        if(!mtarg->mflee || !rn2(chance)) {
-            const char *verb = "throws";
-
-            if (otmp->otyp == ARROW
-                || otmp->otyp == ELVEN_ARROW
-                || otmp->otyp == ORCISH_ARROW
-                || otmp->otyp == YA
-                || otmp->otyp == CROSSBOW_BOLT) verb = "shoots";
-
-            if (ammo_and_launcher(otmp, mwep) && is_launcher(mwep)) {
-                if (dist2(mtmp->mx, mtmp->my, mtarg->mx, mtarg->my) >
-                    PET_MISSILE_RANGE2)
-                    return 0; /* Out of range */
-            }
-
-            if (canseemon(mtmp)) {
-                pline("%s %s %s!", Monnam(mtmp), verb,
-                      obj_is_pname(otmp) ?
-                      the(singular(otmp, xname)) :
-                      an(singular(otmp, xname)));
-            }
-
-            /* Multishot calculations */
-            if ((ammo_and_launcher(otmp, mwep) || skill == P_DAGGER
-                 || skill == -P_DART || skill == -P_SHURIKEN)
-                && !mtmp->mconf) {
-                /* Assumes lords are skilled, princes are expert */
-                if (is_lord(mtmp->data)) multishot++;
-                if (is_prince(mtmp->data)) multishot += 2;
-
-                /*  Elven Craftsmanship makes for light,  quick bows */
-                if (otmp->otyp == ELVEN_ARROW && !otmp->cursed)
-                    multishot++;
-                if (mwep && mwep->otyp == ELVEN_BOW &&
-                    !mwep->cursed) multishot++;
-                /* 1/3 of object enchantment */
-                if (mwep && mwep->spe > 1)
-                    multishot += (long) rounddiv(mwep->spe,3);
-                /* Some randomness */
-                if (multishot > 1L)
-                    multishot = (long) rnd((int) multishot);
-
-                switch (monsndx(mtmp->data)) {
-                case PM_RANGER:
-                    multishot++;
-                    break;
-                case PM_ROGUE:
-                    if (skill == P_DAGGER) multishot++;
-                    break;
-                case PM_SAMURAI:
-                    if (otmp->otyp == YA && mwep &&
-                        mwep->otyp == YUMI) multishot++;
-                    break;
-                default:
-                    break;
-                }
-                { /* racial bonus */
-                    if (is_elf(mtmp->data) &&
-                        otmp->otyp == ELVEN_ARROW &&
-                        mwep && mwep->otyp == ELVEN_BOW)
-                        multishot++;
-                    else if (is_orc(mtmp->data) &&
-                             otmp->otyp == ORCISH_ARROW &&
-                             mwep && mwep->otyp == ORCISH_BOW)
-                        multishot++;
-                }
-
-            }
-            if (otmp->quan < multishot) multishot = (int)otmp->quan;
-            if (multishot < 1) multishot = 1;
+    if (!ispole && m_lined_up(mtarg, mtmp)) {
+        int chance = max(BOLT_LIM - distmin(x, y, mtarg->mx, mtarg->my), 1);
 
+        if (!mtarg->mflee || !rn2(chance)) {
+            if (ammo_and_launcher(otmp, mwep)
+                && dist2(mtmp->mx, mtmp->my, mtarg->mx, mtarg->my)
+                   > PET_MISSILE_RANGE2)
+                return 0; /* Out of range */
             /* Set target monster */
             target = mtarg;
             archer = mtmp;
-            while (multishot-- > 0)
-                m_throw(mtmp, mtmp->mx, mtmp->my,
-                        sgn(tbx), sgn(tby),
-                        distmin(mtmp->mx, mtmp->my,
-                                mtarg->mx, mtarg->my),
-                        otmp);
-            archer = (struct monst *)0;
-            target = (struct monst *)0;
+            monshoot(mtmp, otmp, mwep); /* multishot shooting or throwing */
+            archer = target = (struct monst *) 0;
             nomul(0);
             return 1;
         }
@@ -622,7 +696,6 @@ struct monst *mtmp, *mtarg;
     return 0;
 }
 
-
 /* monster spits substance at monster */
 int
 spitmm(mtmp, mattk, mtarg)
@@ -675,8 +748,9 @@ struct attack *mattk;
     return 0;
 }
 
+/* monster breathes at monster (ranged) */
 int
-breamm(mtmp, mattk, mtarg)        /* monster breathes at monster (ranged) */
+breamm(mtmp, mattk, mtarg)
 struct monst *mtmp, *mtarg;
 struct attack  *mattk;
 {
@@ -691,20 +765,19 @@ struct attack  *mattk;
                 else
                     You_hear("a cough.");
             }
-            return(0);
+            return 0;
         }
         if (!mtmp->mspec_used && rn2(3)) {
             if ((typ >= AD_MAGM) && (typ <= AD_ACID)) {
                 if (canseemon(mtmp))
-                    pline("%s breathes %s!", Monnam(mtmp),
-                          breathwep[typ-1]);
-                dobuzz((int) (-20 - (typ-1)), (int)mattk->damn,
+                    pline("%s breathes %s!", Monnam(mtmp), breathwep[typ - 1]);
+                dobuzz((int) (-20 - (typ - 1)), (int)mattk->damn,
                        mtmp->mx, mtmp->my, sgn(tbx), sgn(tby), FALSE);
                 nomul(0);
                 /* breath runs out sometimes. Also, give monster some
                  * cunning; don't breath if the target fell asleep.
                  */
-                mtmp->mspec_used = 6+rn2(18);
+                mtmp->mspec_used = 6 + rn2(18);
 
                 /* If this is a pet, it'll get hungry. Minions and
                  * spell beings won't hunger */
@@ -717,9 +790,9 @@ struct attack  *mattk;
                 }
             } else impossible("Breath weapon %d used", typ-1);
         } else
-            return (0);
+            return 0;
     }
-    return(1);
+    return 1;
 }
 
 
@@ -762,7 +835,6 @@ struct monst *mtmp;
 {
     struct obj *otmp, *mwep;
     xchar x, y;
-    int multishot;
     const char *onm;
 
     /* Rearranged beginning so monsters can use polearms not in a line */
@@ -821,101 +893,7 @@ struct monst *mtmp;
         return;
 
     mwep = MON_WEP(mtmp); /* wielded weapon */
-
-    /* Multishot calculations */
-    multishot = 1;
-    if (otmp->quan > 1L /* no point checking if there's only 1 */
-        /* ammo requires corresponding launcher be wielded */
-        && (is_ammo(otmp)
-               ? matching_launcher(otmp, mwep)
-               /* otherwise any stackable (non-ammo) weapon */
-               : otmp->oclass == WEAPON_CLASS)
-        && !mtmp->mconf) {
-        int skill = (int) objects[otmp->otyp].oc_skill;
-
-        /* Assumes lords are skilled, princes are expert */
-        if (is_prince(mtmp->data))
-            multishot += 2;
-        else if (is_lord(mtmp->data))
-            multishot++;
-        /* fake players treated as skilled (regardless of role limits) */
-        else if (is_mplayer(mtmp->data))
-            multishot++;
-
-        /* class bonus */
-        switch (monsndx(mtmp->data)) {
-        case PM_MONK:
-            if (skill == -P_SHURIKEN)
-                multishot++;
-            break;
-        case PM_RANGER:
-            multishot++;
-            break;
-        case PM_ROGUE:
-            if (skill == P_DAGGER)
-                multishot++;
-            break;
-        case PM_NINJA:
-            if (skill == -P_SHURIKEN || skill == -P_DART)
-                multishot++;
-            /*FALLTHRU*/
-        case PM_SAMURAI:
-            if (otmp->otyp == YA && mwep && mwep->otyp == YUMI)
-                multishot++;
-            break;
-        default:
-            break;
-        }
-        /* racial bonus */
-        if ((is_elf(mtmp->data) && otmp->otyp == ELVEN_ARROW && mwep
-             && mwep->otyp == ELVEN_BOW)
-            || (is_orc(mtmp->data) && otmp->otyp == ORCISH_ARROW && mwep
-                && mwep->otyp == ORCISH_BOW))
-            multishot++;
-
-        multishot = rnd(multishot);
-        if ((long) multishot > otmp->quan)
-            multishot = (int) otmp->quan;
-    }
-
-    if (canseemon(mtmp)) {
-        char onmbuf[BUFSZ];
-
-        if (multishot > 1) {
-            /* "N arrows"; multishot > 1 implies otmp->quan > 1, so
-               xname()'s result will already be pluralized */
-            Sprintf(onmbuf, "%d %s", multishot, xname(otmp));
-            onm = onmbuf;
-        } else {
-            /* "an arrow" */
-            onm = singular(otmp, xname);
-            onm = obj_is_pname(otmp) ? the(onm) : an(onm);
-        }
-        m_shot.s = ammo_and_launcher(otmp, mwep) ? TRUE : FALSE;
-        pline("%s %s %s!", Monnam(mtmp), m_shot.s ? "shoots" : "throws", onm);
-        m_shot.o = otmp->otyp;
-    } else {
-        m_shot.o = STRANGE_OBJECT; /* don't give multishot feedback */
-    }
-
-    m_shot.n = multishot;
-    for (m_shot.i = 1; m_shot.i <= m_shot.n; m_shot.i++) {
-        m_throw(mtmp, mtmp->mx, mtmp->my, sgn(tbx), sgn(tby),
-                distmin(mtmp->mx, mtmp->my, mtmp->mux, mtmp->muy), otmp);
-        /* conceptually all N missiles are in flight at once, but
-           if mtmp gets killed (shot kills adjacent gas spore and
-           triggers explosion, perhaps), inventory will be dropped
-           and otmp might go away via merging into another stack */
-        if (mtmp->mhp <= 0 && m_shot.i < m_shot.n) {
-            /* cancel pending shots (ought to give a message here since
-               we gave one above about throwing/shooting N missiles) */
-            break; /* endmultishot(FALSE); */
-        }
-    }
-    m_shot.n = m_shot.i = 0;
-    m_shot.o = STRANGE_OBJECT;
-    m_shot.s = FALSE;
-
+    monshoot(mtmp, otmp, mwep); /* multishot shooting or throwing */
     nomul(0);
 }