OSDN Git Service

fix #48255
[jnethack/source.git] / src / uhitm.c
index 5435828..3129358 100644 (file)
@@ -1,11 +1,11 @@
-/* NetHack 3.6 uhitm.c $NHDT-Date: 1521684760 2018/03/22 02:12:40 $  $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.176 $ */
+/* NetHack 3.6 uhitm.c $NHDT-Date: 1573764936 2019/11/14 20:55:36 $  $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.215 $ */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
 /*-Copyright (c) Robert Patrick Rankin, 2012. */
 /* NetHack may be freely redistributed.  See license for details. */
 
 /* JNetHack Copyright */
 /* (c) Issei Numata, Naoki Hamada, Shigehiro Miyashita, 1994-2000  */
-/* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2016            */
+/* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2023            */
 /* JNetHack may be freely redistributed.  See license for details. */
 
 #include "hack.h"
@@ -106,7 +106,7 @@ attack_checks(mtmp, wep)
 register struct monst *mtmp;
 struct obj *wep; /* uwep for attack(), null for kick_monster() */
 {
-    char qbuf[QBUFSZ];
+    int glyph;
 
     /* if you're close enough to attack, alert any waiting monster */
     mtmp->mstrategy &= ~STRAT_WAITMASK;
@@ -129,6 +129,12 @@ struct obj *wep; /* uwep for attack(), null for kick_monster() */
         return FALSE;
     }
 
+    /* cache the shown glyph;
+       cases which might change it (by placing or removing
+       'rembered, unseen monster' glyph or revealing a mimic)
+       always return without further reference to this */
+    glyph = glyph_at(bhitpos.x, bhitpos.y);
+
     /* Put up an invisible monster marker, but with exceptions for
      * monsters that hide and monsters you've been warned about.
      * The former already prints a warning message and
@@ -137,8 +143,8 @@ struct obj *wep; /* uwep for attack(), null for kick_monster() */
      * happening two turns in a row.  The latter shows a glyph on
      * the screen, so you know something is there.
      */
-    if (!canspotmon(mtmp) && !glyph_is_warning(glyph_at(bhitpos.x, bhitpos.y))
-        && !glyph_is_invisible(levl[bhitpos.x][bhitpos.y].glyph)
+    if (!canspotmon(mtmp)
+        && !glyph_is_warning(glyph) && !glyph_is_invisible(glyph)
         && !(!Blind && mtmp->mundetected && hides_under(mtmp->data))) {
 /*JP
         pline("Wait!  There's %s there you can't see!", something);
@@ -148,23 +154,29 @@ struct obj *wep; /* uwep for attack(), null for kick_monster() */
         /* if it was an invisible mimic, treat it as if we stumbled
          * onto a visible mimic
          */
-        if (mtmp->m_ap_type && !Protection_from_shape_changers
+        if (M_AP_TYPE(mtmp) && !Protection_from_shape_changers
             /* applied pole-arm attack is too far to get stuck */
             && distu(mtmp->mx, mtmp->my) <= 2) {
             if (!u.ustuck && !mtmp->mflee && dmgtype(mtmp->data, AD_STCK))
                 u.ustuck = mtmp;
         }
+        /* #H7329 - if hero is on engraved "Elbereth", this will end up
+         * assessing an alignment penalty and removing the engraving
+         * even though no attack actually occurs.  Since it also angers
+         * peacefuls, we're operating as if an attack attempt did occur
+         * and the Elbereth behavior is consistent.
+         */
         wakeup(mtmp, TRUE); /* always necessary; also un-mimics mimics */
         return TRUE;
     }
 
-    if (mtmp->m_ap_type && !Protection_from_shape_changers && !sensemon(mtmp)
-        && !glyph_is_warning(glyph_at(bhitpos.x, bhitpos.y))) {
+    if (M_AP_TYPE(mtmp) && !Protection_from_shape_changers && !sensemon(mtmp)
+        && !glyph_is_warning(glyph)) {
         /* If a hidden mimic was in a square where a player remembers
          * some (probably different) unseen monster, the player is in
          * luck--he attacks it even though it's hidden.
          */
-        if (glyph_is_invisible(levl[mtmp->mx][mtmp->my].glyph)) {
+        if (glyph_is_invisible(glyph)) {
             seemimic(mtmp);
             return FALSE;
         }
@@ -173,24 +185,32 @@ struct obj *wep; /* uwep for attack(), null for kick_monster() */
     }
 
     if (mtmp->mundetected && !canseemon(mtmp)
-        && !glyph_is_warning(glyph_at(bhitpos.x, bhitpos.y))
+        && !glyph_is_warning(glyph)
         && (hides_under(mtmp->data) || mtmp->data->mlet == S_EEL)) {
         mtmp->mundetected = mtmp->msleeping = 0;
         newsym(mtmp->mx, mtmp->my);
-        if (glyph_is_invisible(levl[mtmp->mx][mtmp->my].glyph)) {
+        if (glyph_is_invisible(glyph)) {
             seemimic(mtmp);
             return FALSE;
         }
         if (!((Blind ? Blind_telepat : Unblind_telepat) || Detect_monsters)) {
             struct obj *obj;
 
-            if (Blind || (is_pool(mtmp->mx, mtmp->my) && !Underwater))
+            if (!Blind && Hallucination)
+#if 0 /*JP:T*/
+                pline("A %s %s appeared!",
+                      mtmp->mtame ? "tame" : "wild", l_monnam(mtmp));
+#else
+                pline("%s%s\82ª\8c»\82ê\82½\81I",
+                      mtmp->mtame ? "\8eè\82È\82Ã\82¯\82ç\82ê\82½" : "\96ì\90\82Ì", l_monnam(mtmp));
+#endif
+            else if (Blind || (is_pool(mtmp->mx, mtmp->my) && !Underwater))
 /*JP
                 pline("Wait!  There's a hidden monster there!");
 */
                 pline("\91Ò\82Ä\81I\89ö\95¨\82ª\89B\82ê\82Ä\82¢\82é\81I");
             else if ((obj = level.objects[mtmp->mx][mtmp->my]) != 0)
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                 pline("Wait!  There's %s hiding under %s!",
                       an(l_monnam(mtmp)), doname(obj));
 #else
@@ -205,19 +225,21 @@ struct obj *wep; /* uwep for attack(), null for kick_monster() */
      * make sure to wake up a monster from the above cases if the
      * hero can sense that the monster is there.
      */
-    if ((mtmp->mundetected || mtmp->m_ap_type) && sensemon(mtmp)) {
+    if ((mtmp->mundetected || M_AP_TYPE(mtmp)) && sensemon(mtmp)) {
         mtmp->mundetected = 0;
         wakeup(mtmp, TRUE);
     }
 
-    if (flags.confirm && mtmp->mpeaceful && !Confusion && !Hallucination
-        && !Stunned) {
+    if (flags.confirm && mtmp->mpeaceful
+        && !Confusion && !Hallucination && !Stunned) {
         /* Intelligent chaotic weapons (Stormbringer) want blood */
         if (wep && wep->oartifact == ART_STORMBRINGER) {
             override_confirmation = TRUE;
             return FALSE;
         }
         if (canspotmon(mtmp)) {
+            char qbuf[QBUFSZ];
+
 /*JP
             Sprintf(qbuf, "Really attack %s?", mon_nam(mtmp));
 */
@@ -351,23 +373,26 @@ register struct monst *mtmp;
     /* Intelligent chaotic weapons (Stormbringer) want blood */
     if (is_safepet(mtmp) && !context.forcefight) {
         if (!uwep || uwep->oartifact != ART_STORMBRINGER) {
-            /* there are some additional considerations: this won't work
+            /* There are some additional considerations: this won't work
              * if in a shop or Punished or you miss a random roll or
              * if you can walk thru walls and your pet cannot (KAA) or
-             * if your pet is a long worm (unless someone does better).
-             * there's also a chance of displacing a "frozen" monster.
+             * if your pet is a long worm with a tail.
+             * There's also a chance of displacing a "frozen" monster:
              * sleeping monsters might magically walk in their sleep.
              */
-            boolean foo = (Punished || !rn2(7) || is_longworm(mtmp->data)),
+            boolean foo = (Punished || !rn2(7)
+                           || (is_longworm(mtmp->data) && mtmp->wormno)),
                     inshop = FALSE;
             char *p;
 
-            for (p = in_rooms(mtmp->mx, mtmp->my, SHOPBASE); *p; p++)
-                if (tended_shop(&rooms[*p - ROOMOFFSET])) {
-                    inshop = TRUE;
-                    break;
-                }
-
+            /* only check for in-shop if don't already have reason to stop */
+            if (!foo) {
+                for (p = in_rooms(mtmp->mx, mtmp->my, SHOPBASE); *p; p++)
+                    if (tended_shop(&rooms[*p - ROOMOFFSET])) {
+                        inshop = TRUE;
+                        break;
+                    }
+            }
             if (inshop || foo || (IS_ROCK(levl[u.ux][u.uy].typ)
                                   && !passes_walls(mtmp->data))) {
                 char buf[BUFSZ];
@@ -379,7 +404,8 @@ register struct monst *mtmp;
                 You("stop.  %s is in the way!", buf);
 */
                 You("\8e~\82Ü\82Á\82½\81D%s\82ª\93¹\82É\82¢\82é\81I", buf);
-                context.travel = context.travel1 = context.mv = context.run = 0;
+                context.travel = context.travel1 = context.mv = context.run
+                    = 0;
                 return TRUE;
             } else if ((mtmp->mfrozen || (!mtmp->mcanmove)
                         || (mtmp->data->mmove == 0)) && rn2(6)) {
@@ -387,7 +413,8 @@ register struct monst *mtmp;
                 pline("%s doesn't seem to move!", Monnam(mtmp));
 */
                 pline("%s\82Í\93®\82¯\82È\82¢\82æ\82¤\82¾\81I", Monnam(mtmp));
-                context.travel = context.travel1 = context.mv = context.run = 0;
+                context.travel = context.travel1 = context.mv = context.run
+                    = 0;
                 return TRUE;
             } else
                 return FALSE;
@@ -401,6 +428,7 @@ register struct monst *mtmp;
        it uses bhitpos instead; it might map an invisible monster there */
     bhitpos.x = u.ux + u.dx;
     bhitpos.y = u.uy + u.dy;
+    notonhead = (bhitpos.x != mtmp->mx || bhitpos.y != mtmp->my);
     if (attack_checks(mtmp, uwep))
         return TRUE;
 
@@ -434,7 +462,7 @@ register struct monst *mtmp;
 */
                 You("%s\82Å\89ö\95¨\82ð\82È\82®\82è\82Â\82¯\82½\81D", yname(uwep));
             else if (!cantwield(youmonst.data))
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                 You("begin %s monsters with your %s %s.",
                     ing_suffix(Role_if(PM_MONK) ? "strike" : "bash"),
                     uarmg ? "gloved" : "bare", /* Del Lamb */
@@ -456,8 +484,13 @@ register struct monst *mtmp;
         && !mtmp->mconf && mtmp->mcansee && !rn2(7)
         && (m_move(mtmp, 0) == 2 /* it died */
             || mtmp->mx != u.ux + u.dx
-            || mtmp->my != u.uy + u.dy)) /* it moved */
+            || mtmp->my != u.uy + u.dy)) { /* it moved */
+/*JP
+        You("miss wildly and stumble forwards.");
+*/
+        You("\91å\82«\82­\8aO\82µ\82Ä\91O\82É\82Â\82Ü\82Ã\82¢\82½\81D");
         return FALSE;
+    }
 
     if (Upolyd)
         (void) hmonas(mtmp);
@@ -465,13 +498,13 @@ register struct monst *mtmp;
         (void) hitum(mtmp, youmonst.data->mattk);
     mtmp->mstrategy &= ~STRAT_WAITMASK;
 
-atk_done:
+ atk_done:
     /* see comment in attack_checks() */
     /* we only need to check for this if we did an attack_checks()
      * and it returned 0 (it's okay to attack), and the monster didn't
      * evade.
      */
-    if (context.forcefight && mtmp->mhp > 0 && !canspotmon(mtmp)
+    if (context.forcefight && !DEADMONSTER(mtmp) && !canspotmon(mtmp)
         && !glyph_is_invisible(levl[u.ux + u.dx][u.uy + u.dy].glyph)
         && !(u.uswallow && mtmp == u.ustuck))
         map_invisible(u.ux + u.dx, u.uy + u.dy);
@@ -489,7 +522,9 @@ int rollneeded, armorpenalty; /* for monks */
 struct attack *uattk;
 int dieroll;
 {
-    register boolean malive = TRUE;
+    boolean malive = TRUE,
+            /* hmon() might destroy weapon; remember aspect for cutworm */
+            slice_or_chop = (weapon && (is_blade(weapon) || is_axe(weapon)));
 
     if (override_confirmation) {
         /* this may need to be generalized if weapons other than
@@ -504,7 +539,7 @@ int dieroll;
     if (!*mhit) {
         missum(mon, uattk, (rollneeded + armorpenalty > dieroll));
     } else {
-        int oldhp = mon->mhp, x = u.ux + u.dx, y = u.uy + u.dy;
+        int oldhp = mon->mhp;
         long oldweaphit = u.uconduct.weaphit;
 
         /* KMH, conduct */
@@ -513,7 +548,7 @@ int dieroll;
 
         /* we hit the monster; be careful: it might die or
            be knocked into a different location */
-        notonhead = (mon->mx != x || mon->my != y);
+        notonhead = (mon->mx != bhitpos.x || mon->my != bhitpos.y);
         malive = hmon(mon, weapon, HMON_MELEE, dieroll);
         if (malive) {
             /* monster still alive */
@@ -533,7 +568,7 @@ int dieroll;
                 u.uconduct.weaphit = oldweaphit;
             }
             if (mon->wormno && *mhit)
-                cutworm(mon, x, y, weapon);
+                cutworm(mon, bhitpos.x, bhitpos.y, slice_or_chop);
         }
     }
     return malive;
@@ -553,6 +588,7 @@ struct attack *uattk; /* ... but we don't enforce that here; Null works ok */
        simulation attempt a bit */
     static boolean clockwise = FALSE;
     unsigned i;
+    coord save_bhitpos;
     int count, umort, x = u.ux, y = u.uy;
 
     /* find the direction toward primary target */
@@ -569,6 +605,7 @@ struct attack *uattk; /* ... but we don't enforce that here; Null works ok */
        to primary target */
     i = (i + (clockwise ? 6 : 2)) % 8;
     umort = u.umortality; /* used to detect life-saving */
+    save_bhitpos = bhitpos;
 
     /*
      * Three attacks:  adjacent to primary, primary, adjacent on other
@@ -599,6 +636,7 @@ struct attack *uattk; /* ... but we don't enforce that here; Null works ok */
                                &attknum, &armorpenalty);
         dieroll = rnd(20);
         mhit = (tmp > dieroll);
+        bhitpos.x = tx, bhitpos.y = ty; /* normally set up by attack() */
         (void) known_hitum(mtmp, uwep, &mhit, tmp, armorpenalty,
                            uattk, dieroll);
         (void) passive(mtmp, uwep, mhit, !DEADMONSTER(mtmp), AT_WEAP, !uwep);
@@ -610,6 +648,8 @@ struct attack *uattk; /* ... but we don't enforce that here; Null works ok */
     }
     /* set up for next time */
     clockwise = !clockwise; /* alternate */
+    bhitpos = save_bhitpos; /* in case somebody relies on bhitpos
+                             * designating the primary target */
 
     /* return False if primary target died, True otherwise; note: if 'target'
        was nonNull upon entry then it's still nonNull even if *target died */
@@ -639,6 +679,7 @@ struct attack *uattk;
 
     if (tmp > dieroll)
         exercise(A_DEX, TRUE);
+    /* bhitpos is set up by caller */
     malive = known_hitum(mon, uwep, &mhit, tmp, armorpenalty, uattk, dieroll);
     if (wepbefore && !uwep)
         wep_was_destroyed = TRUE;
@@ -703,18 +744,18 @@ int dieroll;
     boolean ispoisoned = FALSE, needpoismsg = FALSE, poiskilled = FALSE,
             unpoisonmsg = FALSE;
     boolean silvermsg = FALSE, silverobj = FALSE;
+    boolean lightobj = FALSE;
     boolean valid_weapon_attack = FALSE;
     boolean unarmed = !uwep && !uarm && !uarms;
     boolean hand_to_hand = (thrown == HMON_MELEE
                             /* not grapnels; applied implies uwep */
                             || (thrown == HMON_APPLIED && is_pole(uwep)));
     int jousting = 0;
+    long silverhit = 0L;
     int wtype;
     struct obj *monwep;
-    char unconventional[BUFSZ]; /* substituted for word "attack" in msg */
     char saved_oname[BUFSZ];
 
-    unconventional[0] = '\0';
     saved_oname[0] = '\0';
 
     wakeup(mon, TRUE);
@@ -726,25 +767,20 @@ int dieroll;
         else
             tmp = rnd(2);
         valid_weapon_attack = (tmp > 1);
-        /* blessed gloves give bonuses when fighting 'bare-handed' */
-        if (uarmg && uarmg->blessed
-            && (is_undead(mdat) || is_demon(mdat) || is_vampshifter(mon)))
-            tmp += rnd(4);
-        /* So do silver rings.  Note: rings are worn under gloves, so you
-         * don't get both bonuses.
-         */
-        if (!uarmg) {
-            if (uleft && objects[uleft->otyp].oc_material == SILVER)
-                barehand_silver_rings++;
-            if (uright && objects[uright->otyp].oc_material == SILVER)
-                barehand_silver_rings++;
-            if (barehand_silver_rings && mon_hates_silver(mon)) {
-                tmp += rnd(20);
-                silvermsg = TRUE;
-            }
-        }
+        /* Blessed gloves give bonuses when fighting 'bare-handed'.  So do
+           silver rings.  Note:  rings are worn under gloves, so you don't
+           get both bonuses, and two silver rings don't give double bonus. */
+        tmp += special_dmgval(&youmonst, mon, (W_ARMG | W_RINGL | W_RINGR),
+                              &silverhit);
+        barehand_silver_rings += (((silverhit & W_RINGL) ? 1 : 0)
+                                  + ((silverhit & W_RINGR) ? 1 : 0));
+        if (barehand_silver_rings > 0)
+            silvermsg = TRUE;
     } else {
-        Strcpy(saved_oname, cxname(obj));
+        if (!(artifact_light(obj) && obj->lamplit))
+            Strcpy(saved_oname, cxname(obj));
+        else
+            Strcpy(saved_oname, bare_artifactname(obj));
         if (obj->oclass == WEAPON_CLASS || is_weptool(obj)
             || obj->oclass == GEM_CLASS) {
             /* is it not a melee weapon? */
@@ -773,7 +809,7 @@ int dieroll;
                     && rnl(4) == 4 - 1) {
                     boolean more_than_1 = (obj->quan > 1L);
 
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                     pline("As you hit %s, %s%s breaks into splinters.",
                           mon_nam(mon), more_than_1 ? "one of " : "",
                           yname(obj));
@@ -834,7 +870,7 @@ int dieroll;
                      */
                     setmnotwielded(mon, monwep);
                     mon->weapon_check = NEED_WEAPON;
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                     pline("%s from the force of your blow!",
                           Yobjnam2(monwep, "shatter"));
 #else
@@ -851,7 +887,7 @@ int dieroll;
 
                 if (obj->oartifact
                     && artifact_hit(&youmonst, mon, obj, &tmp, dieroll)) {
-                    if (mon->mhp <= 0) /* artifact killed monster */
+                    if (DEADMONSTER(mon)) /* artifact killed monster */
                         return FALSE;
                     if (tmp == 0)
                         return TRUE;
@@ -862,6 +898,9 @@ int dieroll;
                     silvermsg = TRUE;
                     silverobj = TRUE;
                 }
+                if (artifact_light(obj) && obj->lamplit
+                    && mon_hates_light(mon))
+                    lightobj = TRUE;
                 if (u.usteed && !thrown && tmp > 0
                     && weapon_type(obj) == P_LANCE && mon != u.ustuck) {
                     jousting = joust(mon, obj);
@@ -894,7 +933,7 @@ int dieroll;
             freeinv(obj);
             potionhit(mon, obj,
                       hand_to_hand ? POTHIT_HERO_BASH : POTHIT_HERO_THROW);
-            if (mon->mhp <= 0)
+            if (DEADMONSTER(mon))
                 return FALSE; /* killed */
             hittxt = TRUE;
             /* in case potion effect causes transformation */
@@ -903,9 +942,6 @@ int dieroll;
         } else {
             if (mdat == &mons[PM_SHADE] && !shade_aware(obj)) {
                 tmp = 0;
-#if 0 /*JP*/
-                Strcpy(unconventional, cxname(obj));
-#endif
             } else {
                 switch (obj->otyp) {
                 case BOULDER:         /* 1d20 */
@@ -941,7 +977,7 @@ int dieroll;
                     if (touch_petrifies(&mons[obj->corpsenm])) {
                         tmp = 1;
                         hittxt = TRUE;
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                         You("hit %s with %s.", mon_nam(mon),
                             corpse_xname(obj, (const char *) 0,
                                          obj->dknown ? CXN_PFX_THE
@@ -958,7 +994,7 @@ int dieroll;
                         if (resists_ston(mon))
                             break;
                         /* note: hp may be <= 0 even if munstoned==TRUE */
-                        return (boolean) (mon->mhp > 0);
+                        return (boolean) !DEADMONSTER(mon);
 #if 0
                     } else if (touch_petrifies(mdat)) {
                         ; /* maybe turn the corpse into a statue? */
@@ -969,13 +1005,13 @@ int dieroll;
                     break;
 
 #define useup_eggs(o)                    \
-    {                                    \
+    do {                                 \
         if (thrown)                      \
             obfree(o, (struct obj *) 0); \
         else                             \
             useupall(o);                 \
         o = (struct obj *) 0;            \
-    } /* now gone */
+    } while (0) /* now gone */
                 case EGG: {
                     long cnt = obj->quan;
 
@@ -995,8 +1031,8 @@ int dieroll;
 
                     if (touch_petrifies(&mons[obj->corpsenm])) {
                         /*learn_egg_type(obj->corpsenm);*/
-#if 0 /*JP*/
-                        pline("Splat! You hit %s with %s %s egg%s!",
+#if 0 /*JP:T*/
+                        pline("Splat!  You hit %s with %s %s egg%s!",
                               mon_nam(mon),
                               obj->known ? "the" : cnt > 1L ? "some" : "a",
                               obj->known ? mons[obj->corpsenm].mname
@@ -1015,24 +1051,26 @@ int dieroll;
                             minstapetrify(mon, TRUE);
                         if (resists_ston(mon))
                             break;
-                        return (boolean) (mon->mhp > 0);
+                        return (boolean) (!DEADMONSTER(mon));
                     } else { /* ordinary egg(s) */
-#if 0 /*JP*/
-                        const char *eggp =
-                            (obj->corpsenm != NON_PM && obj->known)
-                                ? the(mons[obj->corpsenm].mname)
-                                : (cnt > 1L) ? "some" : "an";
+#if 0 /*JP:T*/
+                        const char *eggp = (obj->corpsenm != NON_PM
+                                            && obj->known)
+                                           ? the(mons[obj->corpsenm].mname)
+                                           : (cnt > 1L) ? "some" : "an";
+
                         You("hit %s with %s egg%s.", mon_nam(mon), eggp,
                             plur(cnt));
 #else
-                        const char *eggp =
-                            (obj->corpsenm != NON_PM && obj->known)
-                                ? mons[obj->corpsenm].mname : "";
-                            You("%s\82É%s%s\97\91\82ð\93\8a\82°\82Â\82¯\82½\81D",
+                        const char *eggp = (obj->corpsenm != NON_PM
+                                            && obj->known)
+                                           ? mons[obj->corpsenm].mname
+                                           : "";
+                        You("%s\82É%s%s\97\91\82ð\93\8a\82°\82Â\82¯\82½\81D",
                                 mon_nam(mon), eggp, *eggp ? "\82Ì" : "");
 #endif
                         if (touch_petrifies(mdat) && !stale_egg(obj)) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                             pline_The("egg%s %s alive any more...", plur(cnt),
                                       (cnt == 1L) ? "isn't" : "aren't");
 #else
@@ -1070,12 +1108,12 @@ int dieroll;
                 case BLINDING_VENOM:
                     mon->msleeping = 0;
                     if (can_blnd(&youmonst, mon,
-                                 (uchar) (obj->otyp == BLINDING_VENOM
+                                 (uchar) ((obj->otyp == BLINDING_VENOM)
                                              ? AT_SPIT
                                              : AT_WEAP),
                                  obj)) {
                         if (Blind) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                             pline(obj->otyp == CREAM_PIE ? "Splat!"
                                                          : "Splash!");
 #else
@@ -1083,7 +1121,7 @@ int dieroll;
                                                          : "\83s\83`\83\83\83b\81I");
 #endif
                         } else if (obj->otyp == BLINDING_VENOM) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                             pline_The("venom blinds %s%s!", mon_nam(mon),
                                       mon->mcansee ? "" : " further");
 #else
@@ -1106,7 +1144,7 @@ int dieroll;
                                 whom = strcat(s_suffix(whom),
                                               mbodypart(mon, FACE));
 #endif
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                             pline("%s %s over %s!", what,
                                   vtense(what, "splash"), whom);
 #else
@@ -1246,25 +1284,14 @@ int dieroll;
             poiskilled = TRUE;
     }
     if (tmp < 1) {
+        boolean mon_is_shade = (mon->data == &mons[PM_SHADE]);
+
         /* make sure that negative damage adjustment can't result
            in inadvertently boosting the victim's hit points */
-        tmp = 0;
-        if (mdat == &mons[PM_SHADE]) {
-            if (!hittxt) {
-#if 0 /*JP*/
-                const char *what = *unconventional ? unconventional : "attack";
-
-                Your("%s %s harmlessly through %s.", what,
-                     vtense(what, "pass"), mon_nam(mon));
-#else
-                Your("\8dU\8c\82\82Í%s\82ð\92Ê\82è\82Ê\82¯\82½\81D", mon_nam(mon));
-#endif
-                hittxt = TRUE;
-            }
-        } else {
-            if (get_dmg_bonus)
-                tmp = 1;
-        }
+        tmp = (get_dmg_bonus && !mon_is_shade) ? 1 : 0;
+        if (mon_is_shade && !hittxt
+            && thrown != HMON_THROWN && thrown != HMON_KICKED)
+            hittxt = shade_miss(&youmonst, mon, obj, FALSE, TRUE);
     }
 
     if (jousting) {
@@ -1299,7 +1326,7 @@ int dieroll;
         if (rnd(100) < P_SKILL(P_BARE_HANDED_COMBAT) && !bigmonst(mdat)
             && !thick_skinned(mdat)) {
             if (canspotmon(mon))
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                 pline("%s %s from your powerful strike!", Monnam(mon),
                       makeplural(stagger(mon->data, "stagger")));
 #else
@@ -1323,7 +1350,7 @@ int dieroll;
        a level draining artifact has already done to max HP */
     if (mon->mhp > mon->mhpmax)
         mon->mhp = mon->mhpmax;
-    if (mon->mhp < 1)
+    if (DEADMONSTER(mon))
         destroyed = TRUE;
     if (mon->mtame && tmp > 0) {
         /* do this even if the pet is being killed (affects revival) */
@@ -1345,11 +1372,12 @@ int dieroll;
             /* but not bashing with darts, arrows or ya */
             && !(is_ammo(obj) || is_missile(obj)))
         && hand_to_hand) {
-        if (clone_mon(mon, 0, 0)) {
+        struct monst *mclone;
+        if ((mclone = clone_mon(mon, 0, 0)) != 0) {
             char withwhat[BUFSZ];
 
             withwhat[0] = '\0';
-#if 0 /*JP*/
+#if 0 /*JP:T*/
             if (u.twoweap && flags.verbose)
                 Sprintf(withwhat, " with %s", yname(obj));
             pline("%s divides as you hit it%s!", Monnam(mon), withwhat);
@@ -1359,6 +1387,7 @@ int dieroll;
             pline("\82 \82È\82½\82Ì%s\8dU\8c\82\82Å%s\82Í\95ª\97ô\82µ\82½\81I", withwhat, Monnam(mon));
 #endif
             hittxt = TRUE;
+            mintrap(mclone);
         }
     }
 
@@ -1373,8 +1402,10 @@ int dieroll;
 */
             pline("\8dU\8c\82\82Í\96½\92\86\82µ\82½\81D");
         else
-#if 0 /*JP*/
-            You("%s %s%s", Role_if(PM_BARBARIAN) ? "smite" : "hit",
+#if 0 /*JP*//*\89£\82è\95û\82ð\95ª\82¯\82é\82Ü\82Å\82Í\82â\82ç\82È\82¢*/
+            You("%s %s%s",
+                (obj && (is_shield(obj) || obj->otyp == HEAVY_IRON_BALL))
+                  ? "bash" : Role_if(PM_BARBARIAN) ? "smite" : "hit",
                 mon_nam(mon), canseemon(mon) ? exclam(tmp) : ".");
 #else
             Your("%s\82Ö\82Ì\8dU\8c\82\82Í\96½\92\86\82µ\82½%s",
@@ -1405,14 +1436,15 @@ int dieroll;
                 Sprintf(silverobjbuf, "Your %s%s %s",
                         strstri(saved_oname, "silver") ? "" : "silver ",
                         saved_oname, vtense(saved_oname, "sear"));
+                (void) strNsubst(silverobjbuf, "%", "%%", 0);
+                Strcat(silverobjbuf, " %s!");
 #else
                 Sprintf(silverobjbuf, "%%s\82Í%s%s\82Å\8fÄ\82©\82ê\82½\81I",
                         strstri(saved_oname, "\8bâ") ?
                         "" : "\8bâ\82Ì",
                         saved_oname);
+                (void) strNsubst(silverobjbuf, "%%", "%", 0);
 #endif
-                (void) strNsubst(silverobjbuf, "%", "%%", 0);
-                Strcat(silverobjbuf, " %s!");
                 fmt = silverobjbuf;
             } else
 /*JP
@@ -1434,6 +1466,46 @@ int dieroll;
             whom = strcat(s_suffix(whom), "\93÷");
         pline(fmt, whom);
     }
+    if (lightobj) {
+        const char *fmt;
+        char *whom = mon_nam(mon);
+        char emitlightobjbuf[BUFSZ];
+
+        if (canspotmon(mon)) {
+            if (saved_oname[0]) {
+#if 0 /*JP*/
+                Sprintf(emitlightobjbuf,
+                        "%s radiance penetrates deep into",
+                        s_suffix(saved_oname));
+                Strcat(emitlightobjbuf, " %s!");
+#else
+                Sprintf(emitlightobjbuf,
+                        "%s\8cõ\82ª%%s\82É\90[\82­\8aÑ\92Ê\82µ\82½\81I",
+                        s_suffix(saved_oname));
+#endif
+                fmt = emitlightobjbuf;
+            } else
+/*JP
+                fmt = "The light sears %s!";
+*/
+                fmt = "\8cõ\82Í%s\82ð\8fÄ\82¢\82½\81I";
+        } else {
+#if 0 /*JP*/
+            *whom = highc(*whom); /* "it" -> "It" */
+#endif
+/*JP
+            fmt = "%s is seared!";
+*/
+            fmt = "%s\82Í\8fÄ\82©\82ê\82½\81I";
+        }
+        /* note: s_suffix returns a modifiable buffer */
+        if (!noncorporeal(mdat) && !amorphous(mdat))
+/*JP
+            whom = strcat(s_suffix(whom), " flesh");
+*/
+            whom = strcat(s_suffix(whom), "\93÷");
+        pline(fmt, whom);
+    }
     /* if a "no longer poisoned" message is coming, it will be last;
        obj->opoisoned was cleared above and any message referring to
        "poisoned <obj>" has now been given; we want just "<obj>" for
@@ -1441,7 +1513,9 @@ int dieroll;
     if (unpoisonmsg)
         Strcpy(saved_oname, cxname(obj));
 
-    /* [note: thrown obj might go away during killed/xkilled call] */
+    /* [note: thrown obj might go away during killed()/xkilled() call
+       (via 'thrownobj'; if swallowed, it gets added to engulfer's
+       minvent and might merge with a stack that's already there)] */
 
     if (needpoismsg)
 /*JP
@@ -1472,7 +1546,7 @@ int dieroll;
         }
     }
     if (unpoisonmsg)
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         Your("%s %s no longer poisoned.", saved_oname,
              vtense(saved_oname, "are"));
 #else
@@ -1505,6 +1579,64 @@ struct obj *obj;
     return FALSE;
 }
 
+/* used for hero vs monster and monster vs monster; also handles
+   monster vs hero but that won't happen because hero can't be a shade */
+boolean
+shade_miss(magr, mdef, obj, thrown, verbose)
+struct monst *magr, *mdef;
+struct obj *obj;
+boolean thrown, verbose;
+{
+    const char *what, *whose, *target;
+    boolean youagr = (magr == &youmonst), youdef = (mdef == &youmonst);
+
+    /* we're using dmgval() for zero/not-zero, not for actual damage amount */
+    if (mdef->data != &mons[PM_SHADE] || (obj && dmgval(obj, mdef)))
+        return FALSE;
+
+    if (verbose
+        && ((youdef || cansee(mdef->mx, mdef->my) || sensemon(mdef))
+            || (magr == &youmonst && distu(mdef->mx, mdef->my) <= 2))) {
+#if 0 /*JP*/
+        static const char harmlessly_thru[] = " harmlessly through ";
+#endif
+
+/*JP
+        what = (!obj || shade_aware(obj)) ? "attack" : cxname(obj);
+*/
+        what = (!obj || shade_aware(obj)) ? "\8dU\8c\82" : cxname(obj);
+/*JP
+        target = youdef ? "you" : mon_nam(mdef);
+*/
+        target = youdef ? "\82 \82È\82½" : mon_nam(mdef);
+        if (!thrown) {
+/*JP
+            whose = youagr ? "Your" : s_suffix(Monnam(magr));
+*/
+            whose = youagr ? "\82 \82È\82½\82Ì" : s_suffix(Monnam(magr));
+#if 0 /*JP:T*/
+            pline("%s %s %s%s%s.", whose, what,
+                  vtense(what, "pass"), harmlessly_thru, target);
+#else
+            pline("%s%s\82Í%s\82ð\92Ê\82è\82Ê\82¯\82½\81D", whose, what,
+                  target);
+#endif
+        } else {
+#if 0 /*JP:T*/
+            pline("%s %s%s%s.", The(what), /* note: not pline_The() */
+                  vtense(what, "pass"), harmlessly_thru, target);
+#else
+            pline("%s\82Í%s\82ð\92Ê\82è\82Ê\82¯\82½\81D", what, target);
+#endif
+        }
+        if (!youdef && !canspotmon(mdef))
+            map_invisible(mdef->mx, mdef->my);
+    }
+    if (!youdef)
+        mdef->msleeping = 0;
+    return TRUE;
+}
+
 /* check whether slippery clothing protects from hug or wrap attack */
 /* [currently assumes that you are the attacker] */
 STATIC_OVL boolean
@@ -1530,7 +1662,7 @@ struct attack *mattk;
        protection might fail (33% chance) when the armor is cursed */
     if (obj && (obj->greased || obj->otyp == OILSKIN_CLOAK)
         && (!obj->cursed || rn2(3))) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         You("%s %s %s %s!",
             mattk->adtyp == AD_WRAP ? "slip off of"
                                     : "grab, but cannot hold onto",
@@ -1655,10 +1787,11 @@ steal_it(mdef, mattk)
 struct monst *mdef;
 struct attack *mattk;
 {
-    struct obj *otmp, *stealoid, **minvent_ptr;
+    struct obj *otmp, *gold = 0, *stealoid, **minvent_ptr;
     long unwornmask;
 
-    if (!mdef->minvent)
+    otmp = mdef->minvent;
+    if (!otmp || (otmp->oclass == COIN_CLASS && !otmp->nobj))
         return; /* nothing to take */
 
     /* look for worn body armor */
@@ -1678,16 +1811,19 @@ struct attack *mattk;
             }
         *minvent_ptr = stealoid; /* put armor back into minvent */
     }
+    gold = findgold(mdef->minvent);
 
     if (stealoid) { /* we will be taking everything */
         if (gender(mdef) == (int) u.mfemale && youmonst.data->mlet == S_NYMPH)
-/*JP
-            You("charm %s.  She gladly hands over her possessions.",
-*/
-            You("%s\82ð\82¤\82Á\82Æ\82è\82³\82¹\82½\81D\94Þ\8f\97\82Í\82æ\82ë\82±\82ñ\82Å\8e\9d\82¿\95¨\82ð\82³\82µ\82¾\82µ\82½\81D",
-                mon_nam(mdef));
+#if 0 /*JP:T*/
+            You("charm %s.  She gladly hands over %sher possessions.",
+                mon_nam(mdef), !gold ? "" : "most of ");
+#else
+            You("%s\82ð\82¤\82Á\82Æ\82è\82³\82¹\82½\81D\94Þ\8f\97\82Í\82æ\82ë\82±\82ñ\82Å\8e\9d\82¿\95¨%s\82ð\82³\82µ\82¾\82µ\82½\81D",
+                mon_nam(mdef), !gold ? "" : "\82Ì\82Ù\82Æ\82ñ\82Ç");
+#endif
         else
-#if 0 /*JP*/
+#if 0 /*JP:T*/
             You("seduce %s and %s starts to take off %s clothes.",
                 mon_nam(mdef), mhe(mdef), mhis(mdef));
 #else
@@ -1696,7 +1832,18 @@ struct attack *mattk;
 #endif
     }
 
+    /* prevent gold from being stolen so that steal-item isn't a superset
+       of steal-gold; shuffling it out of minvent before selecting next
+       item, and then back in case hero or monster dies (hero touching
+       stolen c'trice corpse or monster wielding one and having gloves
+       stolen) is less bookkeeping than skipping it within the loop or
+       taking it out once and then trying to figure out how to put it back */
+    if (gold)
+        obj_extract_self(gold);
+
     while ((otmp = mdef->minvent) != 0) {
+        if (gold) /* put 'mdef's gold back after remembering mdef->minvent */
+            mpickobj(mdef, gold), gold = 0;
         if (!Upolyd)
             break; /* no longer have ability to steal */
         /* take the object away from the monster */
@@ -1707,9 +1854,12 @@ struct attack *mattk;
                 setmnotwielded(mdef, otmp);
             otmp->owornmask = 0L;
             update_mon_intrinsics(mdef, otmp, FALSE, FALSE);
+            /* give monster a chance to wear other equipment on its next
+               move instead of waiting until it picks something up */
+            mdef->misc_worn_check |= I_SPECIAL;
 
             if (otmp == stealoid) /* special message for final item */
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                 pline("%s finishes taking off %s suit.", Monnam(mdef),
                       mhis(mdef));
 #else
@@ -1717,14 +1867,15 @@ struct attack *mattk;
 #endif
         }
         /* give the object to the character */
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         otmp = hold_another_object(otmp, "You snatched but dropped %s.",
                                    doname(otmp), "You steal: ");
 #else
         otmp = hold_another_object(otmp, "\82 \82È\82½\82Í%s\82ð\93\90\82ñ\82¾\82ª\97\8e\82Æ\82µ\82½\81D",
                                    doname(otmp), "\82ð\93\90\82ñ\82¾\81D");
 #endif
-        if (otmp->where != OBJ_INVENT)
+        /* might have dropped otmp, and it might have broken or left level */
+        if (!otmp || otmp->where != OBJ_INVENT)
             continue;
         if (theft_petrifies(otmp))
             break; /* stop thieving even though hero survived */
@@ -1733,23 +1884,38 @@ struct attack *mattk;
             possibly_unwield(mdef, FALSE);
         } else if (unwornmask & W_ARMG) { /* stole worn gloves */
             mselftouch(mdef, (const char *) 0, TRUE);
-            if (mdef->mhp <= 0) /* it's now a statue */
-                return;         /* can't continue stealing */
+            if (DEADMONSTER(mdef)) /* it's now a statue */
+                break; /* can't continue stealing */
         }
 
         if (!stealoid)
             break; /* only taking one item */
+
+        /* take gold out of minvent before making next selection; if it
+           is the only thing left, the loop will terminate and it will be
+           put back below */
+        if ((gold = findgold(mdef->minvent)) != 0)
+            obj_extract_self(gold);
     }
+
+    /* put gold back; won't happen if either hero or 'mdef' dies because
+       gold will be back in monster's inventory at either of those times
+       (so will be present in mdef's minvent for bones, or in its statue
+       now if it has just been turned into one) */
+    if (gold)
+        mpickobj(mdef, gold);
 }
 
 int
-damageum(mdef, mattk)
+damageum(mdef, mattk, specialdmg)
 register struct monst *mdef;
 register struct attack *mattk;
+int specialdmg; /* blessed and/or silver bonus against various things */
 {
     register struct permonst *pd = mdef->data;
     int armpro, tmp = d((int) mattk->damn, (int) mattk->damd);
     boolean negated;
+    struct obj *mongold;
 
     armpro = magic_negation(mdef);
     /* since hero can't be cancelled, only defender's armor applies */
@@ -1764,7 +1930,7 @@ register struct attack *mattk;
     switch (mattk->adtyp) {
     case AD_STUN:
         if (!Blind)
-#if 0 /*JP*/
+#if 0 /*JP:T*/
             pline("%s %s for a moment.", Monnam(mdef),
                   makeplural(stagger(pd, "stagger")));
 #else
@@ -1784,20 +1950,25 @@ register struct attack *mattk;
     case AD_WERE: /* no special effect on monsters */
     case AD_HEAL: /* likewise */
     case AD_PHYS:
-    physical:
+ physical:
+        if (pd == &mons[PM_SHADE]) {
+            tmp = 0;
+            if (!specialdmg)
+                impossible("bad shade attack function flow?");
+        }
+        tmp += specialdmg;
+
         if (mattk->aatyp == AT_WEAP) {
-            if (uwep)
-                tmp = 0;
-        } else if (mattk->aatyp == AT_KICK) {
+            /* hmonas() uses known_hitum() to deal physical damage,
+               then also damageum() for non-AD_PHYS; don't inflict
+               extra physical damage for unusual damage types */
+            tmp = 0;
+        } else if (mattk->aatyp == AT_KICK
+                   || mattk->aatyp == AT_CLAW
+                   || mattk->aatyp == AT_TUCH
+                   || mattk->aatyp == AT_HUGS) {
             if (thick_skinned(pd))
-                tmp = 0;
-            if (pd == &mons[PM_SHADE]) {
-                if (!(uarmf && uarmf->blessed)) {
-                    impossible("bad shade attack function flow?");
-                    tmp = 0;
-                } else
-                    tmp = rnd(4); /* bless damage */
-            }
+                tmp = (mattk->aatyp == AT_KICK) ? 0 : (tmp + 1) / 2;
             /* add ring(s) of increase damage */
             if (u.udaminc > 0) {
                 /* applies even if damage was 0 */
@@ -1828,9 +1999,15 @@ register struct attack *mattk;
 */
                 pline("%s\82Í\8a®\91S\82É\94R\82¦\90s\82«\82½\81I", Monnam(mdef));
             else
+#if 0 /*JP:T*/
                 You("smell burning%s.",
                     (pd == &mons[PM_PAPER_GOLEM]) ? " paper"
                       : (pd == &mons[PM_STRAW_GOLEM]) ? " straw" : "");
+#else
+                You("%s\82ª\94R\82¦\82é\82É\82¨\82¢\82ª\82µ\82½\81D",
+                    (pd == &mons[PM_PAPER_GOLEM]) ? "\8e\86"
+                      : (pd == &mons[PM_STRAW_GOLEM]) ? "\82í\82ç" : "\89½\82©");
+#endif
             xkilled(mdef, XKILL_NOMSG | XKILL_NOCORPSE);
             tmp = 0;
             break;
@@ -1915,24 +2092,22 @@ register struct attack *mattk;
     case AD_SGLD:
         /* This you as a leprechaun, so steal
            real gold only, no lesser coins */
-        {
-            struct obj *mongold = findgold(mdef->minvent);
-            if (mongold) {
-                obj_extract_self(mongold);
-                if (merge_choice(invent, mongold) || inv_cnt(FALSE) < 52) {
-                    addinv(mongold);
+        mongold = findgold(mdef->minvent);
+        if (mongold) {
+            obj_extract_self(mongold);
+            if (merge_choice(invent, mongold) || inv_cnt(FALSE) < 52) {
+                addinv(mongold);
 /*JP
-                    Your("purse feels heavier.");
+                Your("purse feels heavier.");
 */
-                    You("\8dà\95z\82ª\8fd\82­\82È\82Á\82½\82æ\82¤\82È\8bC\82ª\82µ\82½\81D");
-                } else {
+                You("\8dà\95z\82ª\8fd\82­\82È\82Á\82½\82æ\82¤\82È\8bC\82ª\82µ\82½\81D");
+            } else {
 /*JP
-                    You("grab %s's gold, but find no room in your knapsack.",
+                You("grab %s's gold, but find no room in your knapsack.",
 */
-                    You("%s\82Ì\82¨\8bà\82ð\82Â\82©\82ñ\82¾\82ª\81C\8e\9d\82¿\95¨\91Ü\82É\93ü\82ç\82È\82©\82Á\82½\81D",
-                        mon_nam(mdef));
-                    dropy(mongold);
-                }
+                You("%s\82Ì\82¨\8bà\82ð\82Â\82©\82ñ\82¾\82ª\81C\8e\9d\82¿\95¨\91Ü\82É\93ü\82ç\82È\82©\82Á\82½\81D",
+                    mon_nam(mdef));
+                dropy(mongold);
             }
         }
         exercise(A_DEX, TRUE);
@@ -1941,10 +2116,11 @@ register struct attack *mattk;
     case AD_TLPT:
         if (tmp <= 0)
             tmp = 1;
-        if (!negated && tmp < mdef->mhp) {
+        if (!negated) {
             char nambuf[BUFSZ];
-            boolean u_saw_mon =
-                canseemon(mdef) || (u.uswallow && u.ustuck == mdef);
+            boolean u_saw_mon = (canseemon(mdef)
+                                 || (u.uswallow && u.ustuck == mdef));
+
             /* record the name before losing sight of monster */
             Strcpy(nambuf, Monnam(mdef));
             if (u_teleport_mon(mdef, FALSE) && u_saw_mon
@@ -1953,6 +2129,11 @@ register struct attack *mattk;
                 pline("%s suddenly disappears!", nambuf);
 */
                 pline("%s\82Í\93Ë\91R\8fÁ\82¦\82½\81I", nambuf);
+            if (tmp >= mdef->mhp) { /* see hitmu(mhitu.c) */
+                if (mdef->mhp == 1)
+                    ++mdef->mhp;
+                tmp = mdef->mhp - 1;
+            }
         }
         break;
     case AD_BLND:
@@ -1974,7 +2155,7 @@ register struct attack *mattk;
         if (night() && !rn2(10) && !mdef->mcan) {
             if (pd == &mons[PM_CLAY_GOLEM]) {
                 if (!Blind)
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                     pline("Some writing vanishes from %s head!",
                           s_suffix(mon_nam(mdef)));
 #else
@@ -2005,7 +2186,7 @@ register struct attack *mattk;
             mdef->mhp -= xtmp;
             /* !m_lev: level 0 monster is killed regardless of hit points
                rather than drop to level -1 */
-            if (mdef->mhp <= 0 || !mdef->m_lev) {
+            if (DEADMONSTER(mdef) || !mdef->m_lev) {
 /*JP
                 pline("%s dies!", Monnam(mdef));
 */
@@ -2055,12 +2236,12 @@ register struct attack *mattk;
             Your("%s was poisoned!", mpoisons_subj(&youmonst, mattk));
 */
             Your("%s\82Í\93Å\82³\82ê\82Ä\82¢\82é\81I", mpoisons_subj(&youmonst, mattk));
-            if (resists_poison(mdef))
+            if (resists_poison(mdef)) {
 /*JP
                 pline_The("poison doesn't seem to affect %s.", mon_nam(mdef));
 */
                 pline("\93Å\82Í%s\82É\89e\8b¿\82ð\97^\82¦\82È\82¢\81D", mon_nam(mdef));
-            else {
+            else {
                 if (!rn2(10)) {
 /*JP
                     Your("poison was deadly...");
@@ -2096,7 +2277,7 @@ register struct attack *mattk;
             break;
 
         if ((helmet = which_armor(mdef, W_ARMH)) != 0 && rn2(8)) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
             pline("%s %s blocks your attack to %s head.",
                   s_suffix(Monnam(mdef)), helm_simple_name(helmet),
                   mhis(mdef));
@@ -2143,7 +2324,7 @@ register struct attack *mattk;
             } else {
                 tmp = 0;
                 if (flags.verbose)
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                     You("brush against %s %s.", s_suffix(mon_nam(mdef)),
                         mbodypart(mdef, LEG));
 #else
@@ -2178,7 +2359,7 @@ register struct attack *mattk;
         if (negated)
             break; /* physical damage only */
         if (!rn2(4) && !slimeproof(pd)) {
-            if (!munslime(mdef, TRUE) && mdef->mhp > 0) {
+            if (!munslime(mdef, TRUE) && !DEADMONSTER(mdef)) {
                 /* this assumes newcham() won't fail; since hero has
                    a slime attack, green slimes haven't been geno'd */
 /*JP
@@ -2189,7 +2370,7 @@ register struct attack *mattk;
                     pd = mdef->data;
             }
             /* munslime attempt could have been fatal */
-            if (mdef->mhp < 1)
+            if (DEADMONSTER(mdef))
                 return 2; /* skip death message */
             tmp = 0;
         }
@@ -2227,7 +2408,7 @@ register struct attack *mattk;
 
     mdef->mstrategy &= ~STRAT_WAITFORU; /* in case player is very fast */
     mdef->mhp -= tmp;
-    if (mdef->mhp < 1) {
+    if (DEADMONSTER(mdef)) {
         if (mdef->mtame && !cansee(mdef->mx, mdef->my)) {
 /*JP
             You_feel("embarrassed for a moment.");
@@ -2254,6 +2435,7 @@ explum(mdef, mattk)
 register struct monst *mdef;
 register struct attack *mattk;
 {
+    boolean resistance; /* only for cold/fire/elec */
     register int tmp = d((int) mattk->damn, (int) mattk->damd);
 
 /*JP
@@ -2261,8 +2443,6 @@ register struct attack *mattk;
 */
     You("\94\9a\94­\82µ\82½\81I");
     switch (mattk->adtyp) {
-        boolean resistance; /* only for cold/fire/elec */
-
     case AD_BLND:
         if (!resists_blnd(mdef)) {
 /*JP
@@ -2290,14 +2470,14 @@ register struct attack *mattk;
         goto common;
     case AD_ELEC:
         resistance = resists_elec(mdef);
   common:
+ common:
         if (!resistance) {
 /*JP
             pline("%s gets blasted!", Monnam(mdef));
 */
             pline("%s\82Í\94\9a\94­\82ð\97\81\82Ñ\82½\81I", Monnam(mdef));
             mdef->mhp -= tmp;
-            if (mdef->mhp <= 0) {
+            if (DEADMONSTER(mdef)) {
                 killed(mdef);
                 return 2;
             }
@@ -2324,7 +2504,7 @@ struct monst *mdef;
 {
     if (!Invisible) {
         map_location(u.ux, u.uy, TRUE);
-        tmp_at(DISP_ALWAYS, mon_to_glyph(&youmonst));
+        tmp_at(DISP_ALWAYS, mon_to_glyph(&youmonst, rn2_on_display_rng));
         tmp_at(mdef->mx, mdef->my);
     }
 /*JP
@@ -2379,9 +2559,15 @@ register struct attack *mattk;
            vampire form now instead of dealing with that when it dies */
         if (is_vampshifter(mdef)
             && newcham(mdef, &mons[mdef->cham], FALSE, FALSE)) {
+/*JP
             You("engulf it, then expel it.");
+*/
+            You("\88ù\82Ý\8d\9e\82ñ\82Å\81C\93f\82«\8fo\82µ\82½\81D");
             if (canspotmon(mdef))
+/*JP
                 pline("It turns into %s.", a_monnam(mdef));
+*/
+                pline("\82»\82ê\82Í%s\82É\82È\82Á\82½\81D", a_monnam(mdef));
             else
                 map_invisible(mdef->mx, mdef->my);
             return 1;
@@ -2441,7 +2627,7 @@ register struct attack *mattk;
                 }
 
                 /* Use up amulet of life saving */
-                if (!!(otmp = mlifesaver(mdef)))
+                if ((otmp = mlifesaver(mdef)) != 0)
                     m_useup(mdef, otmp);
 
                 newuhs(FALSE);
@@ -2452,7 +2638,7 @@ register struct attack *mattk;
                    several turns) but the level-gain message seems out of
                    order if the kill message is left implicit */
                 xkilled(mdef, XKILL_GIVEMSG | XKILL_NOCORPSE);
-                if (mdef->mhp > 0) { /* monster lifesaved */
+                if (!DEADMONSTER(mdef)) { /* monster lifesaved */
 /*JP
                     You("hurriedly regurgitate the sizzling in your %s.",
 */
@@ -2612,12 +2798,12 @@ register struct attack *mattk;
             }
             end_engulf();
             mdef->mhp -= dam;
-            if (mdef->mhp <= 0) {
+            if (DEADMONSTER(mdef)) {
                 killed(mdef);
-                if (mdef->mhp <= 0) /* not lifesaved */
+                if (DEADMONSTER(mdef)) /* not lifesaved */
                     return 2;
             }
-#if 0 /*JP*/
+#if 0 /*JP:T*/
             You("%s %s!", is_animal(youmonst.data) ? "regurgitate" : "expel",
                 mon_nam(mdef));
 #else
@@ -2625,7 +2811,7 @@ register struct attack *mattk;
                 is_animal(youmonst.data) ? "\93f\82«\96ß" : "\94r\8fo");
 #endif
             if (Slow_digestion || is_animal(youmonst.data)) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                 pline("Obviously, you didn't like %s taste.",
                       s_suffix(mon_nam(mdef)));
 #else
@@ -2669,24 +2855,44 @@ boolean wouldhavehit;
         wakeup(mdef, TRUE);
 }
 
-/* attack monster as a monster. */
+/* attack monster as a monster; returns True if mon survives */
 STATIC_OVL boolean
 hmonas(mon)
 register struct monst *mon;
 {
     struct attack *mattk, alt_attk;
     struct obj *weapon, **originalweapon;
-    boolean altwep = FALSE, weapon_used = FALSE;
+    boolean altwep = FALSE, weapon_used = FALSE, odd_claw = TRUE;
     int i, tmp, armorpenalty, sum[NATTK], nsum = 0, dhit = 0, attknum = 0;
-    int dieroll;
+    int dieroll, multi_claw = 0;
 
+    /* with just one touch/claw/weapon attack, both rings matter;
+       with more than one, alternate right and left when checking
+       whether silver ring causes successful hit */
     for (i = 0; i < NATTK; i++) {
         sum[i] = 0;
         mattk = getmattk(&youmonst, mon, i, sum, &alt_attk);
+        if (mattk->aatyp == AT_WEAP
+            || mattk->aatyp == AT_CLAW || mattk->aatyp == AT_TUCH)
+            ++multi_claw;
+    }
+    multi_claw = (multi_claw > 1); /* switch from count to yes/no */
+
+    for (i = 0; i < NATTK; i++) {
+        /* sum[i] = 0; -- now done above */
+        mattk = getmattk(&youmonst, mon, i, sum, &alt_attk);
         weapon = 0;
         switch (mattk->aatyp) {
         case AT_WEAP:
-        use_weapon:
+            /* if (!uwep) goto weaponless; */
+ use_weapon:
+            odd_claw = !odd_claw; /* see case AT_CLAW,AT_TUCH below */
+            /* if we've already hit with a two-handed weapon, we don't
+               get to make another weapon attack (note:  monsters who
+               use weapons do not have this restriction, but they also
+               never have the opportunity to use two weapons) */
+            if (weapon_used && sum[i - 1] && uwep && bimanual(uwep))
+                continue;
             /* Certain monsters don't use weapons when encountered as enemies,
              * but players who polymorph into them have hands or claws and
              * thus should be able to use weapons.  This shouldn't prohibit
@@ -2703,11 +2909,24 @@ register struct monst *mon;
                be Null, and we want to track that for passive() */
             originalweapon = (altwep && uswapwep) ? &uswapwep : &uwep;
             if (uswapwep /* set up 'altwep' flag for next iteration */
+                /* only consider seconary when wielding one-handed primary */
+                && uwep && (uwep->oclass == WEAPON_CLASS || is_weptool(uwep))
+                && !bimanual(uwep)
+                /* only switch if not wearing shield and not at artifact;
+                   shield limitation is iffy since still get extra swings
+                   if polyform has them, but it matches twoweap behavior;
+                   twoweap also only allows primary to be an artifact, so
+                   if alternate weapon is one, don't use it */
+                && !uarms && !uswapwep->oartifact
                 /* only switch to uswapwep if it's a weapon */
                 && (uswapwep->oclass == WEAPON_CLASS || is_weptool(uswapwep))
                 /* only switch if uswapwep is not bow, arrows, or darts */
                 && !(is_launcher(uswapwep) || is_ammo(uswapwep)
-                     || is_missile(uswapwep))) /* dart, shuriken, boomerang */
+                     || is_missile(uswapwep)) /* dart, shuriken, boomerang */
+                /* and not two-handed and not incapable of being wielded */
+                && !bimanual(uswapwep)
+                && !(objects[uswapwep->otyp].oc_material == SILVER
+                     && Hate_silver))
                 altwep = !altwep; /* toggle for next attack */
             weapon = *originalweapon;
             if (!weapon) /* no need to go beyond no-gloves to rings; not ...*/
@@ -2717,9 +2936,10 @@ register struct monst *mon;
                                    &armorpenalty);
             dieroll = rnd(20);
             dhit = (tmp > dieroll || u.uswallow);
-            /* Enemy dead, before any special abilities used */
+            /* caller must set bhitpos */
             if (!known_hitum(mon, weapon, &dhit, tmp,
                              armorpenalty, mattk, dieroll)) {
+                /* enemy dead, before any special abilities used */
                 sum[i] = 2;
                 break;
             } else
@@ -2728,14 +2948,15 @@ register struct monst *mon;
                now be empty if the weapon was destroyed during the hit;
                passive(,weapon,...) won't call passive_obj() in that case */
             weapon = *originalweapon; /* might receive passive erosion */
-            /* might be a worm that gets cut in half */
-            if (m_at(u.ux + u.dx, u.uy + u.dy) != mon)
-                return (boolean) (nsum != 0);
-            /* Do not print "You hit" message, since known_hitum
-             * already did it.
-             */
+            /* might be a worm that gets cut in half; if so, early return */
+            if (m_at(u.ux + u.dx, u.uy + u.dy) != mon) {
+                i = NATTK; /* skip additional attacks */
+                /* proceed with uswapwep->cursed check, then exit loop */
+                goto passivedone;
+            }
+            /* Do not print "You hit" message; known_hitum already did it. */
             if (dhit && mattk->adtyp != AD_SPEL && mattk->adtyp != AD_PHYS)
-                sum[i] = damageum(mon, mattk);
+                sum[i] = damageum(mon, mattk, 0);
             break;
         case AT_CLAW:
             if (uwep && !cantwield(youmonst.data) && !weapon_used)
@@ -2750,21 +2971,26 @@ register struct monst *mon;
         case AT_STNG:
         case AT_BUTT:
         case AT_TENT:
+        /*weaponless:*/
             tmp = find_roll_to_hit(mon, mattk->aatyp, (struct obj *) 0,
                                    &attknum, &armorpenalty);
             dieroll = rnd(20);
             dhit = (tmp > dieroll || u.uswallow);
             if (dhit) {
-                int compat;
+                int compat, specialdmg;
+                long silverhit = 0L;
+#if 0 /*JP*//* \82·\82×\82Ä\81u\8dU\8c\82\82µ\82½\81v\82É\82·\82é */
+                const char *verb = 0; /* verb or body part */
+#endif
 
                 if (!u.uswallow
-                    && (compat = could_seduce(&youmonst, mon, mattk))) {
-#if 0 /*JP*/
+                    && (compat = could_seduce(&youmonst, mon, mattk)) != 0) {
+#if 0 /*JP:T*/
                     You("%s %s %s.",
-                        mon->mcansee && haseyes(mon->data) ? "smile at"
-                                                           : "talk to",
+                        (mon->mcansee && haseyes(mon->data)) ? "smile at"
+                                                             : "talk to",
                         mon_nam(mon),
-                        compat == 2 ? "engagingly" : "seductively");
+                        (compat == 2) ? "engagingly" : "seductively");
 #else
                     You("%s\82Ö%s%s\81D",
                         mon_nam(mon),
@@ -2773,94 +2999,222 @@ register struct monst *mon;
                                                            : "\98b\82µ\82©\82¯\82½");
 #endif
                     /* doesn't anger it; no wakeup() */
-                    sum[i] = damageum(mon, mattk);
+                    sum[i] = damageum(mon, mattk, 0);
                     break;
                 }
                 wakeup(mon, TRUE);
-                /* maybe this check should be in damageum()? */
-                if (mon->data == &mons[PM_SHADE]
-                    && !(mattk->aatyp == AT_KICK && uarmf
-                         && uarmf->blessed)) {
-/*JP
-                    Your("attack passes harmlessly through %s.",
-*/
-                    Your("\8dU\8c\82\82Í%s\82ð\82·\82Á\82Æ\92Ê\82è\82Ê\82¯\82½\81D",
-                         mon_nam(mon));
+
+                specialdmg = 0; /* blessed and/or silver bonus */
+                switch (mattk->aatyp) {
+                case AT_CLAW:
+                case AT_TUCH:
+                    /* verb=="claws" may be overridden below */
+#if 0 /*JP*/
+                    verb = (mattk->aatyp == AT_TUCH) ? "touch" : "claws";
+#endif
+                    /* decide if silver-hater will be hit by silver ring(s);
+                       for 'multi_claw' where attacks alternate right/left,
+                       assume 'even' claw or touch attacks use right hand
+                       or paw, 'odd' ones use left for ring interaction;
+                       even vs odd is based on actual attacks rather
+                       than on index into mon->dat->mattk[] so that {bite,
+                       claw,claw} instead of {claw,claw,bite} doesn't
+                       make poly'd hero mysteriously become left-handed */
+                    odd_claw = !odd_claw;
+                    specialdmg = special_dmgval(&youmonst, mon,
+                                                W_ARMG
+                                                | ((odd_claw || !multi_claw)
+                                                   ? W_RINGL : 0L)
+                                                | ((!odd_claw || !multi_claw)
+                                                   ? W_RINGR : 0L),
+                                                &silverhit);
+                    break;
+                case AT_TENT:
+                    /* assumes mind flayer's tentacles-on-head rather
+                       than sea monster's tentacle-as-arm */
+#if 0 /*JP*/
+                    verb = "tentacles";
+#endif
+                    break;
+                case AT_KICK:
+#if 0 /*JP*/
+                    verb = "kick";
+#endif
+                    specialdmg = special_dmgval(&youmonst, mon, W_ARMF,
+                                                &silverhit);
+                    break;
+                case AT_BUTT:
+#if 0 /*JP*/
+                    verb = "head butt"; /* mbodypart(mon,HEAD)=="head" */
+                    /* hypothetical; if any form with a head-butt attack
+                       could wear a helmet, it would hit shades when
+                       wearing a blessed (or silver) one */
+#endif
+                    specialdmg = special_dmgval(&youmonst, mon, W_ARMH,
+                                                &silverhit);
+                    break;
+                case AT_BITE:
+#if 0 /*JP*/
+                    verb = "bite";
+#endif
+                    break;
+                case AT_STNG:
+#if 0 /*JP*/
+                    verb = "sting";
+#endif
+                    break;
+                default:
+#if 0 /*JP*/
+                    verb = "hit";
+#endif
                     break;
                 }
-                if (mattk->aatyp == AT_KICK)
-/*JP
-                    You("kick %s.", mon_nam(mon));
-*/
-                    You("%s\82ð\8fR\82Á\82½\81D", mon_nam(mon));
-                else if (mattk->aatyp == AT_BITE)
-/*JP
-                    You("bite %s.", mon_nam(mon));
-*/
-                    You("%s\82É\8a\9a\82Ý\82Â\82¢\82½\81D", mon_nam(mon));
-                else if (mattk->aatyp == AT_STNG)
-/*JP
-                    You("sting %s.", mon_nam(mon));
-*/
-                    You("%s\82É\93Ë\82«\82³\82µ\82½\81D", mon_nam(mon));
-                else if (mattk->aatyp == AT_BUTT)
-/*JP
-                    You("butt %s.", mon_nam(mon));
-*/
-                    You("%s\82É\93ª\93Ë\82«\82ð\82­\82ç\82í\82µ\82½\81D", mon_nam(mon));
-                else if (mattk->aatyp == AT_TUCH)
-/*JP
-                    You("touch %s.", mon_nam(mon));
-*/
-                    You("%s\82É\90G\82ê\82½\81D", mon_nam(mon));
-                else if (mattk->aatyp == AT_TENT)
-/*JP
-                    Your("tentacles suck %s.", mon_nam(mon));
-*/
-                    Your("\90G\8eè\82ª%s\82Ì\91Ì\89t\82ð\8bz\82¢\82Æ\82Á\82½\81D", mon_nam(mon));
-                else
+                if (mon->data == &mons[PM_SHADE] && !specialdmg) {
+#if 0 /*JP*/
+                    if (!strcmp(verb, "hit")
+                        || (mattk->aatyp == AT_CLAW && humanoid(mon->data)))
+                        verb = "attack";
+#endif
+#if 0 /*JP*/
+                    Your("%s %s harmlessly through %s.",
+                         verb, vtense(verb, "pass"), mon_nam(mon));
+#else
+                    Your("\8dU\8c\82\82Í%s\82ð\92Ê\82è\82Ê\82¯\82½\81D",
+                         mon_nam(mon));
+#endif
+                } else {
+                    if (mattk->aatyp == AT_TENT) {
 /*JP
-                    You("hit %s.", mon_nam(mon));
+                        Your("tentacles suck %s.", mon_nam(mon));
 */
-                    Your("%s\82Ö\82Ì\8dU\8c\82\82Í\96½\92\86\82µ\82½\81D", mon_nam(mon));
-                sum[i] = damageum(mon, mattk);
-            } else {
+                        Your("\90G\8eè\82ª%s\82Ì\91Ì\89t\82ð\8bz\82¢\82Æ\82Á\82½\81D", mon_nam(mon));
+                    } else {
+#if 0 /*JP*/
+                        if (mattk->aatyp == AT_CLAW)
+                            verb = "hit"; /* not "claws" */
+#endif
+#if 0 /*JP*/
+                        You("%s %s.", verb, mon_nam(mon));
+#else
+                        You("%s\82ð\8dU\8c\82\82µ\82½\81D", mon_nam(mon));
+#endif
+                        if (silverhit && flags.verbose)
+                            silver_sears(&youmonst, mon, silverhit);
+                    }
+                    sum[i] = damageum(mon, mattk, specialdmg);
+                }
+            } else { /* !dhit */
                 missum(mon, mattk, (tmp + armorpenalty > dieroll));
             }
             break;
 
-        case AT_HUGS:
+        case AT_HUGS: {
+            int specialdmg;
+            long silverhit = 0L;
+            boolean byhand = hug_throttles(&mons[u.umonnum]), /* rope golem */
+                    unconcerned = (byhand && !can_be_strangled(mon));
+
+            if (sticks(mon->data) || u.uswallow || notonhead
+                || (byhand && (uwep || !has_head(mon->data)))) {
+                /* can't hold a holder due to subsequent ambiguity over
+                   who is holding whom; can't hug engulfer from inside;
+                   can't hug a worm tail (would immobilize entire worm!);
+                   byhand: can't choke something that lacks a head;
+                   not allowed to make a choking hug if wielding a weapon
+                   (but might have grabbed w/o weapon, then wielded one,
+                   and may even be attacking a different monster now) */
+                if (byhand && uwep && u.ustuck
+                    && !(sticks(u.ustuck->data) || u.uswallow))
+                    uunstick();
+                continue; /* not 'break'; bypass passive counter-attack */
+            }
             /* automatic if prev two attacks succeed, or if
-             * already grabbed in a previous attack
-             */
+               already grabbed in a previous attack */
             dhit = 1;
             wakeup(mon, TRUE);
-            if (mon->data == &mons[PM_SHADE])
-/*JP
-                Your("hug passes harmlessly through %s.", mon_nam(mon));
-*/
-                You("%s\82ð\82Â\82©\82Ü\82¦\82æ\82¤\82Æ\82µ\82½\82ª\92Ê\82è\82Ê\82¯\82½\81D", mon_nam(mon));
-            else if (!sticks(mon->data) && !u.uswallow) {
-                if (mon == u.ustuck) {
+            /* choking hug/throttling grab uses hands (gloves or rings);
+               normal hug uses outermost of cloak/suit/shirt */
+            specialdmg = special_dmgval(&youmonst, mon,
+                                        byhand ? (W_ARMG | W_RINGL | W_RINGR)
+                                               : (W_ARMC | W_ARM | W_ARMU),
+                                        &silverhit);
+            if (unconcerned) {
+                /* strangling something which can't be strangled */
+                if (mattk != &alt_attk) {
+                    alt_attk = *mattk;
+                    mattk = &alt_attk;
+                }
+                /* change damage to 1d1; not strangling but still
+                   doing [minimal] physical damage to victim's body */
+                mattk->damn = mattk->damd = 1;
+                /* don't give 'unconcerned' feedback if there is extra damage
+                   or if it is nearly destroyed or if creature doesn't have
+                   the mental ability to be concerned in the first place */
+                if (specialdmg || mindless(mon->data)
+                    || mon->mhp <= 1 + max(u.udaminc, 1))
+                    unconcerned = FALSE;
+            }
+            if (mon->data == &mons[PM_SHADE]) {
 #if 0 /*JP*/
-                    pline("%s is being %s.", Monnam(mon),
-                          u.umonnum == PM_ROPE_GOLEM ? "choked" : "crushed");
+                const char *verb = byhand ? "grasp" : "hug";
+#endif
+
+                /* hugging a shade; successful if blessed outermost armor
+                   for normal hug, or blessed gloves or silver ring(s) for
+                   choking hug; deals damage but never grabs hold */
+                if (specialdmg) {
+#if 0 /*JP:T*/
+                    You("%s %s%s", verb, mon_nam(mon), exclam(specialdmg));
 #else
-                    pline("%s\82Í%s\81D", Monnam(mon),
-                          u.umonnum==PM_ROPE_GOLEM ? "\8eñ\82ð\8di\82ß\82ç\82ê\82Ä\82¢\82é"
-                                                   : "\89\9f\82µ\82Â\82Ô\82³\82ê\82Ä\82¢\82é");
+                    You("%s\82ð\82Â\82©\82ñ\82¾%s", mon_nam(mon), exclam(specialdmg));
+#endif
+                    if (silverhit && flags.verbose)
+                        silver_sears(&youmonst, mon, silverhit);
+                    sum[i] = damageum(mon, mattk, specialdmg);
+                } else {
+#if 0 /*JP:T*/
+                    Your("%s passes harmlessly through %s.",
+                         verb, mon_nam(mon));
+#else
+                    You("%s\82ð\82Â\82©\82Ü\82¦\82æ\82¤\82Æ\82µ\82½\82ª\92Ê\82è\82Ê\82¯\82½\81D",
+                        mon_nam(mon));
 #endif
-                    sum[i] = damageum(mon, mattk);
-                } else if (i >= 2 && sum[i - 1] && sum[i - 2]) {
-/*JP
-                    You("grab %s!", mon_nam(mon));
-*/
-                    You("%s\82ð\82Â\82©\82Ü\82¦\82½\81I", mon_nam(mon));
-                    u.ustuck = mon;
-                    sum[i] = damageum(mon, mattk);
                 }
+                break;
             }
-            break;
+            /* hug attack against ordinary foe */
+            if (mon == u.ustuck) {
+#if 0 /*JP:T*/
+                pline("%s is being %s%s.", Monnam(mon),
+                      byhand ? "throttled" : "crushed",
+                      /* extra feedback for non-breather being choked */
+                      unconcerned ? " but doesn't seem concerned" : "");
+#else
+                pline("%s\82Í%s%s\81D", Monnam(mon),
+                      byhand ? "\8eñ\82ð\8di\82ß\82ç\82ê\82Ä\82¢\82é" : "\89\9f\82µ\82Â\82Ô\82³\82ê\82Ä\82¢\82é",
+                      /* extra feedback for non-breather being choked */
+                      unconcerned ? "\82ª\81C\8bC\82É\82µ\82Ä\82¢\82È\82¢\82æ\82¤\82¾" : "");
+#endif
+                if (silverhit && flags.verbose)
+                    silver_sears(&youmonst, mon, silverhit);
+                sum[i] = damageum(mon, mattk, specialdmg);
+            } else if (i >= 2 && sum[i - 1] && sum[i - 2]) {
+                /* in case we're hugging a new target while already
+                   holding something else; yields feedback
+                   "<u.ustuck> is no longer in your clutches" */
+                if (u.ustuck && u.ustuck != mon)
+                    uunstick();
+/*JP
+                You("grab %s!", mon_nam(mon));
+*/
+                You("%s\82ð\82Â\82©\82Ü\82¦\82½\81I", mon_nam(mon));
+                u.ustuck = mon;
+                if (silverhit && flags.verbose)
+                    silver_sears(&youmonst, mon, silverhit);
+                sum[i] = damageum(mon, mattk, specialdmg);
+            }
+            break; /* AT_HUGS */
+        }
 
         case AT_EXPL: /* automatic hit if next to */
             dhit = -1;
@@ -2925,17 +3279,34 @@ register struct monst *mon;
         }
         if (sum[i] == 2) {
             /* defender dead */
-            return (boolean) passive(mon, weapon, 1, 0, mattk->aatyp, FALSE);
+            (void) passive(mon, weapon, 1, 0, mattk->aatyp, FALSE);
+            nsum = 0; /* return value below used to be 'nsum > 0' */
         } else {
             (void) passive(mon, weapon, sum[i], 1, mattk->aatyp, FALSE);
             nsum |= sum[i];
         }
+
+        /* don't use sum[i] beyond this point;
+           'i' will be out of bounds if we get here via 'goto' */
+ passivedone:
+        /* when using dual weapons, cursed secondary weapon doesn't weld,
+           it gets dropped; do the same when multiple AT_WEAP attacks
+           simulate twoweap */
+        if (uswapwep && weapon == uswapwep && weapon->cursed) {
+            drop_uswapwep();
+            break; /* don't proceed with additional attacks */
+        }
+        /* stop attacking if defender has died;
+           needed to defer this until after uswapwep->cursed check */
+        if (DEADMONSTER(mon))
+            break;
         if (!Upolyd)
             break; /* No extra attacks if no longer a monster */
         if (multi < 0)
             break; /* If paralyzed while attacking, i.e. floating eye */
     }
-    return (boolean) (nsum != 0);
+    /* return value isn't used, but make it match hitum()'s */
+    return !DEADMONSTER(mon);
 }
 
 /*      Special (passive) attacks on you by monsters done here.
@@ -3065,7 +3436,7 @@ boolean wep_was_destroyed;
 /*JP
             pline("A hail of magic missiles narrowly misses you!");
 */
-            pline("\96\82\96@\82Ì\96î\82Ì\89J\82ð\82È\82ñ\82Æ\82©\82©\82í\82µ\82½\81I");
+            pline("\96\82\96@\82Ì\96î\82Ì\89J\82ª\82©\82·\82ß\82Ä\82¢\82Á\82½\81I");
         } else {
 /*JP
             You("are hit by magic missiles appearing from thin air!");
@@ -3100,7 +3471,7 @@ boolean wep_was_destroyed;
                     break;
                 }
                 if (mon->mcansee) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                     if (ureflects("%s gaze is reflected by your %s.",
                                   s_suffix(Monnam(mon)))) {
 #else
@@ -3108,23 +3479,26 @@ boolean wep_was_destroyed;
                                   Monnam(mon))) {
 #endif
                         ;
-                    } else if (Free_action) {
-#if 0 /*JP*/
-                        You("momentarily stiffen under %s gaze!",
-                            s_suffix(mon_nam(mon)));
-#else
-                        You("%s\82Ì\82É\82ç\82Ý\82Å\88ê\8fu\8dd\92¼\82µ\82½\81I",
-                            mon_nam(mon));
-#endif
                     } else if (Hallucination && rn2(4)) {
-#if 0 /*JP*/
+                        /* [it's the hero who should be getting paralyzed
+                           and isn't; this message describes the monster's
+                           reaction rather than the hero's escape] */
+#if 0 /*JP:T*/
                         pline("%s looks %s%s.", Monnam(mon),
                               !rn2(2) ? "" : "rather ",
-                              !rn2(2) ? "numb" : "stupified");
+                              !rn2(2) ? "numb" : "stupefied");
 #else
                         pline("%s\82Í%s\82Î\82©\82É\82È\82Á\82½\82æ\82¤\82¾\81D", Monnam(mon),
                               !rn2(2) ? "" : "\8f­\82µ");
 #endif
+                    } else if (Free_action) {
+#if 0 /*JP:T*/
+                        You("momentarily stiffen under %s gaze!",
+                            s_suffix(mon_nam(mon)));
+#else
+                        You("%s\82Ì\82É\82ç\82Ý\82Å\88ê\8fu\8dd\92¼\82µ\82½\81I",
+                            mon_nam(mon));
+#endif
                     } else {
 /*JP
                         You("are frozen by %s gaze!", s_suffix(mon_nam(mon)));
@@ -3138,7 +3512,7 @@ boolean wep_was_destroyed;
                         nomovemsg = 0;
                     }
                 } else {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                     pline("%s cannot defend itself.",
                           Adjmonnam(mon, "blind"));
 #else
@@ -3329,7 +3703,7 @@ struct monst *mtmp;
     if (Blind) {
         if (!Blind_telepat)
             what = generic; /* with default fmt */
-        else if (mtmp->m_ap_type == M_AP_MONSTER)
+        else if (M_AP_TYPE(mtmp) == M_AP_MONSTER)
             what = a_monnam(mtmp); /* differs from what was sensed */
     } else {
         int glyph = levl[u.ux + u.dx][u.uy + u.dy].glyph;
@@ -3430,7 +3804,7 @@ struct obj *otmp; /* source of flash */
                                               : rn2(min(mtmp->mhp, 4));
                 light_hits_gremlin(mtmp, amt);
             }
-            if (mtmp->mhp > 0) {
+            if (!DEADMONSTER(mtmp)) {
                 if (!context.mon_moving)
                     setmangry(mtmp, TRUE);
                 if (tmp < 9 && !mtmp->isshk && rn2(4))
@@ -3448,7 +3822,7 @@ light_hits_gremlin(mon, dmg)
 struct monst *mon;
 int dmg;
 {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
     pline("%s %s!", Monnam(mon),
           (dmg > mon->mhp / 2) ? "wails in agony" : "cries out in pain");
 #else
@@ -3457,7 +3831,7 @@ int dmg;
 #endif
     mon->mhp -= dmg;
     wake_nearto(mon->mx, mon->my, 30);
-    if (mon->mhp <= 0) {
+    if (DEADMONSTER(mon)) {
         if (context.mon_moving)
             monkilled(mon, (char *) 0, AD_BLND);
         else