OSDN Git Service

update year to 2020
[jnethack/source.git] / src / mon.c
index ba40f19..279b11d 100644 (file)
--- a/src/mon.c
+++ b/src/mon.c
@@ -1,10 +1,11 @@
-/* NetHack 3.6 mon.c   $NHDT-Date: 1449269918 2015/12/04 22:58:38 $  $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.199 $ */
+/* NetHack 3.6 mon.c   $NHDT-Date: 1569276991 2019/09/23 22:16:31 $  $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.297 $ */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/*-Copyright (c) Derek S. Ray, 2015. */
 /* 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-2020            */
 /* JNetHack may be freely redistributed.  See license for details. */
 
 /* If you're using precompiled headers, you don't want this either */
 #include "mfndpos.h"
 #include <ctype.h>
 
-STATIC_VAR boolean vamp_rise_msg;
+STATIC_VAR boolean vamp_rise_msg, disintegested;
 
-STATIC_DCL void FDECL(sanity_check_single_mon, (struct monst *, const char *));
+STATIC_DCL void FDECL(sanity_check_single_mon, (struct monst *, BOOLEAN_P,
+                                                const char *));
 STATIC_DCL boolean FDECL(restrap, (struct monst *));
 STATIC_DCL long FDECL(mm_aggression, (struct monst *, struct monst *));
 STATIC_DCL long FDECL(mm_displacement, (struct monst *, struct monst *));
@@ -27,12 +29,15 @@ STATIC_DCL void FDECL(kill_eggs, (struct obj *));
 STATIC_DCL int FDECL(pickvampshape, (struct monst *));
 STATIC_DCL boolean FDECL(isspecmon, (struct monst *));
 STATIC_DCL boolean FDECL(validspecmon, (struct monst *, int));
-STATIC_DCL boolean FDECL(validvamp, (struct monst *, int *, int));
-STATIC_DCL struct permonst *FDECL(accept_newcham_form, (int));
+STATIC_DCL struct permonst *FDECL(accept_newcham_form, (struct monst *, int));
 STATIC_DCL struct obj *FDECL(make_corpse, (struct monst *, unsigned));
 STATIC_DCL void FDECL(m_detach, (struct monst *, struct permonst *));
 STATIC_DCL void FDECL(lifesaved_monster, (struct monst *));
+STATIC_DCL void FDECL(migrate_mon, (struct monst *, XCHAR_P, XCHAR_P));
+STATIC_DCL boolean FDECL(ok_to_obliterate, (struct monst *));
+STATIC_DCL void FDECL(deal_with_overcrowding, (struct monst *));
 
+/* note: duplicated in dog.c */
 #define LEVEL_SPECIFIC_NOCORPSE(mdat) \
     (Is_rogue_level(&u.uz)            \
      || (level.flags.graveyard && is_undead(mdat) && rn2(3)))
@@ -48,36 +53,99 @@ const char *warnings[] = {
 #endif /* 0 */
 
 
-void
-sanity_check_single_mon(mtmp, msg)
+STATIC_OVL void
+sanity_check_single_mon(mtmp, chk_geno, msg)
 struct monst *mtmp;
+boolean chk_geno;
 const char *msg;
 {
-    if (DEADMONSTER(mtmp))
-        return;
-    if (mtmp->data < &mons[LOW_PM] || mtmp->data >= &mons[NUMMONS])
-        impossible("illegal mon data (%s)", msg);
+    if (mtmp->data < &mons[LOW_PM] || mtmp->data >= &mons[NUMMONS]) {
+        impossible("illegal mon data %s; mnum=%d (%s)",
+                   fmt_ptr((genericptr_t) mtmp->data), mtmp->mnum, msg);
+    } else {
+        int mndx = monsndx(mtmp->data);
+
+        if (mtmp->mnum != mndx) {
+            impossible("monster mnum=%d, monsndx=%d (%s)",
+                       mtmp->mnum, mndx, msg);
+            mtmp->mnum = mndx;
+        }
+        if (DEADMONSTER(mtmp)) {
+#if 0
+            /* bad if not fmons list or if not vault guard */
+            if (strcmp(msg, "fmon") || !mtmp->isgd)
+                impossible("dead monster on %s; %s at <%d,%d>",
+                           msg, mons[mndx].mname, mtmp->mx, mtmp->my);
+#endif
+            return;
+        }
+        if (chk_geno && (mvitals[mndx].mvflags & G_GENOD) != 0)
+            impossible("genocided %s in play (%s)", mons[mndx].mname, msg);
+    }
+    if (mtmp->isshk && !has_eshk(mtmp))
+        impossible("shk without eshk (%s)", msg);
+    if (mtmp->ispriest && !has_epri(mtmp))
+        impossible("priest without epri (%s)", msg);
+    if (mtmp->isgd && !has_egd(mtmp))
+        impossible("guard without egd (%s)", msg);
+    if (mtmp->isminion && !has_emin(mtmp))
+        impossible("minion without emin (%s)", msg);
+    /* guardian angel on astral level is tame but has emin rather than edog */
+    if (mtmp->mtame && !has_edog(mtmp) && !mtmp->isminion)
+        impossible("pet without edog (%s)", msg);
 }
 
 void
 mon_sanity_check()
 {
-    int x,y;
-    struct monst *mtmp = fmon;
+    int x, y;
+    struct monst *mtmp, *m;
 
-    while (mtmp) {
-        sanity_check_single_mon(mtmp, "fmon");
-        mtmp = mtmp->nmon;
+    for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
+        /* dead monsters should still have sane data */
+        sanity_check_single_mon(mtmp, TRUE, "fmon");
+        if (DEADMONSTER(mtmp) && !mtmp->isgd)
+            continue;
+
+        x = mtmp->mx, y = mtmp->my;
+        if (!isok(x, y) && !(mtmp->isgd && x == 0 && y == 0)) {
+            impossible("mon (%s) claims to be at <%d,%d>?",
+                       fmt_ptr((genericptr_t) mtmp), x, y);
+        } else if (mtmp == u.usteed) {
+            /* steed is in fmon list but not on the map; its
+               <mx,my> coordinates should match hero's location */
+            if (x != u.ux || y != u.uy)
+                impossible("steed (%s) claims to be at <%d,%d>?",
+                           fmt_ptr((genericptr_t) mtmp), x, y);
+        } else if (level.monsters[x][y] != mtmp) {
+            impossible("mon (%s) at <%d,%d> is not there!",
+                       fmt_ptr((genericptr_t) mtmp), x, y);
+        } else if (mtmp->wormno) {
+            sanity_check_worm(mtmp);
+        }
     }
-    for (x = 0; x < COLNO; x++)
+
+    for (x = 1; x < COLNO; x++)
         for (y = 0; y < ROWNO; y++)
-            if ((mtmp = m_at(x,y)) != 0)
-                sanity_check_single_mon(mtmp, "m_at");
+            if ((mtmp = level.monsters[x][y]) != 0) {
+                for (m = fmon; m; m = m->nmon)
+                    if (m == mtmp)
+                        break;
+                if (!m)
+                    impossible("map mon (%s) at <%d,%d> not in fmon list!",
+                               fmt_ptr((genericptr_t) mtmp), x, y);
+                else if (mtmp == u.usteed)
+                    impossible("steed (%s) is on the map at <%d,%d>!",
+                               fmt_ptr((genericptr_t) mtmp), x, y);
+                else if ((mtmp->mx != x || mtmp->my != y)
+                         && mtmp->data != &mons[PM_LONG_WORM])
+                    impossible("map mon (%s) at <%d,%d> is found at <%d,%d>?",
+                               fmt_ptr((genericptr_t) mtmp),
+                               mtmp->mx, mtmp->my, x, y);
+            }
 
-    mtmp = migrating_mons;
-    while (mtmp) {
-        sanity_check_single_mon(mtmp, "migr");
-        mtmp = mtmp->nmon;
+    for (mtmp = migrating_mons; mtmp; mtmp = mtmp->nmon) {
+        sanity_check_single_mon(mtmp, FALSE, "migr");
     }
 }
 
@@ -274,11 +342,13 @@ unsigned corpseflags;
     case PM_BLACK_UNICORN:
         if (mtmp->mrevived && rn2(2)) {
             if (canseemon(mtmp))
-/*JP
+#if 0 /*JP:T*/
                 pline("%s recently regrown horn crumbles to dust.",
-*/
-                pline("\8dÅ\8bß\8dÄ\90\82µ\82½%s\82Ì\8ap\82Í\95²\81X\82É\82È\82Á\82½\81D",
                       s_suffix(Monnam(mtmp)));
+#else
+                pline("\8dÅ\8bß\8dÄ\90\82µ\82½%s\82Ì\8ap\82Í\95²\81X\82É\82È\82Á\82½\81D",
+                      Monnam(mtmp));
+#endif
         } else {
             obj = mksobj_at(UNICORN_HORN, x, y, TRUE, FALSE);
             if (obj && mtmp->mrevived)
@@ -381,7 +451,6 @@ unsigned corpseflags;
         }
         free_mname(mtmp);
         return obj;
-        break;
     default_1:
     default:
         if (mvitals[mndx].mvflags & G_NOCORPSE) {
@@ -403,7 +472,8 @@ unsigned corpseflags;
     }
     /* All special cases should precede the G_NOCORPSE check */
 
-    if (!obj) return NULL;
+    if (!obj)
+        return (struct obj *) 0;
 
     /* if polymorph or undead turning has killed this monster,
        prevent the same attack beam from hitting its corpse */
@@ -419,7 +489,7 @@ unsigned corpseflags;
      *  underneath it, you could be told the corpse type of a
      *  monster that you never knew was there without this.
      *  The code in hitmu() substitutes the word "something"
-     *  if the corpses obj->dknown is 0.
+     *  if the corpse's obj->dknown is 0.
      */
     if (Blind && !sensemon(mtmp))
         obj->dknown = 0;
@@ -436,15 +506,20 @@ register struct monst *mtmp;
 {
     boolean inpool, inlava, infountain;
 
-    /* [what about ceiling clingers?] */
+    /* [ceiling clingers are handled below] */
     inpool = (is_pool(mtmp->mx, mtmp->my)
-              && !(is_flyer(mtmp->data) || is_floater(mtmp->data)));
+              && (!(is_flyer(mtmp->data) || is_floater(mtmp->data))
+                  /* there's no "above the surface" on the plane of water */
+                  || Is_waterlevel(&u.uz)));
     inlava = (is_lava(mtmp->mx, mtmp->my)
               && !(is_flyer(mtmp->data) || is_floater(mtmp->data)));
     infountain = IS_FOUNTAIN(levl[mtmp->mx][mtmp->my].typ);
 
-    /* Flying and levitation keeps our steed out of the liquid */
-    /* (but not water-walking or swimming) */
+    /* Flying and levitation keeps our steed out of the liquid
+       (but not water-walking or swimming; note: if hero is in a
+       water location on the Plane of Water, flight and levitating
+       are blocked so this (Flying || Levitation) test fails there
+       and steed will be subject to water effects, as intended) */
     if (mtmp == u.usteed && (Flying || Levitation))
         return 0;
 
@@ -469,9 +544,9 @@ register struct monst *mtmp;
         mtmp->mhp -= dam;
         if (mtmp->mhpmax > dam)
             mtmp->mhpmax -= dam;
-        if (mtmp->mhp < 1) {
+        if (DEADMONSTER(mtmp)) {
             mondead(mtmp);
-            if (mtmp->mhp < 1)
+            if (DEADMONSTER(mtmp))
                 return 1;
         }
         water_damage_chain(mtmp->minvent, FALSE);
@@ -485,22 +560,40 @@ register struct monst *mtmp;
          * themselves  --ALI
          */
         if (!is_clinger(mtmp->data) && !likes_lava(mtmp->data)) {
+            /* not fair...?  hero doesn't automatically teleport away
+               from lava, just from water */
+            if (can_teleport(mtmp->data) && !tele_restrict(mtmp)) {
+                if (rloc(mtmp, TRUE))
+                    return 0;
+            }
             if (!resists_fire(mtmp)) {
-                if (cansee(mtmp->mx, mtmp->my))
+                if (cansee(mtmp->mx, mtmp->my)) {
+                    struct attack *dummy = &mtmp->data->mattk[0];
+                    const char *how = on_fire(mtmp->data, dummy);
+
 #if 0 /*JP*/
                     pline("%s %s.", Monnam(mtmp),
-                          mtmp->data == &mons[PM_WATER_ELEMENTAL]
-                              ? "boils away"
-                              : "burns to a crisp");
-#else
+                          !strcmp(how, "boiling") ? "boils away"
+                             : !strcmp(how, "melting") ? "melts away"
+                                : "burns to a crisp");
+#else /*mon.c:on_fire()\82Ì\95Ô\82è\92l*/
                     pline("%s\82Í%s\82½\81D", Monnam(mtmp),
-                          mtmp->data == &mons[PM_WATER_ELEMENTAL]
-                              ? "\95¦\93«\82µ"
-                              : "\94R\82¦\82Ä\83p\83\8a\83p\83\8a\82É\82È\82Á");
+                          !strcmp(how, "\95¦\93«\82µ\82½") ? "\95¦\93«\82µ"
+                             : !strcmp(how, "\97n\82¯\82½") ? "\97n\82¯\82½"
+                                : "\94R\82¦\82Ä\83p\83\8a\83p\83\8a\82É\82È\82Á");
 #endif
-                mondead(mtmp);
+                }
+                /* unlike fire -> melt ice -> pool, there's no way for the
+                   hero to create lava beneath a monster, so the !mon_moving
+                   case is not expected to happen (and we haven't made a
+                   player-against-monster variation of the message above) */
+                if (context.mon_moving)
+                    mondead(mtmp);
+                else
+                    xkilled(mtmp, XKILL_NOMSG);
             } else {
-                if (--mtmp->mhp < 1) {
+                mtmp->mhp -= 1;
+                if (DEADMONSTER(mtmp)) {
                     if (cansee(mtmp->mx, mtmp->my))
 /*JP
                         pline("%s surrenders to the fire.", Monnam(mtmp));
@@ -513,7 +606,7 @@ register struct monst *mtmp;
 */
                     pline("%s\82Í\82¿\82å\82Á\82Æ\8fÅ\82°\82½\81D", Monnam(mtmp));
             }
-            if (mtmp->mhp > 0) {
+            if (!DEADMONSTER(mtmp)) {
                 (void) fire_damage_chain(mtmp->minvent, FALSE, FALSE,
                                          mtmp->mx, mtmp->my);
                 (void) rloc(mtmp, FALSE);
@@ -528,25 +621,44 @@ register struct monst *mtmp;
          */
         if (!is_clinger(mtmp->data) && !is_swimmer(mtmp->data)
             && !amphibious(mtmp->data)) {
+            /* like hero with teleport intrinsic or spell, teleport away
+               if possible */
+            if (can_teleport(mtmp->data) && !tele_restrict(mtmp)) {
+                if (rloc(mtmp, TRUE))
+                    return 0;
+            }
             if (cansee(mtmp->mx, mtmp->my)) {
+                if (context.mon_moving)
+/*JP
+                    pline("%s drowns.", Monnam(mtmp));
+*/
+                    pline("%s\82Í\93M\82ê\82½\81D", Monnam(mtmp));
+                else
+                    /* hero used fire to melt ice that monster was on */
 /*JP
-                pline("%s drowns.", Monnam(mtmp));
+                    You("drown %s.", mon_nam(mtmp));
 */
-                pline("%s\82Í\93M\82ê\82½\81D", Monnam(mtmp));
+                    You("%s\82É\93M\82ê\82½\81D", mon_nam(mtmp));
             }
             if (u.ustuck && u.uswallow && u.ustuck == mtmp) {
                 /* This can happen after a purple worm plucks you off a
-                flying steed while you are over water. */
-/*JP
-                pline("%s sinks as water rushes in and flushes you out.",
-*/
+                   flying steed while you are over water. */
+#if 0 /*JP*/
+                pline("%s sinks as %s rushes in and flushes you out.",
+                      Monnam(mtmp), hliquid("water"));
+#else /*hliquid\82Í\95s\8e©\91R\82É\82È\82é\82Ì\82Å\82Æ\82è\82 \82¦\82¸\8eg\82í\82È\82¢*/
                 pline("%s\82Í\90\85\97¬\82É\92¾\82Ý\81D\82 \82È\82½\82ð\93f\82«\8fo\82µ\82½\81D",
                       Monnam(mtmp));
+#endif
             }
-            mondead(mtmp);
-            if (mtmp->mhp > 0) {
+            if (context.mon_moving)
+                mondead(mtmp);
+            else
+                xkilled(mtmp, XKILL_NOMSG);
+            if (!DEADMONSTER(mtmp)) {
                 water_damage_chain(mtmp->minvent, FALSE);
-                (void) rloc(mtmp, FALSE);
+                if (!rloc(mtmp, TRUE))
+                    deal_with_overcrowding(mtmp);
                 return 0;
             }
             return 1;
@@ -568,6 +680,7 @@ mcalcmove(mon)
 struct monst *mon;
 {
     int mmove = mon->data->mmove;
+    int mmove_adj;
 
     /* Note: MSLOW's `+ 1' prevents slowed speed 1 getting reduced to 0;
      *       MFAST's `+ 2' prevents hasted speed 1 from becoming a no-op;
@@ -578,21 +691,24 @@ struct monst *mon;
     else if (mon->mspeed == MFAST)
         mmove = (4 * mmove + 2) / 3;
 
-    if (mon == u.usteed) {
-        if (u.ugallop && context.mv) {
-            /* average movement is 1.50 times normal */
-            mmove = ((rn2(2) ? 4 : 5) * mmove) / 3;
-        }
-    } else if (mmove) {
-        /* vary movement points allocated to slightly reduce predictability;
-           random increment (avg +2) exceeds random decrement (avg +1) by
-           a small amount; normal speed monsters will occasionally get an
-           extra move and slow ones won't be quite as slow */
-        mmove += rn2(5) - rn2(3); /* + 0..4 - 0..2, average net +1 */
-        if (mmove < 1)
-            mmove = 1;
+    if (mon == u.usteed && u.ugallop && context.mv) {
+        /* increase movement by a factor of 1.5; also increase variance of
+           movement speed (if it's naturally 24, we don't want it to always
+           become 36) */
+        mmove = ((rn2(2) ? 4 : 5) * mmove) / 3;
     }
 
+    /* Randomly round the monster's speed to a multiple of NORMAL_SPEED.
+       This makes it impossible for the player to predict when they'll get
+       a free turn (thus preventing exploits like "melee kiting"), while
+       retaining guarantees about shopkeepers not being outsped by a
+       normal-speed player, normal-speed players being unable to open up
+       a gap when fleeing a normal-speed monster, etc. */
+    mmove_adj = mmove % NORMAL_SPEED;
+    mmove -= mmove_adj;
+    if (rn2(NORMAL_SPEED) < mmove_adj)
+        mmove += NORMAL_SPEED;
+
     return mmove;
 }
 
@@ -608,8 +724,9 @@ mcalcdistress()
         if (DEADMONSTER(mtmp))
             continue;
 
-        /* must check non-moving monsters once/turn in case
-         * they managed to end up in liquid */
+        /* must check non-moving monsters once/turn in case they managed
+           to end up in water or lava; note: when not in liquid they regen,
+           shape-shift, timeout temporary maladies just like other monsters */
         if (mtmp->data->mmove == 0) {
             if (vision_full_recalc)
                 vision_recalc(0);
@@ -621,12 +738,10 @@ mcalcdistress()
         mon_regen(mtmp, FALSE);
 
         /* possibly polymorph shapechangers and lycanthropes */
-        if (mtmp->cham >= LOW_PM) {
-            if (is_vampshifter(mtmp) || mtmp->data->mlet == S_VAMPIRE)
-                decide_to_shapeshift(mtmp, 0);
-            else if (!rn2(6))
-                (void) newcham(mtmp, (struct permonst *) 0, FALSE, FALSE);
-        }
+        if (mtmp->cham >= LOW_PM)
+            decide_to_shapeshift(mtmp, (canspotmon(mtmp)
+                                        || (u.uswallow && mtmp == u.ustuck))
+                                          ? SHIFT_MSG : 0);
         were_change(mtmp);
 
         /* gradually time out temporary problems */
@@ -678,10 +793,20 @@ movemon()
             break;
         }
         nmtmp = mtmp->nmon;
-        /* one dead monster needs to perform a move after death:
-           vault guard whose temporary corridor is still on the map */
-        if (mtmp->isgd && !mtmp->mx && mtmp->mhp <= 0)
-            (void) gd_move(mtmp);
+        /* one dead monster needs to perform a move after death: vault
+           guard whose temporary corridor is still on the map; live
+           guards who have led the hero back to civilization get moved
+           off the map too; gd_move() decides whether the temporary
+           corridor can be removed and guard discarded (via clearing
+           mon->isgd flag so that dmonsfree() will get rid of mon) */
+        if (mtmp->isgd && !mtmp->mx) {
+            /* parked at <0,0>; eventually isgd should get set to false */
+            if (monstermoves > mtmp->mlstmv) {
+                (void) gd_move(mtmp);
+                mtmp->mlstmv = monstermoves;
+            }
+            continue;
+        }
         if (DEADMONSTER(mtmp))
             continue;
 
@@ -703,12 +828,23 @@ movemon()
         if (minliquid(mtmp))
             continue;
 
+        /* after losing equipment, try to put on replacement */
+        if (mtmp->misc_worn_check & I_SPECIAL) {
+            long oldworn;
+
+            mtmp->misc_worn_check &= ~I_SPECIAL;
+            oldworn = mtmp->misc_worn_check;
+            m_dowear(mtmp, FALSE);
+            if (mtmp->misc_worn_check != oldworn || !mtmp->mcanmove)
+                continue;
+        }
+
         if (is_hider(mtmp->data)) {
             /* unwatched mimics and piercers may hide again  [MRS] */
             if (restrap(mtmp))
                 continue;
-            if (mtmp->m_ap_type == M_AP_FURNITURE
-                || mtmp->m_ap_type == M_AP_OBJECT)
+            if (M_AP_TYPE(mtmp) == M_AP_FURNITURE
+                || M_AP_TYPE(mtmp) == M_AP_OBJECT)
                 continue;
             if (mtmp->mundetected)
                 continue;
@@ -796,7 +932,7 @@ register struct monst *mtmp;
             && touch_artifact(otmp, mtmp)) {
             if (mtmp->data == &mons[PM_RUST_MONSTER] && otmp->oerodeproof) {
                 if (canseemon(mtmp) && flags.verbose) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                     pline("%s eats %s!", Monnam(mtmp),
                           distant_name(otmp, doname));
 #else
@@ -808,7 +944,7 @@ register struct monst *mtmp;
                 otmp->oerodeproof = 0;
                 mtmp->mstun = 1;
                 if (canseemon(mtmp) && flags.verbose) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                     pline("%s spits %s out in disgust!", Monnam(mtmp),
                           distant_name(otmp, doname));
 #else
@@ -818,7 +954,7 @@ register struct monst *mtmp;
                 }
             } else {
                 if (cansee(mtmp->mx, mtmp->my) && flags.verbose)
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                     pline("%s eats %s!", Monnam(mtmp),
                           distant_name(otmp, doname));
 #else
@@ -889,9 +1025,9 @@ int
 meatobj(mtmp) /* for gelatinous cubes */
 struct monst *mtmp;
 {
-    register struct obj *otmp, *otmp2;
+    struct obj *otmp, *otmp2;
     struct permonst *ptr, *original_ptr = mtmp->data;
-    int poly, grow, heal, count = 0, ecount = 0;
+    int poly, grow, heal, eyes, count = 0, ecount = 0;
     char buf[BUFSZ];
 
     buf[0] = '\0';
@@ -916,7 +1052,12 @@ struct monst *mtmp;
                     && !resists_ston(mtmp))
                    /* don't engulf boulders and statues or ball&chain */
                    || otmp->oclass == ROCK_CLASS
-                   || otmp == uball || otmp == uchain) {
+                   || otmp == uball || otmp == uchain
+                   /* normally mtmp won't have stepped onto scare monster
+                      scroll, but if it does, don't eat or engulf that
+                      (note: scrolls inside eaten containers will still
+                      become engulfed) */
+                   || otmp->otyp == SCR_SCARE_MONSTER) {
             /* do nothing--neither eaten nor engulfed */
             continue;
 
@@ -929,7 +1070,8 @@ struct monst *mtmp;
                        || otmp->otyp == RIN_SLOW_DIGESTION)
                    /* cockatrice corpses handled above; this
                       touch_petrifies() check catches eggs */
-                   || ((otmp->otyp == CORPSE || otmp->otyp == EGG)
+                   || ((otmp->otyp == CORPSE || otmp->otyp == EGG
+                        || otmp->globby)
                        && ((touch_petrifies(&mons[otmp->corpsenm])
                             && !resists_ston(mtmp))
                            || (otmp->corpsenm == PM_GREEN_SLIME
@@ -937,7 +1079,7 @@ struct monst *mtmp;
             /* engulf */
             ++ecount;
             if (ecount == 1)
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                 Sprintf(buf, "%s engulfs %s.", Monnam(mtmp),
                         distant_name(otmp, doname));
 #else
@@ -956,19 +1098,28 @@ struct monst *mtmp;
         } else {
             /* devour */
             ++count;
-            if (cansee(mtmp->mx, mtmp->my) && flags.verbose)
-#if 0 /*JP*/
-                pline("%s eats %s!", Monnam(mtmp),
-                      distant_name(otmp, doname));
+            if (cansee(mtmp->mx, mtmp->my)) {
+                if (flags.verbose)
+#if 0 /*JP:T*/
+                    pline("%s eats %s!", Monnam(mtmp),
+                          distant_name(otmp, doname));
 #else
-                pline("%s\82Í%s\82ð\90H\82×\82Ä\82¢\82é\81I", Monnam(mtmp),
-                      distant_name(otmp, doname));
+                    pline("%s\82Í%s\82ð\90H\82×\82Ä\82¢\82é\81I", Monnam(mtmp),
+                          distant_name(otmp, doname));
 #endif
-            else if (flags.verbose)
+                /* give this one even if !verbose */
+#if 0 /*JP*//*\93ú\96{\8cê\94Å\82Å\82Í\82±\82ê\82Í\82È\82¢*/
+                if (otmp->oclass == SCROLL_CLASS
+                    && !strcmpi(OBJ_DESCR(objects[otmp->otyp]), "YUM YUM"))
+                    pline("Yum%c", otmp->blessed ? '!' : '.');
+#endif
+            } else {
+                if (flags.verbose)
 /*JP
-                You_hear("a slurping sound.");
+                    You_hear("a slurping sound.");
 */
-                You_hear("\82²\82­\82ñ\82Æ\88ù\82Ý\8d\9e\82Þ\89¹\82ð\95·\82¢\82½\81D");
+                    You_hear("\82²\82­\82ñ\82Æ\88ù\82Ý\8d\9e\82Þ\89¹\82ð\95·\82¢\82½\81D");
+            }
             /* Heal up to the object's weight in hp */
             if (mtmp->mhp < mtmp->mhpmax) {
                 mtmp->mhp += objects[otmp->otyp].oc_weight;
@@ -992,6 +1143,7 @@ struct monst *mtmp;
             poly = polyfodder(otmp);
             grow = mlevelgain(otmp);
             heal = mhealup(otmp);
+            eyes = (otmp->otyp == CARROT);
             delobj(otmp); /* munch */
             ptr = mtmp->data;
             if (poly) {
@@ -1002,6 +1154,8 @@ struct monst *mtmp;
             } else if (heal) {
                 mtmp->mhp = mtmp->mhpmax;
             }
+            if ((eyes || heal) && !mtmp->mcansee)
+                mcureblindness(mtmp, canseemon(mtmp));
             /* in case it polymorphed or died */
             if (ptr != original_ptr)
                 return !ptr ? 2 : 1;
@@ -1016,7 +1170,7 @@ struct monst *mtmp;
         if (cansee(mtmp->mx, mtmp->my) && flags.verbose && buf[0])
             pline1(buf);
         else if (flags.verbose)
-#if 0 /*JP*/
+#if 0 /*JP:T*/
             You_hear("%s slurping sound%s.",
                      (ecount == 1) ? "a" : "several", plur(ecount));
 #else
@@ -1031,15 +1185,19 @@ mpickgold(mtmp)
 register struct monst *mtmp;
 {
     register struct obj *gold;
+#if 0 /*JP*/
     int mat_idx;
+#endif
 
     if ((gold = g_at(mtmp->mx, mtmp->my)) != 0) {
+#if 0 /*JP*/
         mat_idx = objects[gold->otyp].oc_material;
+#endif
         obj_extract_self(gold);
         add_to_minv(mtmp, gold);
         if (cansee(mtmp->mx, mtmp->my)) {
             if (flags.verbose && !mtmp->isgd)
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                 pline("%s picks up some %s.", Monnam(mtmp),
                       mat_idx == GOLD ? "gold" : "money");
 #else
@@ -1086,7 +1244,7 @@ register const char *str;
                 otmp3 = splitobj(otmp, carryamt);
             }
             if (cansee(mtmp->mx, mtmp->my) && flags.verbose)
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                 pline("%s picks up %s.", Monnam(mtmp),
                       (distu(mtmp->mx, mtmp->my) <= 5)
                           ? doname(otmp3)
@@ -1257,17 +1415,31 @@ long flag;
     boolean wantpool, poolok, lavaok, nodiag;
     boolean rockok = FALSE, treeok = FALSE, thrudoor;
     int maxx, maxy;
+    boolean poisongas_ok, in_poisongas;
+    NhRegion *gas_reg;
+    int gas_glyph = cmap_to_glyph(S_poisoncloud);
 
     x = mon->mx;
     y = mon->my;
     nowtyp = levl[x][y].typ;
 
     nodiag = NODIAG(mdat - mons);
-    wantpool = mdat->mlet == S_EEL;
-    poolok = (is_flyer(mdat) || is_clinger(mdat)
+    wantpool = (mdat->mlet == S_EEL);
+    poolok = ((!Is_waterlevel(&u.uz)
+               && (is_flyer(mdat) || is_floater(mdat) || is_clinger(mdat)))
               || (is_swimmer(mdat) && !wantpool));
-    lavaok = (is_flyer(mdat) || is_clinger(mdat) || likes_lava(mdat));
+    /* note: floating eye is the only is_floater() so this could be
+       simplified, but then adding another floater would be error prone */
+    lavaok = (is_flyer(mdat) || is_floater(mdat) || is_clinger(mdat)
+              || likes_lava(mdat));
+    if (mdat == &mons[PM_FLOATING_EYE]) /* prefers to avoid heat */
+        lavaok = FALSE;
     thrudoor = ((flag & (ALLOW_WALL | BUSTDOOR)) != 0L);
+    poisongas_ok = ((nonliving(mdat) || is_vampshifter(mon)
+                     || breathless(mdat)) || resists_poison(mon));
+    in_poisongas = ((gas_reg = visible_region_at(x,y)) != 0
+                    && gas_reg->glyph == gas_glyph);
+
     if (flag & ALLOW_DIG) {
         struct obj *mw_tmp;
 
@@ -1289,8 +1461,8 @@ long flag;
             thrudoor = TRUE;
     }
 
-nexttry: /* eels prefer the water, but if there is no water nearby,
-            they will crawl over land */
+ nexttry: /* eels prefer the water, but if there is no water nearby,
+             they will crawl over land */
     if (mon->mconf) {
         flag |= ALLOW_ALL;
         flag &= ~NOTONL;
@@ -1309,13 +1481,22 @@ nexttry: /* eels prefer the water, but if there is no water nearby,
                 && !((IS_TREE(ntyp) ? treeok : rockok) && may_dig(nx, ny)))
                 continue;
             /* KMH -- Added iron bars */
-            if (ntyp == IRONBARS && !(flag & ALLOW_BARS))
+            if (ntyp == IRONBARS
+                && (!(flag & ALLOW_BARS)
+                    || ((levl[nx][ny].wall_info & W_NONDIGGABLE)
+                        && (dmgtype(mdat, AD_RUST)
+                            || dmgtype(mdat, AD_CORR)))))
                 continue;
             if (IS_DOOR(ntyp) && !(amorphous(mdat) || can_fog(mon))
                 && (((levl[nx][ny].doormask & D_CLOSED) && !(flag & OPENDOOR))
                     || ((levl[nx][ny].doormask & D_LOCKED)
                         && !(flag & UNLOCKDOOR))) && !thrudoor)
                 continue;
+            /* avoid poison gas? */
+            if (!poisongas_ok && !in_poisongas
+                && (gas_reg = visible_region_at(nx,ny)) != 0
+                && gas_reg->glyph == gas_glyph)
+                continue;
             /* first diagonal checks (tight squeezes handled below) */
             if (nx != x && ny != y
                 && (nodiag
@@ -1430,8 +1611,8 @@ nexttry: /* eels prefer the water, but if there is no water nearby,
                     if ((ttmp->ttyp != RUST_TRAP
                          || mdat == &mons[PM_IRON_GOLEM])
                         && ttmp->ttyp != STATUE_TRAP
-                        && ((ttmp->ttyp != PIT && ttmp->ttyp != SPIKED_PIT
-                             && ttmp->ttyp != TRAPDOOR && ttmp->ttyp != HOLE)
+                        && ttmp->ttyp != VIBRATING_SQUARE
+                        && ((!is_pit(ttmp->ttyp) && !is_hole(ttmp->ttyp))
                             || (!is_flyer(mdat) && !is_floater(mdat)
                                 && !is_clinger(mdat)) || Sokoban)
                         && (ttmp->ttyp != SLP_GAS_TRAP || !resists_sleep(mon))
@@ -1442,7 +1623,8 @@ nexttry: /* eels prefer the water, but if there is no water nearby,
                         && (ttmp->ttyp != FIRE_TRAP || !resists_fire(mon))
                         && (ttmp->ttyp != SQKY_BOARD || !is_flyer(mdat))
                         && (ttmp->ttyp != WEB
-                            || (!amorphous(mdat) && !webmaker(mdat)))
+                            || (!amorphous(mdat) && !webmaker(mdat)
+                                && !is_whirly(mdat) && !unsolid(mdat)))
                         && (ttmp->ttyp != ANTI_MAGIC || !resists_magm(mon))) {
                         if (!(flag & ALLOW_TRAPS)) {
                             if (mon->mtrapseen & (1L << (ttmp->ttyp - 1)))
@@ -1527,10 +1709,12 @@ dmonsfree()
 {
     struct monst **mtmp, *freetmp;
     int count = 0;
+    char buf[QBUFSZ];
 
+    buf[0] = '\0';
     for (mtmp = &fmon; *mtmp;) {
         freetmp = *mtmp;
-        if (freetmp->mhp <= 0 && !freetmp->isgd) {
+        if (DEADMONSTER(freetmp) && !freetmp->isgd) {
             *mtmp = freetmp->nmon;
             freetmp->nmon = NULL;
             dealloc_monst(freetmp);
@@ -1539,9 +1723,11 @@ dmonsfree()
             mtmp = &(freetmp->nmon);
     }
 
-    if (count != iflags.purge_monsters)
-        impossible("dmonsfree: %d removed doesn't match %d pending",
-                   count, iflags.purge_monsters);
+    if (count != iflags.purge_monsters) {
+        describe_level(buf);
+        impossible("dmonsfree: %d removed doesn't match %d pending on %s",
+                   count, iflags.purge_monsters, buf);
+    }
     iflags.purge_monsters = 0;
 }
 
@@ -1567,7 +1753,7 @@ struct monst *mtmp, *mtmp2;
     if (mtmp != u.usteed) /* don't place steed onto the map */
         place_monster(mtmp2, mtmp2->mx, mtmp2->my);
     if (mtmp2->wormno)      /* update level.monsters[wseg->wx][wseg->wy] */
-        place_wsegs(mtmp2); /* locations to mtmp2 not mtmp. */
+        place_wsegs(mtmp2, NULL); /* locations to mtmp2 not mtmp. */
     if (emits_light(mtmp2->data)) {
         /* since this is so rare, we don't have any `mon_move_light_source' */
         new_light_source(mtmp2->mx, mtmp2->my, emits_light(mtmp2->data),
@@ -1596,8 +1782,9 @@ struct monst *mon;
 struct monst **monst_list; /* &migrating_mons or &mydogs or null */
 {
     struct monst *mtmp;
-    boolean unhide = (monst_list != 0);
     int mx = mon->mx, my = mon->my;
+    boolean on_map = (m_at(mx, my) == mon),
+            unhide = (monst_list != 0);
 
     if (!fmon)
         panic("relmon: no fmon available.");
@@ -1607,11 +1794,17 @@ struct monst **monst_list; /* &migrating_mons or &mydogs or null */
            clone can continue imitating some other monster form); also,
            might be imitating a boulder so need line-of-sight unblocking */
         mon->mundetected = 0;
-        if (mon->m_ap_type && mon->m_ap_type != M_AP_MONSTER)
+        if (M_AP_TYPE(mon) && M_AP_TYPE(mon) != M_AP_MONSTER)
             seemimic(mon);
     }
 
-    remove_monster(mx, my);
+    if (on_map) {
+        mon->mtrapped = 0;
+        if (mon->wormno)
+            remove_worm(mon);
+        else
+            remove_monster(mx, my);
+    }
 
     if (mon == fmon) {
         fmon = fmon->nmon;
@@ -1627,7 +1820,8 @@ struct monst **monst_list; /* &migrating_mons or &mydogs or null */
     }
 
     if (unhide) {
-        newsym(mx, my);
+        if (on_map)
+            newsym(mx, my);
         /* insert into mydogs or migrating_mons */
         mon->nmon = *monst_list;
         *monst_list = mon;
@@ -1653,32 +1847,27 @@ struct monst *mtmp2, *mtmp1;
     if (EGD(mtmp1)) {
         if (!EGD(mtmp2))
             newegd(mtmp2);
-        (void) memcpy((genericptr_t) EGD(mtmp2), (genericptr_t) EGD(mtmp1),
-                      sizeof (struct egd));
+        *EGD(mtmp2) = *EGD(mtmp1);
     }
     if (EPRI(mtmp1)) {
         if (!EPRI(mtmp2))
             newepri(mtmp2);
-        (void) memcpy((genericptr_t) EPRI(mtmp2), (genericptr_t) EPRI(mtmp1),
-                      sizeof (struct epri));
+        *EPRI(mtmp2) = *EPRI(mtmp1);
     }
     if (ESHK(mtmp1)) {
         if (!ESHK(mtmp2))
             neweshk(mtmp2);
-        (void) memcpy((genericptr_t) ESHK(mtmp2), (genericptr_t) ESHK(mtmp1),
-                      sizeof (struct eshk));
+        *ESHK(mtmp2) = *ESHK(mtmp1);
     }
     if (EMIN(mtmp1)) {
         if (!EMIN(mtmp2))
             newemin(mtmp2);
-        (void) memcpy((genericptr_t) EMIN(mtmp2), (genericptr_t) EMIN(mtmp1),
-                      sizeof (struct emin));
+        *EMIN(mtmp2) = *EMIN(mtmp1);
     }
     if (EDOG(mtmp1)) {
         if (!EDOG(mtmp2))
             newedog(mtmp2);
-        (void) memcpy((genericptr_t) EDOG(mtmp2), (genericptr_t) EDOG(mtmp1),
-                      sizeof (struct edog));
+        *EDOG(mtmp2) = *EDOG(mtmp1);
     }
     if (has_mcorpsenm(mtmp1))
         MCORPSENM(mtmp2) = MCORPSENM(mtmp1);
@@ -1714,8 +1903,13 @@ void
 dealloc_monst(mon)
 struct monst *mon;
 {
-    if (mon->nmon)
-        panic("dealloc_monst with nmon");
+    char buf[QBUFSZ];
+
+    buf[0] = '\0';
+    if (mon->nmon) {
+        describe_level(buf);
+        panic("dealloc_monst with nmon on %s", buf);
+    }
     if (mon->mextra)
         dealloc_mextra(mon);
     free((genericptr_t) mon);
@@ -1727,6 +1921,8 @@ m_detach(mtmp, mptr)
 struct monst *mtmp;
 struct permonst *mptr; /* reflects mtmp->data _prior_ to mtmp's death */
 {
+    boolean onmap = (mtmp->mx > 0);
+
     if (mtmp == context.polearm.hitmon)
         context.polearm.hitmon = 0;
     if (mtmp->mleashed)
@@ -1735,17 +1931,30 @@ struct permonst *mptr; /* reflects mtmp->data _prior_ to mtmp's death */
     mtmp->mtrapped = 0;
     mtmp->mhp = 0; /* simplify some tests: force mhp to 0 */
     relobj(mtmp, 0, FALSE);
-    remove_monster(mtmp->mx, mtmp->my);
+    if (onmap || mtmp == level.monsters[0][0]) {
+        if (mtmp->wormno)
+            remove_worm(mtmp);
+        else
+            remove_monster(mtmp->mx, mtmp->my);
+    }
     if (emits_light(mptr))
         del_light_source(LS_MONSTER, monst_to_any(mtmp));
-    newsym(mtmp->mx, mtmp->my);
+    if (M_AP_TYPE(mtmp))
+        seemimic(mtmp);
+    if (onmap)
+        newsym(mtmp->mx, mtmp->my);
     unstuck(mtmp);
-    fill_pit(mtmp->mx, mtmp->my);
+    if (onmap)
+        fill_pit(mtmp->mx, mtmp->my);
 
     if (mtmp->isshk)
         shkgone(mtmp);
     if (mtmp->wormno)
         wormgone(mtmp);
+    if (In_endgame(&u.uz))
+        mtmp->mstate |= MON_ENDGAME_FREE;
+
+    mtmp->mstate |= MON_DETACH;
     iflags.purge_monsters++;
 }
 
@@ -1779,11 +1988,11 @@ struct monst *mtmp;
 /*JP
             pline("But wait...");
 */
-                        pline("\82µ\82©\82µ\81D\81D\81D");
+            pline("\82¿\82å\82Á\82Æ\82Ü\82Á\82½\81D\81D\81D");
 /*JP
             pline("%s medallion begins to glow!", s_suffix(Monnam(mtmp)));
 */
-            pline("%s\82Ì\83\81\83_\83\8a\83I\83\93\82ª\8bP\82«\82Í\82\82ß\82½\81I", Monnam(mtmp));
+            pline("%s\82Ì\96\82\8f\9c\82¯\82ª\8bP\82«\82Í\82\82ß\82½\81I", Monnam(mtmp));
             makeknown(AMULET_OF_LIFE_SAVING);
             /* amulet is visible, but monster might not be */
             if (canseemon(mtmp)) {
@@ -1802,9 +2011,11 @@ struct monst *mtmp;
 /*JP
             pline_The("medallion crumbles to dust!");
 */
-            pline_The("\83\81\83_\83\8a\83I\83\93\82Í\82±\82È\82²\82È\82É\82­\82¾\82¯\82Ä\82µ\82Ü\82Á\82½\81I");
+            pline("\96\82\8f\9c\82¯\82Í\82±\82È\82²\82È\82É\82­\82¾\82¯\82½\81I");
         }
         m_useup(mtmp, lifesave);
+        /* equip replacement amulet, if any, on next move */
+        mtmp->misc_worn_check |= I_SPECIAL;
 
         surviver = !(mvitals[monsndx(mtmp->data)].mvflags & G_GENOD);
         mtmp->mcanmove = 1;
@@ -1815,17 +2026,18 @@ struct monst *mtmp;
         if (mtmp->mhpmax <= 0)
             mtmp->mhpmax = 10;
         mtmp->mhp = mtmp->mhpmax;
-        if (surviver)
-            return;
 
-        /* genocided monster can't be life-saved */
-        if (cansee(mtmp->mx, mtmp->my))
+        if (!surviver) {
+            /* genocided monster can't be life-saved */
+            if (cansee(mtmp->mx, mtmp->my))
 /*JP
-            pline("Unfortunately, %s is still genocided...", mon_nam(mtmp));
+                pline("Unfortunately, %s is still genocided...",
 */
-            pline("\8ec\94O\82È\82ª\82ç%s\82Í\8bs\8eE\82³\82ê\82Ä\82¢\82é\81D\81D\81D", mon_nam(mtmp));
+                pline("\8ec\94O\82È\82ª\82ç%s\82Í\8bs\8eE\82³\82ê\82Ä\82¢\82é\81D\81D\81D",
+                      mon_nam(mtmp));
+            mtmp->mhp = 0;
+        }
     }
-    mtmp->mhp = 0;
 }
 
 void
@@ -1835,8 +2047,9 @@ register struct monst *mtmp;
     struct permonst *mptr;
     int tmp;
 
+    mtmp->mhp = 0; /* in case caller hasn't done this */
     lifesaved_monster(mtmp);
-    if (mtmp->mhp > 0)
+    if (!DEADMONSTER(mtmp))
         return;
 
     if (is_vampshifter(mtmp)) {
@@ -1844,31 +2057,51 @@ register struct monst *mtmp;
         int x = mtmp->mx, y = mtmp->my;
 
         /* this only happens if shapeshifted */
-        if (mndx >= LOW_PM && mndx != monsndx(mtmp->data)) {
+        if (mndx >= LOW_PM && mndx != monsndx(mtmp->data)
+            && !(mvitals[mndx].mvflags & G_GENOD)) {
             char buf[BUFSZ];
             boolean in_door = (amorphous(mtmp->data)
                                && closed_door(mtmp->mx, mtmp->my)),
+#if 0 /*JP*/
                 /* alternate message phrasing for some monster types */
                 spec_mon = (nonliving(mtmp->data)
                             || noncorporeal(mtmp->data)
-                            || amorphous(mtmp->data));
+                            || amorphous(mtmp->data)),
+#endif
+                spec_death = (disintegested /* disintegrated or digested */
+                              || noncorporeal(mtmp->data)
+                              || amorphous(mtmp->data));
 
-            /* construct a format string before transformation */
-            Sprintf(buf, "The %s%s suddenly %s and rises as %%s!",
-                    spec_mon ? "" : "seemingly dead ",
+            /* construct a format string before transformation;
+               will be capitalized when used, expects one %s arg */
+#if 0 /*JP*/
+            Sprintf(buf, "%s suddenly %s and rises as %%s!",
+                    x_monnam(mtmp, ARTICLE_THE,
+                             spec_mon ? (char *) 0 : "seemingly dead",
+                             (SUPPRESS_INVISIBLE | SUPPRESS_IT), FALSE),
+                    spec_death ? "reconstitutes" : "transforms");
+#else
+            Sprintf(buf, "%s%s\82Í\93Ë\91R%s\81C%%s\82Æ\82µ\82Ä\91h\82Á\82½\81I",
+                    spec_death ? "" : "\8e\80\82ñ\82¾\82æ\82¤\82É\8ev\82í\82ê\82½",
                     x_monnam(mtmp, ARTICLE_NONE, (char *) 0,
                              SUPPRESS_SADDLE | SUPPRESS_HALLUCINATION
                                  | SUPPRESS_INVISIBLE | SUPPRESS_IT,
                              FALSE),
-                    spec_mon ? "reconstitutes" : "transforms");
+                    spec_death ? "\8dÄ\8d\\90¬\82³\82ê" : "\95Ï\89»\82µ");
+#endif
             mtmp->mcanmove = 1;
             mtmp->mfrozen = 0;
             if (mtmp->mhpmax <= 0)
                 mtmp->mhpmax = 10;
             mtmp->mhp = mtmp->mhpmax;
-            /* this can happen if previously a fog cloud */
-            if (u.uswallow && (mtmp == u.ustuck))
-                expels(mtmp, mtmp->data, FALSE);
+            /* mtmp==u.ustuck can happen if previously a fog cloud
+               or poly'd hero is hugging a vampire bat */
+            if (mtmp == u.ustuck) {
+                if (u.uswallow)
+                    expels(mtmp, mtmp->data, FALSE);
+                else
+                    uunstick();
+            }
             if (in_door) {
                 coord new_xy;
 
@@ -1882,7 +2115,13 @@ register struct monst *mtmp;
             else
                 mtmp->cham = mndx;
             if (canspotmon(mtmp)) {
-                pline(buf, a_monnam(mtmp));
+                /* 3.6.0 used a_monnam(mtmp); that was weird if mtmp was
+                   named: "Dracula suddenly transforms and rises as Dracula";
+                   3.6.1 used mtmp->data->mname; that ignored hallucination */
+                pline(upstart(buf),
+                      x_monnam(mtmp, ARTICLE_A, (char *) 0,
+                               (SUPPRESS_NAME | SUPPRESS_IT
+                                | SUPPRESS_INVISIBLE), FALSE));
                 vamp_rise_msg = TRUE;
             }
             newsym(x, y);
@@ -1903,18 +2142,16 @@ register struct monst *mtmp;
     mptr = mtmp->data; /* save this for m_detach() */
     /* restore chameleon, lycanthropes to true form at death */
     if (mtmp->cham >= LOW_PM) {
-        set_mon_data(mtmp, &mons[mtmp->cham], -1);
+        set_mon_data(mtmp, &mons[mtmp->cham]);
         mtmp->cham = NON_PM;
     } else if (mtmp->data == &mons[PM_WEREJACKAL])
-        set_mon_data(mtmp, &mons[PM_HUMAN_WEREJACKAL], -1);
+        set_mon_data(mtmp, &mons[PM_HUMAN_WEREJACKAL]);
     else if (mtmp->data == &mons[PM_WEREWOLF])
-        set_mon_data(mtmp, &mons[PM_HUMAN_WEREWOLF], -1);
+        set_mon_data(mtmp, &mons[PM_HUMAN_WEREWOLF]);
     else if (mtmp->data == &mons[PM_WERERAT])
-        set_mon_data(mtmp, &mons[PM_HUMAN_WERERAT], -1);
+        set_mon_data(mtmp, &mons[PM_HUMAN_WERERAT]);
 
-    /* if MAXMONNO monsters of a given type have died, and it
-     * can be done, extinguish that monster.
-     *
+    /*
      * mvitals[].died does double duty as total number of dead monsters
      * and as experience factor for the player killing more monsters.
      * this means that a dragon dying by other means reduces the
@@ -1995,7 +2232,7 @@ boolean was_swallowed; /* digestion */
                     There("is an explosion in your %s!", body_part(STOMACH));
 */
                     pline("%s\82Ì\92\86\82Å\94\9a\94­\82ª\8bN\82«\82½\81I", body_part(STOMACH));
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                     Sprintf(killer.name, "%s explosion",
                             s_suffix(mdat->mname));
 #else
@@ -2008,9 +2245,9 @@ boolean was_swallowed; /* digestion */
 */
                     You_hear("\94\9a\94­\89¹\82ð\95·\82¢\82½\81D");
                     magr->mhp -= tmp;
-                    if (magr->mhp < 1)
+                    if (DEADMONSTER(magr))
                         mondied(magr);
-                    if (magr->mhp < 1) { /* maybe lifesaved */
+                    if (DEADMONSTER(magr)) { /* maybe lifesaved */
                         if (canspotmon(magr))
 /*JP
                             pline("%s rips open!", Monnam(magr));
@@ -2032,6 +2269,8 @@ boolean was_swallowed; /* digestion */
             Sprintf(killer.name, "%s\82Ì\94\9a\94­\82Å", mdat->mname);
             killer.format = KILLED_BY_AN;
             explode(mon->mx, mon->my, -1, tmp, MON_EXPLODE, EXPL_NOXIOUS);
+            killer.name[0] = '\0';
+            killer.format = 0;
             return FALSE;
         }
     }
@@ -2043,7 +2282,7 @@ boolean was_swallowed; /* digestion */
         return FALSE;
 
     if (((bigmonst(mdat) || mdat == &mons[PM_LIZARD]) && !mon->mcloned)
-        || is_golem(mdat) || is_mplayer(mdat) || is_rider(mdat))
+        || is_golem(mdat) || is_mplayer(mdat) || is_rider(mdat) || mon->isshk)
         return TRUE;
     tmp = 2 + ((mdat->geno & G_FREQ) < 2) + verysmall(mdat);
     return (boolean) !rn2(tmp);
@@ -2055,7 +2294,7 @@ mondied(mdef)
 register struct monst *mdef;
 {
     mondead(mdef);
-    if (mdef->mhp > 0)
+    if (!DEADMONSTER(mdef))
         return; /* lifesaved */
 
     if (corpse_chance(mdef, (struct monst *) 0, FALSE)
@@ -2077,6 +2316,8 @@ struct monst *mdef;
     /* hero is thrown from his steed when it disappears */
     if (mdef == u.usteed)
         dismount_steed(DISMOUNT_GENERIC);
+    /* stuck to you? release */
+    unstuck(mdef);
     /* drop special items like the Amulet so that a dismissed Kop or nurse
        can't remove them from the game */
     mdrop_special_objs(mdef);
@@ -2094,12 +2335,18 @@ struct monst *mdef;
     xchar x = mdef->mx, y = mdef->my;
     boolean wasinside = FALSE;
 
+    /* vampshifter reverts to vampire;
+       3.6.3: also used to unshift shape-changed sandestin */
+    if (!vamp_stone(mdef))
+        return;
+
     /* we have to make the statue before calling mondead, to be able to
      * put inventory in it, and we have to check for lifesaving before
      * making the statue....
      */
+    mdef->mhp = 0; /* in case caller hasn't done this */
     lifesaved_monster(mdef);
-    if (mdef->mhp > 0)
+    if (!DEADMONSTER(mdef))
         return;
 
     mdef->mtrapped = 0; /* (see m_detach) */
@@ -2144,6 +2391,7 @@ struct monst *mdef;
             otmp = oname(otmp, MNAME(mdef));
         while ((obj = oldminvent) != 0) {
             oldminvent = obj->nobj;
+            obj->nobj = 0; /* avoid merged-> obfree-> dealloc_obj-> panic */
             (void) add_to_container(otmp, obj);
         }
         /* Archeologists should not break unique statues */
@@ -2166,7 +2414,7 @@ struct monst *mdef;
     mondead(mdef);
     if (wasinside) {
         if (is_animal(mdef->data))
-#if 0 /*JP*/
+#if 0 /*JP:T*/
             You("%s through an opening in the new %s.",
                 locomotion(youmonst.data, "jump"), xname(otmp));
 #else
@@ -2205,12 +2453,13 @@ int how;
         be_sad = (mdef->mtame != 0);
 
     /* no corpses if digested or disintegrated */
-    if (how == AD_DGST || how == -AD_RBRE)
+    disintegested = (how == AD_DGST || how == -AD_RBRE);
+    if (disintegested)
         mondead(mdef);
     else
         mondied(mdef);
 
-    if (be_sad && mdef->mhp <= 0)
+    if (be_sad && DEADMONSTER(mdef))
 /*JP
         You("have a sad feeling for a moment, then it passes.");
 */
@@ -2231,6 +2480,10 @@ struct monst *mtmp;
                 placebc();
             vision_full_recalc = 1;
             docrt();
+            /* prevent swallower (mtmp might have just poly'd into something
+               without an engulf attack) from immediately re-engulfing */
+            if (attacktype(mtmp->data, AT_ENGL) && !mtmp->mspec_used)
+                mtmp->mspec_used = rnd(2);
         }
         u.ustuck = 0;
     }
@@ -2240,63 +2493,54 @@ void
 killed(mtmp)
 struct monst *mtmp;
 {
-    xkilled(mtmp, 1);
+    xkilled(mtmp, XKILL_GIVEMSG);
 }
 
 /* the player has killed the monster mtmp */
 void
-xkilled(mtmp, dest)
+xkilled(mtmp, xkill_flags)
 struct monst *mtmp;
-int dest; /* dest==1, normal; dest==0, don't print message; dest==2, don't
-             drop corpse either; dest==3, message but no corpse */
+int xkill_flags; /* 1: suppress message, 2: suppress corpse, 4: pacifist */
 {
     int tmp, mndx, x = mtmp->mx, y = mtmp->my;
     struct permonst *mdat;
     struct obj *otmp;
     struct trap *t;
-    boolean wasinside = u.uswallow && (u.ustuck == mtmp);
-    boolean burycorpse = FALSE;
-
-    /* KMH, conduct */
-    u.uconduct.killer++;
+    boolean wasinside = u.uswallow && (u.ustuck == mtmp),
+            burycorpse = FALSE,
+            nomsg = (xkill_flags & XKILL_NOMSG) != 0,
+            nocorpse = (xkill_flags & XKILL_NOCORPSE) != 0,
+            noconduct = (xkill_flags & XKILL_NOCONDUCT) != 0;
 
-    if (dest & 1) {
-#if 0 /*JP*/
-        const char *verb = nonliving(mtmp->data) ? "destroy" : "kill";
-#endif
+    mtmp->mhp = 0; /* caller will usually have already done this */
+    if (!noconduct) /* KMH, conduct */
+        u.uconduct.killer++;
 
-        if (!wasinside && !canspotmon(mtmp))
-/*JP
-            You("%s it!", verb);
-*/
-            You("\89½\8eÒ\82©\82ð\93|\82µ\82½\81I");
-        else {
+    if (!nomsg) {
 #if 0 /*JP*/
-            You("%s %s!", verb,
-                !mtmp->mtame
-                    ? mon_nam(mtmp)
-                    : x_monnam(mtmp,
-                               (has_mname(mtmp)) ? ARTICLE_NONE : ARTICLE_THE,
-                               "poor",
-                               (has_mname(mtmp)) ? SUPPRESS_SADDLE : 0,
-                               FALSE));
+        boolean namedpet = has_mname(mtmp) && !Hallucination;
+
+        You("%s %s!",
+            nonliving(mtmp->data) ? "destroy" : "kill",
+            !(wasinside || canspotmon(mtmp)) ? "it"
+              : !mtmp->mtame ? mon_nam(mtmp)
+                : x_monnam(mtmp, namedpet ? ARTICLE_NONE : ARTICLE_THE,
+                           "poor", namedpet ? SUPPRESS_SADDLE : 0, FALSE));
 #else
-            You("%s%s\82ð\93|\82µ\82½\81I",
-                mtmp->mtame ? "\82©\82í\82¢\82»\82¤\82È" : "",
+        You("%s%s\82ð\93|\82µ\82½\81I",
+            !(wasinside || canspotmon(mtmp)) ? "\82»\82ê"
+              : mtmp->mtame ? "\82©\82í\82¢\82»\82¤\82È" : "",
                 mon_nam(mtmp));
 #endif
-        }
     }
 
     if (mtmp->mtrapped && (t = t_at(x, y)) != 0
-        && (t->ttyp == PIT || t->ttyp == SPIKED_PIT)) {
+        && is_pit(t->ttyp)) {
         if (sobj_at(BOULDER, x, y))
-            dest |= 2; /*
-        * Prevent corpses/treasure being created "on top"
-        * of the boulder that is about to fall in. This is
-        * out of order, but cannot be helped unless this
-        * whole routine is rearranged.
-        */
+            nocorpse = TRUE; /* Prevent corpses/treasure being created
+                                "on top" of boulder that is about to fall in.
+                                This is out of order, but cannot be helped
+                                unless this whole routine is rearranged. */
         if (m_carrying(mtmp, BOULDER))
             burycorpse = TRUE;
     }
@@ -2305,7 +2549,9 @@ int dest; /* dest==1, normal; dest==0, don't print message; dest==2, don't
     if (mtmp->mtame && !mtmp->isminion)
         EDOG(mtmp)->killed_by_u = 1;
 
-    if (wasinside && thrownobj && thrownobj != uball) {
+    if (wasinside && thrownobj && thrownobj != uball
+        /* don't give to mon if missile is going to return to hero */
+        && thrownobj != (struct obj *) iflags.returning_missile) {
         /* thrown object has killed hero's engulfer; add it to mon's
            inventory now so that it will be placed with mon's other
            stuff prior to lookhere/autopickup when hero is expelled
@@ -2316,18 +2562,19 @@ int dest; /* dest==1, normal; dest==0, don't print message; dest==2, don't
         thrownobj = 0;
     }
 
-    vamp_rise_msg = FALSE; /* might get set in mondead() */
+    vamp_rise_msg = FALSE; /* might get set in mondead(); only checked below */
+    disintegested = nocorpse; /* alternate vamp_rise message needed if true */
     /* dispose of monster and make cadaver */
     if (stoned)
         monstone(mtmp);
     else
         mondead(mtmp);
+    disintegested = FALSE; /* reset */
 
-    if (mtmp->mhp > 0) { /* monster lifesaved */
+    if (!DEADMONSTER(mtmp)) { /* monster lifesaved */
         /* Cannot put the non-visible lifesaving message in
-         * lifesaved_monster() since the message appears only when you
-         * kill it (as opposed to visible lifesaving which always
-         * appears).
+         * lifesaved_monster() since the message appears only when _you_
+         * kill it (as opposed to visible lifesaving which always appears).
          */
         stoned = FALSE;
         if (!cansee(x, y) && !vamp_rise_msg)
@@ -2346,7 +2593,7 @@ int dest; /* dest==1, normal; dest==0, don't print message; dest==2, don't
         goto cleanup;
     }
 
-    if ((dest & 2) || LEVEL_SPECIFIC_NOCORPSE(mdat))
+    if (nocorpse || LEVEL_SPECIFIC_NOCORPSE(mdat))
         goto cleanup;
 
 #ifdef MAIL
@@ -2373,7 +2620,11 @@ int dest; /* dest==1, normal; dest==0, don't print message; dest==2, don't
                 /* oc_big is also oc_bimanual and oc_bulky */
                 && (otmp->owt > 30 || objects[otyp].oc_big)) {
                 delobj(otmp);
-            } else if (!flooreffects(otmp, x, y, (dest & 1) ? "fall" : "")) {
+#if 0 /*JP:T*/
+            } else if (!flooreffects(otmp, x, y, nomsg ? "" : "fall")) {
+#else
+            } else if (!flooreffects(otmp, x, y, nomsg ? "" : "\97\8e\82¿\82é")) {
+#endif
                 place_object(otmp, x, y);
                 stackobj(otmp);
             }
@@ -2383,8 +2634,11 @@ int dest; /* dest==1, normal; dest==0, don't print message; dest==2, don't
             cadaver = make_corpse(mtmp, burycorpse ? CORPSTAT_BURIED
                                                    : CORPSTAT_NONE);
             if (burycorpse && cadaver && cansee(x, y) && !mtmp->minvis
-                && cadaver->where == OBJ_BURIED && (dest & 1)) {
+                && cadaver->where == OBJ_BURIED && !nomsg) {
+/*JP
                 pline("%s corpse ends up buried.", s_suffix(Monnam(mtmp)));
+*/
+                pline("%s\82Ì\8e\80\91Ì\82Í\96\84\82Ü\82Á\82Ä\82µ\82Ü\82Á\82½\81D", Monnam(mtmp));
             }
         }
     }
@@ -2393,9 +2647,10 @@ int dest; /* dest==1, normal; dest==0, don't print message; dest==2, don't
     /* monster is gone, corpse or other object might now be visible */
     newsym(x, y);
 
-cleanup:
+ cleanup:
     /* punish bad behaviour */
-    if (is_human(mdat) && (!always_hostile(mdat) && mtmp->malign <= 0)
+    if (is_human(mdat)
+        && (!always_hostile(mdat) && mtmp->malign <= 0)
         && (mndx < PM_ARCHEOLOGIST || mndx > PM_WIZARD)
         && u.ualign.type != A_CHAOTIC) {
         HTelepat &= ~INTRINSIC;
@@ -2425,7 +2680,7 @@ cleanup:
     /* adjust alignment points */
     if (mtmp->m_id == quest_status.leader_m_id) { /* REAL BAD! */
         adjalign(-(u.ualign.record + (int) ALIGNLIM / 2));
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         pline("That was %sa bad idea...",
               u.uevent.qcompleted ? "probably " : "");
 #else
@@ -2503,6 +2758,212 @@ struct monst *mtmp;
         impossible("Can't polystone %s!", a_monnam(mtmp));
 }
 
+boolean
+vamp_stone(mtmp)
+struct monst *mtmp;
+{
+    if (is_vampshifter(mtmp)) {
+        int mndx = mtmp->cham;
+        int x = mtmp->mx, y = mtmp->my;
+
+        /* this only happens if shapeshifted */
+        if (mndx >= LOW_PM && mndx != monsndx(mtmp->data)
+            && !(mvitals[mndx].mvflags & G_GENOD)) {
+            char buf[BUFSZ];
+            boolean in_door = (amorphous(mtmp->data)
+                               && closed_door(mtmp->mx, mtmp->my));
+
+            /* construct a format string before transformation */
+#if 0 /*JP:T*/
+            Sprintf(buf, "The lapidifying %s %s %s",
+                    x_monnam(mtmp, ARTICLE_NONE, (char *) 0,
+                             (SUPPRESS_SADDLE | SUPPRESS_HALLUCINATION
+                              | SUPPRESS_INVISIBLE | SUPPRESS_IT), FALSE),
+                    amorphous(mtmp->data) ? "coalesces on the"
+                       : is_flyer(mtmp->data) ? "drops to the"
+                          : "writhes on the",
+                    surface(x,y));
+#else
+            Sprintf(buf, "\90Î\89»\82µ\82Â\82Â\82 \82é%s\82ª%s%s",
+                    x_monnam(mtmp, ARTICLE_NONE, (char *) 0,
+                             (SUPPRESS_SADDLE | SUPPRESS_HALLUCINATION
+                              | SUPPRESS_INVISIBLE | SUPPRESS_IT), FALSE),
+                    surface(x,y),
+                    amorphous(mtmp->data) ? "\82Ì\8fã\82Å\97Z\8d\87\82µ\82½"
+                       : is_flyer(mtmp->data) ? "\82É\97\8e\82¿\82½"
+                          : "\82Ì\8fã\82Å\90g\82à\82¾\82¦\82½");
+#endif
+            mtmp->mcanmove = 1;
+            mtmp->mfrozen = 0;
+            if (mtmp->mhpmax <= 0)
+                mtmp->mhpmax = 10;
+            mtmp->mhp = mtmp->mhpmax;
+            /* this can happen if previously a fog cloud */
+            if (u.uswallow && (mtmp == u.ustuck))
+                expels(mtmp, mtmp->data, FALSE);
+            if (in_door) {
+                coord new_xy;
+
+                if (enexto(&new_xy, mtmp->mx, mtmp->my, &mons[mndx])) {
+                    rloc_to(mtmp, new_xy.x, new_xy.y);
+                }
+            }
+            if (canspotmon(mtmp)) {
+/*JP
+                pline("%s!", buf);
+*/
+                pline("%s\81I", buf);
+                display_nhwindow(WIN_MESSAGE, FALSE);
+            }
+            newcham(mtmp, &mons[mndx], FALSE, FALSE);
+            if (mtmp->data == &mons[mndx])
+                mtmp->cham = NON_PM;
+            else
+                mtmp->cham = mndx;
+            if (canspotmon(mtmp)) {
+#if 0 /*JP:T*/
+                pline("%s rises from the %s with renewed agility!",
+                      Amonnam(mtmp), surface(mtmp->mx, mtmp->my));
+#else
+                pline("%s\82Í\8b@\95q\82³\82ð\8eæ\82è\96ß\82µ\82Ä%s\82©\82ç\95\9c\8a\88\82µ\82½\81I",
+                      Amonnam(mtmp), surface(mtmp->mx, mtmp->my));
+#endif
+            }
+            newsym(mtmp->mx, mtmp->my);
+            return FALSE;   /* didn't petrify */
+        }
+    } else if (mtmp->cham >= LOW_PM
+               && (mons[mtmp->cham].mresists & MR_STONE)) {
+        /* sandestins are stoning-immune so if hit by stoning damage
+           they revert to innate shape rather than become a statue */
+        mtmp->mcanmove = 1;
+        mtmp->mfrozen = 0;
+        if (mtmp->mhpmax <= 0)
+            mtmp->mhpmax = 10;
+        mtmp->mhp = mtmp->mhpmax;
+        (void) newcham(mtmp, &mons[mtmp->cham], FALSE, TRUE);
+        newsym(mtmp->mx, mtmp->my);
+        return FALSE;   /* didn't petrify */
+    }
+    return TRUE;
+}
+
+/* drop monster into "limbo" - that is, migrate to the current level */
+void
+m_into_limbo(mtmp)
+struct monst *mtmp;
+{
+    xchar target_lev = ledger_no(&u.uz), xyloc = MIGR_APPROX_XY;
+
+    mtmp->mstate |= MON_LIMBO;
+    migrate_mon(mtmp, target_lev, xyloc);
+}
+
+STATIC_OVL void
+migrate_mon(mtmp, target_lev, xyloc)
+struct monst *mtmp;
+xchar target_lev, xyloc;
+{
+    unstuck(mtmp);
+    mdrop_special_objs(mtmp);
+    migrate_to_level(mtmp, target_lev, xyloc, (coord *) 0);
+    mtmp->mstate |= MON_MIGRATING;
+}
+
+STATIC_OVL boolean
+ok_to_obliterate(mtmp)
+struct monst *mtmp;
+{
+    /*
+     * Add checks for monsters that should not be obliterated
+     * here (return FALSE).
+     */
+    if (mtmp->data == &mons[PM_WIZARD_OF_YENDOR] || is_rider(mtmp->data)
+        || has_emin(mtmp) || has_epri(mtmp) || has_eshk(mtmp)
+        || (u.ustuck == mtmp) || (u.usteed == mtmp))
+        return FALSE;
+    return TRUE;
+}
+
+void
+elemental_clog(mon)
+struct monst *mon;
+{
+    int m_lev = 0;
+    static long msgmv = 0L;
+    struct monst *mtmp, *m1, *m2, *m3, *m4, *m5, *zm;
+
+    if (In_endgame(&u.uz)) {
+        m1 = m2 = m3 = m4 = m5 = zm = (struct monst *) 0;
+        if (!msgmv || (moves - msgmv) > 200L) {
+            if (!msgmv || rn2(2))
+/*JP
+                You_feel("besieged.");
+*/
+                You_feel("\95ï\88Í\82³\82ê\82½\82æ\82¤\82É\8a´\82\82½\81D");
+            msgmv = moves;
+        }
+        /*
+         * m1 an elemental from another plane.
+         * m2 an elemental from this plane.
+         * m3 the least powerful monst encountered in loop so far.
+         * m4 some other non-tame monster.
+         * m5 a pet.
+         */
+        for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
+            if (DEADMONSTER(mtmp) || mtmp == mon)
+                continue;
+            if (mtmp->mx == 0 && mtmp->my == 0)
+                continue;
+            if (mon_has_amulet(mtmp) || !ok_to_obliterate(mtmp))
+                continue;
+            if (mtmp->data->mlet == S_ELEMENTAL) {
+                if (!is_home_elemental(mtmp->data)) {
+                    if (!m1)
+                        m1 = mtmp;
+                } else {
+                    if (!m2)
+                        m2 = mtmp;
+                }
+            } else {
+                if (!mtmp->mtame) {
+                    if (!m_lev || mtmp->m_lev < m_lev) {
+                        m_lev = mtmp->m_lev;
+                        m3 = mtmp;
+                    } else if (!m4) {
+                        m4 = mtmp;
+                    }
+                } else {
+                    if (!m5)
+                        m5 = mtmp;
+                    break;
+                }
+            }
+        }
+        mtmp = m1 ? m1 : m2 ? m2 : m3 ? m3 : m4 ? m4 : m5 ? m5 : zm;
+        if (mtmp) {
+            int mx = mtmp->mx, my = mtmp->my;
+
+            mtmp->mstate |= MON_OBLITERATE;
+            mongone(mtmp);
+            /* places in the code might still reference mtmp->mx, mtmp->my */
+            /* mtmp->mx = mtmp->my = 0; */
+            rloc_to(mon, mx, my);           /* note: mon, not mtmp */
+
+        /* last resort - migrate mon to the next plane */
+        } else if (!Is_astralevel(&u.uz)) {
+            d_level dest;
+            xchar target_lev;
+
+            dest = u.uz;
+            dest.dlevel--;
+            target_lev = ledger_no(&dest);
+            mon->mstate |= MON_ENDGAME_MIGR;
+            migrate_mon(mon, target_lev, MIGR_RANDOM);
+        }
+    }
+}
+
 /* make monster mtmp next to you (if possible);
    might place monst on far side of a wall or boulder */
 void
@@ -2519,18 +2980,37 @@ struct monst *mtmp;
         return;
     }
 
-    if (!enexto(&mm, u.ux, u.uy, mtmp->data))
+    if (!enexto(&mm, u.ux, u.uy, mtmp->data) || !isok(mm.x, mm.y)) {
+        deal_with_overcrowding(mtmp);
         return;
+    }
     rloc_to(mtmp, mm.x, mm.y);
     if (!in_mklev && (mtmp->mstrategy & STRAT_APPEARMSG)) {
         mtmp->mstrategy &= ~STRAT_APPEARMSG; /* one chance only */
         if (!couldspot && canspotmon(mtmp))
+#if 0 /*JP:T*/
             pline("%s suddenly %s!", Amonnam(mtmp),
                   !Blind ? "appears" : "arrives");
+#else
+            pline("\93Ë\91R%s\82ª\8c»\82ê\82½\81I", Amonnam(mtmp));
+#endif
     }
     return;
 }
 
+STATIC_OVL void
+deal_with_overcrowding(mtmp)
+struct monst *mtmp;
+{
+    if (In_endgame(&u.uz)) {
+        debugpline1("overcrowding: elemental_clog on %s", m_monnam(mtmp));
+        elemental_clog(mtmp);
+    } else {
+        debugpline1("overcrowding: sending %s into limbo", m_monnam(mtmp));
+        m_into_limbo(mtmp);
+    }
+}
+
 /* like mnexto() but requires destination to be directly accessible */
 void
 maybe_mnexto(mtmp)
@@ -2556,10 +3036,15 @@ struct monst *mtmp;
 /* mnearto()
  * Put monster near (or at) location if possible.
  * Returns:
- *      1 - if a monster was moved from x, y to put mtmp at x, y.
- *      0 - in most cases.
+ *  2 if another monster was moved out of this one's way;
+ *  1 if relocation was successful (without moving another one);
+ *  0 otherwise.
+ * Note: if already at the target spot, result is 1 rather than 0.
+ *
+ * Might be called recursively if 'move_other' is True; if so, that argument
+ * will be False on the nested call so there won't be any further recursion.
  */
-boolean
+int
 mnearto(mtmp, x, y, move_other)
 register struct monst *mtmp;
 xchar x, y;
@@ -2568,15 +3053,19 @@ boolean move_other; /* make sure mtmp gets to x, y! so move m_at(x, y) */
     struct monst *othermon = (struct monst *) 0;
     xchar newx, newy;
     coord mm;
+    int res = 1;
 
-    if (mtmp->mx == x && mtmp->my == y)
-        return FALSE;
+    if (mtmp->mx == x && mtmp->my == y && m_at(x, y) == mtmp)
+        return res;
 
     if (move_other && (othermon = m_at(x, y)) != 0) {
         if (othermon->wormno)
             remove_worm(othermon);
         else
             remove_monster(x, y);
+
+        othermon->mx = othermon->my = 0; /* 'othermon' is not on the map */
+        othermon->mstate |= MON_OFFMAP;
     }
 
     newx = x;
@@ -2586,29 +3075,28 @@ boolean move_other; /* make sure mtmp gets to x, y! so move m_at(x, y) */
          * Migrating_mons that need to be placed will cause
          * no end of trouble.
          */
-        if (!enexto(&mm, newx, newy, mtmp->data))
-            return FALSE;
+        if (!enexto(&mm, newx, newy, mtmp->data) || !isok(mm.x, mm.y)) {
+            if (othermon) {
+                /* othermon already had its mx, my set to 0 above
+                 * and this would shortly cause a sanity check to fail
+                 * if we just return 0 here. The caller only possesses
+                 * awareness of mtmp, not othermon. */
+                deal_with_overcrowding(othermon);
+            }
+            return 0;
+        }
         newx = mm.x;
         newy = mm.y;
     }
     rloc_to(mtmp, newx, newy);
 
     if (move_other && othermon) {
-        xchar oldx = othermon->mx, oldy = othermon->my;
-
-        othermon->mx = othermon->my = 0;
-        (void) mnearto(othermon, x, y, FALSE);
-        if (othermon->mx == 0 && othermon->my == 0) {
-            /* reloc failed, dump monster into "limbo"
-               (aka migrate to current level) */
-            othermon->mx = oldx;
-            othermon->my = oldy;
-            mdrop_special_objs(othermon);
-            migrate_to_level(othermon, ledger_no(&u.uz), MIGR_APPROX_XY, NULL);
-        }
+        res = 2; /* moving another monster out of the way */
+        if (!mnearto(othermon, x, y, FALSE))  /* no 'move_other' this time */
+            deal_with_overcrowding(othermon);
     }
 
-    return FALSE;
+    return res;
 }
 
 /* monster responds to player action; not the same as a passive attack;
@@ -2644,10 +3132,43 @@ struct monst *mtmp;
     }
 }
 
+/* Called whenever the player attacks mtmp; also called in other situations
+   where mtmp gets annoyed at the player. Handles mtmp getting annoyed at the
+   attack and any ramifications that might have. Useful also in situations
+   where mtmp was already hostile; it checks for situations where the player
+   shouldn't be attacking and any ramifications /that/ might have. */
 void
-setmangry(mtmp)
+setmangry(mtmp, via_attack)
 struct monst *mtmp;
+boolean via_attack;
 {
+    if (via_attack && sengr_at("Elbereth", u.ux, u.uy, TRUE)
+        /* only hypocritical if monster is vulnerable to Elbereth (or
+           peaceful--not vulnerable but attacking it is hypocritical) */
+        && (onscary(u.ux, u.uy, mtmp) || mtmp->mpeaceful)) {
+/*JP
+        You_feel("like a hypocrite.");
+*/
+        You_feel("\8bU\91P\8eÒ\82Ì\82æ\82¤\82È\8bC\82ª\82µ\82½\81D");
+        /* AIS: Yes, I know alignment penalties and bonuses aren't balanced
+           at the moment. This is about correct relative to other "small"
+           penalties; it should be fairly large, as attacking while standing
+           on an Elbereth means that you're requesting peace and then
+           violating your own request. I know 5 isn't actually large, but
+           it's intentionally larger than the 1s and 2s that are normally
+           given for this sort of thing. */
+        /* reduce to 3 (average) when alignment is already very low */
+        adjalign((u.ualign.record > 5) ? -5 : -rnd(5));
+
+        if (!Blind)
+/*JP
+            pline("The engraving beneath you fades.");
+*/
+            pline("\82 \82È\82½\82Ì\91«\8c³\82Ì\95\8e\9a\82ª\94\96\82ê\82½\81D");
+        del_engr_at(u.ux, u.uy);
+    }
+
+    /* AIS: Should this be in both places, or just in wakeup()? */
     mtmp->mstrategy &= ~STRAT_WAITMASK;
     if (!mtmp->mpeaceful)
         return;
@@ -2688,68 +3209,142 @@ struct monst *mtmp;
                     ++got_mad;
             }
         }
-        if (got_mad && !Hallucination)
-#if 0 /*JP*/
-            pline_The("%s appear%s to be angry too...",
-                      got_mad == 1 ? q_guardian->mname
-                                   : makeplural(q_guardian->mname),
-                      got_mad == 1 ? "s" : "");
+        if (got_mad && !Hallucination) {
+            const char *who = q_guardian->mname;
+
+#if 0 /*JP:T*/
+            if (got_mad > 1)
+                who = makeplural(who);
+            pline_The("%s %s to be angry too...",
+                      who, vtense(who, "appear"));
 #else
-            pline("%s\82à\93{\82Á\82½\81D\81D\81D", q_guardian->mname);
+            pline("%s\82à\93{\82Á\82½\82æ\82¤\82¾\81D\81D\81D", who);
 #endif
+        }
+    }
+
+    /* make other peaceful monsters react */
+    if (!context.mon_moving) {
+        static const char *const Exclam[] = {
+/*JP
+            "Gasp!", "Uh-oh.", "Oh my!", "What?", "Why?",
+*/
+            "\82®\82Í\82Á\81I", "\82¤\82í\81D", "\82È\82ñ\82Æ\81I", "\82È\82É\82Á\81H", "\82È\82ñ\82¾\81H",
+        };
+        struct monst *mon;
+        int mndx = monsndx(mtmp->data);
+
+        for (mon = fmon; mon; mon = mon->nmon) {
+            if (DEADMONSTER(mon))
+                continue;
+            if (mon == mtmp) /* the mpeaceful test catches this since mtmp */
+                continue;    /* is no longer peaceful, but be explicit...  */
+
+            if (!mindless(mon->data) && mon->mpeaceful
+                && couldsee(mon->mx, mon->my) && !mon->msleeping
+                && mon->mcansee && m_canseeu(mon)) {
+                boolean exclaimed = FALSE;
+
+                if (humanoid(mon->data) || mon->isshk || mon->ispriest) {
+                    if (is_watch(mon->data)) {
+/*JP
+                        verbalize("Halt!  You're under arrest!");
+*/
+                        verbalize("\8e~\82Ü\82ê\81I\91ß\95ß\82·\82é\81I");
+                        (void) angry_guards(!!Deaf);
+                    } else {
+                        if (!rn2(5)) {
+                            verbalize("%s", Exclam[mon->m_id % SIZE(Exclam)]);
+                            exclaimed = TRUE;
+                        }
+                        /* shopkeepers and temple priests might gasp in
+                           surprise, but they won't become angry here */
+                        if (mon->isshk || mon->ispriest)
+                            continue;
+
+                        if (mon->data->mlevel < rn2(10)) {
+                            monflee(mon, rn2(50) + 25, TRUE, !exclaimed);
+                            exclaimed = TRUE;
+                        }
+                        if (mon->mtame) {
+                            /* mustn't set mpeaceful to 0 as below;
+                               perhaps reduce tameness? */
+                        } else {
+                            mon->mpeaceful = 0;
+                            adjalign(-1);
+                            if (!exclaimed)
+/*JP
+                                pline("%s gets angry!", Monnam(mon));
+*/
+                                pline("%s\82Í\93{\82Á\82½\81I", Monnam(mon));
+                        }
+                    }
+                } else if (mon->data->mlet == mtmp->data->mlet
+                           && big_little_match(mndx, monsndx(mon->data))
+                           && !rn2(3)) {
+                    if (!rn2(4)) {
+                        growl(mon);
+                        exclaimed = TRUE;
+                    }
+                    if (rn2(6))
+                        monflee(mon, rn2(25) + 15, TRUE, !exclaimed);
+                }
+            }
+        }
     }
 }
 
-/* wake up a monster, usually making it angry in the process */
+/* wake up a monster, possibly making it angry in the process */
 void
-wakeup(mtmp)
+wakeup(mtmp, via_attack)
 register struct monst *mtmp;
+boolean via_attack;
 {
     mtmp->msleeping = 0;
-    finish_meating(mtmp);
-    setmangry(mtmp);
-    if (mtmp->m_ap_type) {
+    if (M_AP_TYPE(mtmp)) {
         seemimic(mtmp);
     } else if (context.forcefight && !context.mon_moving
                && mtmp->mundetected) {
         mtmp->mundetected = 0;
         newsym(mtmp->mx, mtmp->my);
     }
+    finish_meating(mtmp);
+    if (via_attack)
+        setmangry(mtmp, TRUE);
 }
 
 /* Wake up nearby monsters without angering them. */
 void
 wake_nearby()
 {
-    register struct monst *mtmp;
-
-    for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
-        if (DEADMONSTER(mtmp))
-            continue;
-        if (distu(mtmp->mx, mtmp->my) < u.ulevel * 20) {
-            mtmp->msleeping = 0;
-            if (!unique_corpstat(mtmp->data))
-                mtmp->mstrategy &= ~STRAT_WAITMASK;
-            if (mtmp->mtame && !mtmp->isminion)
-                EDOG(mtmp)->whistletime = moves;
-        }
-    }
+    wake_nearto(u.ux, u.uy, u.ulevel * 20);
 }
 
 /* Wake up monsters near some particular location. */
 void
 wake_nearto(x, y, distance)
-register int x, y, distance;
+int x, y, distance;
 {
-    register struct monst *mtmp;
+    struct monst *mtmp;
 
     for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
         if (DEADMONSTER(mtmp))
             continue;
         if (distance == 0 || dist2(mtmp->mx, mtmp->my, x, y) < distance) {
-            mtmp->msleeping = 0;
-            if (!unique_corpstat(mtmp->data))
-                mtmp->mstrategy &= ~STRAT_WAITMASK;
+            /* sleep for N turns uses mtmp->mfrozen, but so does paralysis
+               so we leave mfrozen monsters alone */
+            mtmp->msleeping = 0; /* wake indeterminate sleep */
+            if (!(mtmp->data->geno & G_UNIQ))
+                mtmp->mstrategy &= ~STRAT_WAITMASK; /* wake 'meditation' */
+            if (context.mon_moving)
+                continue;
+            if (mtmp->mtame) {
+                if (!mtmp->isminion)
+                    EDOG(mtmp)->whistletime = moves;
+                /* Clear mtrack. This is to fix up a pet who is
+                   stuck "fleeing" its master. */
+                memset(mtmp->mtrack, 0, sizeof mtmp->mtrack);
+            }
         }
     }
 }
@@ -2759,8 +3354,7 @@ void
 seemimic(mtmp)
 register struct monst *mtmp;
 {
-    boolean is_blocker_appear = (is_door_mappear(mtmp)
-                                 || is_obj_mappear(mtmp, BOULDER));
+    boolean is_blocker_appear = (is_lightblocker_mappear(mtmp));
 
     if (has_mcorpsenm(mtmp))
         freemcorpsenm(mtmp);
@@ -2795,7 +3389,7 @@ rescham()
         }
         if (is_were(mtmp->data) && mtmp->data->mlet != S_HUMAN)
             new_were(mtmp);
-        if (mtmp->m_ap_type && cansee(mtmp->mx, mtmp->my)) {
+        if (M_AP_TYPE(mtmp) && cansee(mtmp->mx, mtmp->my)) {
             seemimic(mtmp);
             /* we pretend that the mimic doesn't
                know that it has been unmasked */
@@ -2813,7 +3407,8 @@ restartcham()
     for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
         if (DEADMONSTER(mtmp))
             continue;
-        mtmp->cham = pm_to_cham(monsndx(mtmp->data));
+        if (!mtmp->mcan)
+            mtmp->cham = pm_to_cham(monsndx(mtmp->data));
         if (mtmp->data->mlet == S_MIMIC && mtmp->msleeping
             && cansee(mtmp->mx, mtmp->my)) {
             set_mimic_sym(mtmp);
@@ -2851,11 +3446,11 @@ register struct monst *mtmp;
 {
     struct trap *t;
 
-    if (mtmp->mcan || mtmp->m_ap_type || cansee(mtmp->mx, mtmp->my)
+    if (mtmp->mcan || M_AP_TYPE(mtmp) || cansee(mtmp->mx, mtmp->my)
         || rn2(3) || mtmp == u.ustuck
         /* can't hide while trapped except in pits */
         || (mtmp->mtrapped && (t = t_at(mtmp->mx, mtmp->my)) != 0
-            && !(t->ttyp == PIT || t->ttyp == SPIKED_PIT))
+            && !is_pit(t->ttyp))
         || (sensemon(mtmp) && distu(mtmp->mx, mtmp->my) <= 2))
         return FALSE;
 
@@ -2883,7 +3478,7 @@ struct monst *mtmp;
         ; /* can't hide if holding you or held by you */
     } else if (is_u ? (u.utrap && u.utraptype != TT_PIT)
                     : (mtmp->mtrapped && (t = t_at(x, y)) != 0
-                       && !(t->ttyp == PIT || t->ttyp == SPIKED_PIT))) {
+                       && !is_pit(t->ttyp))) {
         ; /* can't hide while stuck in a non-pit trap */
     } else if (mtmp->data->mlet == S_EEL) {
         undetected = (is_pool(x, y) && !Is_waterlevel(&u.uz));
@@ -2912,7 +3507,7 @@ struct monst *mon;
     boolean hider_under = hides_under(mon->data) || mon->data->mlet == S_EEL;
 
     if ((is_hider(mon->data) || hider_under)
-        && !(mon->mundetected || mon->m_ap_type)) {
+        && !(mon->mundetected || M_AP_TYPE(mon))) {
         xchar x = mon->mx, y = mon->my;
         char save_viz = viz_array[y][x];
 
@@ -2921,7 +3516,7 @@ struct monst *mon;
         if (is_hider(mon->data))
             (void) restrap(mon);
         /* try again if mimic missed its 1/3 chance to hide */
-        if (mon->data->mlet == S_MIMIC && !mon->m_ap_type)
+        if (mon->data->mlet == S_MIMIC && !M_AP_TYPE(mon))
             (void) restrap(mon);
         if (hider_under)
             (void) hideunder(mon);
@@ -2980,38 +3575,68 @@ decide_to_shapeshift(mon, shiftflags)
 struct monst *mon;
 int shiftflags;
 {
-    struct permonst *ptr;
+    struct permonst *ptr = 0;
+    int mndx;
     unsigned was_female = mon->female;
-    boolean msg = FALSE;
+    boolean msg = FALSE, dochng = FALSE;
 
     if ((shiftflags & SHIFT_MSG)
         || ((shiftflags & SHIFT_SEENMSG) && sensemon(mon)))
         msg = TRUE;
 
-    if (is_vampshifter(mon)) {
+    if (!is_vampshifter(mon)) {
+        /* regular shapeshifter */
+        if (!rn2(6))
+            dochng = TRUE;
+    } else {
         /* The vampire has to be in good health (mhp) to maintain
          * its shifted form.
-             *
-         * If we're shifted and getting low on hp, maybe shift back.
+         *
+         * If we're shifted and getting low on hp, maybe shift back, or
+         * if we're a fog cloud at full hp, maybe pick a different shape.
          * If we're not already shifted and in good health, maybe shift.
          */
-        if ((mon->mhp <= mon->mhpmax / 6) && rn2(4) && (mon->cham >= LOW_PM))
-            (void) newcham(mon, &mons[mon->cham], FALSE, msg);
-    } else if (mon->data->mlet == S_VAMPIRE && mon->cham == NON_PM && !rn2(6)
-               && (mon->mhp > mon->mhpmax - ((mon->mhpmax / 10) + 1))) {
-        (void) newcham(mon, (struct permonst *) 0, FALSE, msg);
+        if (mon->data->mlet != S_VAMPIRE) {
+            if ((mon->mhp <= (mon->mhpmax + 5) / 6) && rn2(4)
+                && mon->cham >= LOW_PM) {
+                ptr = &mons[mon->cham];
+                dochng = TRUE;
+            } else if (mon->data == &mons[PM_FOG_CLOUD]
+                     && mon->mhp == mon->mhpmax && !rn2(4)
+                     && (!canseemon(mon)
+                         || distu(mon->mx, mon->my) > BOLT_LIM * BOLT_LIM)) {
+                /* if a fog cloud, maybe change to wolf or vampire bat;
+                   those are more likely to take damage--at least when
+                   tame--and then switch back to vampire; they'll also
+                   switch to fog cloud if they encounter a closed door */
+                mndx = pickvampshape(mon);
+                if (mndx >= LOW_PM) {
+                    ptr = &mons[mndx];
+                    dochng = (ptr != mon->data);
+                }
+            }
+        } else {
+            if (mon->mhp >= 9 * mon->mhpmax / 10 && !rn2(6)
+                && (!canseemon(mon)
+                    || distu(mon->mx, mon->my) > BOLT_LIM * BOLT_LIM))
+                dochng = TRUE; /* 'ptr' stays Null */
+        }
+    }
+    if (dochng) {
+        if (newcham(mon, ptr, FALSE, msg) && is_vampshifter(mon)) {
+            /* for vampshift, override the 10% chance for sex change */
+            ptr = mon->data;
+            if (!is_male(ptr) && !is_female(ptr) && !is_neuter(ptr))
+                mon->female = was_female;
+        }
     }
-    /* override the 10% chance for sex change */
-    ptr = mon->data;
-    if (!is_male(ptr) && !is_female(ptr) && !is_neuter(ptr))
-        mon->female = was_female;
 }
 
 STATIC_OVL int
 pickvampshape(mon)
 struct monst *mon;
 {
-    int mndx = mon->cham;
+    int mndx = mon->cham, wolfchance = 10;
     /* avoid picking monsters with lowercase display symbols ('d' for wolf
        and 'v' for fog cloud) on rogue level*/
     boolean uppercase_only = Is_rogue_level(&u.uz);
@@ -3021,9 +3646,10 @@ struct monst *mon;
         /* ensure Vlad can keep carrying the Candelabrum */
         if (mon_has_special(mon))
             break; /* leave mndx as is */
+        wolfchance = 3;
     /*FALLTHRU*/
     case PM_VAMPIRE_LORD: /* vampire lord or Vlad can become wolf */
-        if (!rn2(10) && !uppercase_only) {
+        if (!rn2(wolfchance) && !uppercase_only) {
             mndx = PM_WOLF;
             break;
         }
@@ -3055,7 +3681,7 @@ int mndx;
     if (mndx == NON_PM)
         return TRUE; /* caller wants random */
 
-    if (!accept_newcham_form(mndx))
+    if (!accept_newcham_form(mon, mndx))
         return FALSE; /* geno'd or !polyok */
 
     if (isspecmon(mon)) {
@@ -3071,7 +3697,7 @@ int mndx;
 }
 
 /* prevent wizard mode user from specifying invalid vampshifter shape */
-STATIC_OVL boolean
+boolean
 validvamp(mon, mndx_p, monclass)
 struct monst *mon;
 int *mndx_p, monclass;
@@ -3080,17 +3706,17 @@ int *mndx_p, monclass;
     if (!is_vampshifter(mon))
         return validspecmon(mon, *mndx_p);
 
-    if (*mndx_p == PM_VAMPIRE || *mndx_p == PM_VAMPIRE_LORD
-        || *mndx_p == PM_VLAD_THE_IMPALER) {
-        /* player picked some type of vampire; use mon's self */
-        *mndx_p = mon->cham;
-        return TRUE;
-    }
     if (mon->cham == PM_VLAD_THE_IMPALER && mon_has_special(mon)) {
         /* Vlad with Candelabrum; override choice, then accept it */
         *mndx_p = PM_VLAD_THE_IMPALER;
         return TRUE;
     }
+    if (*mndx_p >= LOW_PM && is_shapeshifter(&mons[*mndx_p])) {
+        /* player picked some type of shapeshifter; use mon's self
+           (vampire or chameleon) */
+        *mndx_p = mon->cham;
+        return TRUE;
+    }
     /* basic vampires can't become wolves; any can become fog or bat
        (we don't enforce upper-case only for rogue level here) */
     if (*mndx_p == PM_WOLF)
@@ -3114,7 +3740,7 @@ int *mndx_p, monclass;
             *mndx_p = PM_WOLF;
             break;
         }
-    /*FALLTHRU*/
+        /*FALLTHRU*/
     default:
         *mndx_p = NON_PM;
         break;
@@ -3174,15 +3800,38 @@ struct monst *mon;
       }
         break;
     }
+
     /* for debugging: allow control of polymorphed monster */
     if (wizard && iflags.mon_polycontrol) {
-        char pprompt[BUFSZ], buf[BUFSZ];
-        int monclass;
-
-        Sprintf(pprompt, "Change %s @ <%d,%d> into what kind of monster?",
-                noit_mon_nam(mon), (int) mon->mx, (int) mon->my);
-        tryct = 5;
+        char pprompt[BUFSZ], parttwo[QBUFSZ], buf[BUFSZ];
+        int monclass, len;
+
+        /* construct prompt in pieces */
+        Sprintf(pprompt, "Change %s", noit_mon_nam(mon));
+        Sprintf(parttwo, " @ %s into what?",
+                coord_desc((int) mon->mx, (int) mon->my, buf,
+                           (iflags.getpos_coords != GPCOORDS_NONE)
+                              ? iflags.getpos_coords : GPCOORDS_MAP));
+        /* combine the two parts, not exceeding QBUFSZ-1 in overall length;
+           if combined length is too long it has to be due to monster's
+           name so we'll chop enough of that off to fit the second part */
+        if ((len = (int) strlen(pprompt) + (int) strlen(parttwo)) >= QBUFSZ)
+            /* strlen(parttwo) is less than QBUFSZ/2 so strlen(pprompt) is
+               more than QBUFSZ/2 and excess amount being truncated can't
+               exceed pprompt's length and back up to before &pprompt[0]) */
+            *(eos(pprompt) - (len - (QBUFSZ - 1))) = '\0';
+        Strcat(pprompt, parttwo);
+
+        buf[0] = '\0'; /* clear buffer for EDIT_GETLIN */
+#define TRYLIMIT 5
+        tryct = TRYLIMIT;
         do {
+            if (tryct == TRYLIMIT - 1) { /* first retry */
+                /* change "into what?" to "into what kind of monster?" */
+                if (strlen(pprompt) + sizeof " kind of monster" - 1 < QBUFSZ)
+                    Strcpy(eos(pprompt) - 1, " kind of monster?");
+            }
+#undef TRYLIMIT
             monclass = 0;
             getlin(pprompt, buf);
             mungspaces(buf);
@@ -3190,7 +3839,7 @@ struct monst *mon;
             if (*buf == '\033')
                 break;
             /* for "*", use NON_PM to pick an arbitrary shape below */
-            if (!strcmp(buf, "*") || !strcmp(buf, "random")) {
+            if (!strcmp(buf, "*") || !strcmpi(buf, "random")) {
                 mndx = NON_PM;
                 break;
             }
@@ -3212,6 +3861,7 @@ struct monst *mon;
 
             pline("It can't become that.");
         } while (--tryct > 0);
+
         if (!tryct)
             pline1(thats_enough_tries);
         if (is_vampshifter(mon) && !validvamp(mon, &mndx, monclass))
@@ -3233,7 +3883,8 @@ struct monst *mon;
 
 /* this used to be inline within newcham() but monpolycontrol needs it too */
 STATIC_OVL struct permonst *
-accept_newcham_form(mndx)
+accept_newcham_form(mon, mndx)
+struct monst *mon;
 int mndx;
 {
     struct permonst *mdat;
@@ -3250,6 +3901,11 @@ int mndx;
        polyok() rejects, so we need a special case here */
     if (is_mplayer(mdat))
         return mdat;
+    /* shapeshifters are rejected by polyok() but allow a shapeshifter
+       to take on its 'natural' form */
+    if (is_shapeshifter(mdat)
+        && mon->cham >= LOW_PM && mdat == &mons[mon->cham])
+        return mdat;
     /* polyok() rules out M2_PNAME, M2_WERE, and all humans except Kops */
     return polyok(mdat) ? mdat : 0;
 }
@@ -3284,11 +3940,25 @@ boolean msg;      /* "The oldmon turns into a newmon!" */
     int hpn, hpd;
     int mndx, tryct;
     struct permonst *olddata = mtmp->data;
-    char oldname[BUFSZ], newname[BUFSZ];
+    char *p, oldname[BUFSZ], l_oldname[BUFSZ], newname[BUFSZ];
 
-    /* Riders are immune to polymorph and green slime */
-    if (is_rider(mtmp->data))
-        return 0;
+    /* Riders are immune to polymorph and green slime
+       (but apparent Rider might actually be a doppelganger) */
+    if (mtmp->cham == NON_PM) { /* not a shapechanger */
+        if (is_rider(olddata))
+            return 0;
+        /* make Nazgul and erinyes immune too, to reduce chance of
+           anomalous extinction feedback during final disclsoure */
+        if (mbirth_limit(monsndx(olddata)) < MAXMONNO)
+            return 0;
+        /* cancelled shapechangers become uncancelled prior
+           to being given a new shape */
+        if (mtmp->mcan && !Protection_from_shape_changers) {
+            mtmp->cham = pm_to_cham(monsndx(mtmp->data));
+            if (mtmp->cham != NON_PM)
+                mtmp->mcan = 0;
+        }
+    }
 
     if (msg) {
         /* like Monnam() but never mention saddle */
@@ -3296,6 +3966,9 @@ boolean msg;      /* "The oldmon turns into a newmon!" */
                                  SUPPRESS_SADDLE, FALSE));
         oldname[0] = highc(oldname[0]);
     }
+    /* we need this one whether msg is true or not */
+    Strcpy(l_oldname, x_monnam(mtmp, ARTICLE_THE, (char *) 0,
+                               has_mname(mtmp) ? SUPPRESS_SADDLE : 0, FALSE));
 
     /* mdat = 0 -> caller wants a random monster shape */
     if (mdat == 0) {
@@ -3304,7 +3977,7 @@ boolean msg;      /* "The oldmon turns into a newmon!" */
         tryct = 20;
         do {
             mndx = select_newcham_form(mtmp);
-            mdat = accept_newcham_form(mndx);
+            mdat = accept_newcham_form(mtmp, mndx);
             /* for the first several tries we require upper-case on
                the rogue level (after that, we take whatever we get) */
             if (tryct > 15 && Is_rogue_level(&u.uz)
@@ -3318,29 +3991,24 @@ boolean msg;      /* "The oldmon turns into a newmon!" */
     } else if (mvitals[monsndx(mdat)].mvflags & G_GENOD)
         return 0; /* passed in mdat is genocided */
 
-    mgender_from_permonst(mtmp, mdat);
-
-    if (In_endgame(&u.uz) && is_mplayer(olddata) && has_mname(mtmp)) {
-        /* mplayers start out as "Foo the Bar", but some of the
-         * titles are inappropriate when polymorphed, particularly
-         * into the opposite sex.  players don't use ranks when
-         * polymorphed, so dropping the rank for mplayers seems
-         * reasonable.
-         */
-        char *p = index(MNAME(mtmp), ' ');
-
-        if (p)
-            *p = '\0';
-    }
-
-    if (mdat == mtmp->data)
+    if (mdat == olddata)
         return 0; /* still the same monster */
 
+    mgender_from_permonst(mtmp, mdat);
+    /* Endgame mplayers start out as "Foo the Bar", but some of the
+     * titles are inappropriate when polymorphed, particularly into
+     * the opposite sex.  Player characters don't use ranks when
+     * polymorphed, so dropping rank for mplayers seems reasonable.
+     */
+    if (In_endgame(&u.uz) && is_mplayer(olddata)
+        && has_mname(mtmp) && (p = strstr(MNAME(mtmp), " the ")) != 0)
+        *p = '\0';
+
     if (mtmp->wormno) { /* throw tail away */
         wormgone(mtmp);
         place_monster(mtmp, mtmp->mx, mtmp->my);
     }
-    if (mtmp->m_ap_type && mdat->mlet != S_MIMIC)
+    if (M_AP_TYPE(mtmp) && mdat->mlet != S_MIMIC)
         seemimic(mtmp); /* revert to normal monster */
 
     /* (this code used to try to adjust the monster's health based on
@@ -3364,7 +4032,10 @@ boolean msg;      /* "The oldmon turns into a newmon!" */
         mtmp->mhp = 1;
 
     /* take on the new form... */
-    set_mon_data(mtmp, mdat, 0);
+    set_mon_data(mtmp, mdat);
+
+    if (mtmp->mleashed && !leashable(mtmp))
+        m_unleash(mtmp, TRUE);
 
     if (emits_light(olddata) != emits_light(mtmp->data)) {
         /* used to give light, now doesn't, or vice versa,
@@ -3386,13 +4057,38 @@ boolean msg;      /* "The oldmon turns into a newmon!" */
                 /* Does mdat care? */
                 if (!noncorporeal(mdat) && !amorphous(mdat)
                     && !is_whirly(mdat) && (mdat != &mons[PM_YELLOW_LIGHT])) {
-#if 0 /*JP*/
-                    You("break out of %s%s!", mon_nam(mtmp),
-                        (is_animal(mdat) ? "'s stomach" : ""));
+                    char msgtrail[BUFSZ];
+
+                    if (is_vampshifter(mtmp)) {
+#if 0 /*JP:T*/
+                        Sprintf(msgtrail, " which was a shapeshifted %s",
+                                noname_monnam(mtmp, ARTICLE_NONE));
 #else
-                    You("%s%s\82ð\94j\82è\8fo\82½\81I", mon_nam(mtmp),
-                        (is_animal(mdat) ? "\82Ì\88Ý\91Ü" : ""));
+                        Sprintf(msgtrail, "(\8eÀ\8dÛ\82É\82Í\8c`\82ð\95Ï\82¦\82½%s)",
+                                noname_monnam(mtmp, ARTICLE_NONE));
 #endif
+                    } else if (is_animal(mdat)) {
+/*JP
+                        Strcpy(msgtrail, "'s stomach");
+*/
+                        Strcpy(msgtrail, "\82Ì\88Ý");
+                    } else {
+                        msgtrail[0] = '\0';
+                    }
+
+                    /* Do this even if msg is FALSE */
+#if 0 /*JP:T*/
+                    You("%s %s%s!",
+                        (amorphous(olddata) || is_whirly(olddata))
+                            ? "emerge from" : "break out of",
+                        l_oldname, msgtrail);
+#else
+                    You("%s%s%s\81I", l_oldname, msgtrail,
+                        (amorphous(olddata) || is_whirly(olddata))
+                            ? "\82©\82ç\94ò\82Ñ\8fo\82µ\82½" : "\82ð\94j\82è\8fo\82½"
+                        );
+#endif
+                    msg = FALSE; /* message has been given */
                     mtmp->mhp = 1; /* almost dead */
                 }
                 expels(mtmp, olddata, FALSE);
@@ -3415,48 +4111,57 @@ boolean msg;      /* "The oldmon turns into a newmon!" */
 #endif
         /* we can now create worms with tails - 11/91 */
         initworm(mtmp, rn2(5));
-        if (count_wsegs(mtmp))
-            place_worm_tail_randomly(mtmp, mtmp->mx, mtmp->my);
+        place_worm_tail_randomly(mtmp, mtmp->mx, mtmp->my);
     }
 
     newsym(mtmp->mx, mtmp->my);
 
     if (msg) {
-        char *save_mname = 0;
-
-        if (has_mname(mtmp)) {
-            save_mname = MNAME(mtmp);
-            MNAME(mtmp) = (char *) 0;
-        }
-#if 0 /*JP*/
-        Strcpy(newname, (mdat == &mons[PM_GREEN_SLIME])
-                            ? "slime"
-                            : x_monnam(mtmp, ARTICLE_A, (char *) 0,
-                                       SUPPRESS_SADDLE, FALSE));
+        Strcpy(newname, noname_monnam(mtmp, ARTICLE_A));
+        /* oldname was capitalized above; newname will be lower case */
+#if 0 /*JP:T*/
+        if (!strcmpi(newname, "it")) { /* can't see or sense it now */
+#else
+        if (!strcmpi(newname, "\89½\8eÒ\82©")) { /* can't see or sense it now */
+#endif
+#if 0 /*JP:T*/
+            if (!!strcmpi(oldname, "it")) /* could see or sense it before */
 #else
-        Strcpy(newname, (mdat == &mons[PM_GREEN_SLIME])
-                            ? "\83X\83\89\83C\83\80"
-                            : x_monnam(mtmp, ARTICLE_A, (char *) 0,
-                                       SUPPRESS_SADDLE, FALSE));
+            if (!!strcmpi(oldname, "\89½\8eÒ\82©")) /* could see or sense it before */
 #endif
 /*JP
-        if (!strcmpi(oldname, "it") && !strcmpi(newname, "it"))
+                pline("%s disappears!", oldname);
 */
-        if (!strcmpi(oldname, "\89½\8eÒ\82©") && !strcmpi(newname, "\89½\8eÒ\82©"))
+                pline("%s\82Í\8fÁ\82¦\82½\81I", oldname);
             (void) usmellmon(mdat);
-        else
+        } else { /* can see or sense it now */
+#if 0 /*JP:T*/
+            if (!strcmpi(oldname, "it")) /* couldn't see or sense it before */
+#else
+            if (!strcmpi(oldname, "\89½\8eÒ\82©")) /* couldn't see or sense it before */
+#endif
+/*JP
+                pline("%s appears!", upstart(newname));
+*/
+                pline("%s\82ª\8c»\82ê\82½\81I", upstart(newname));
+            else
 /*JP
-            pline("%s turns into %s!", oldname, newname);
+                pline("%s turns into %s!", oldname, newname);
 */
-            pline("%s\82Í%s\82É\82È\82Á\82½\81I", oldname, newname);
-        if (save_mname)
-            MNAME(mtmp) = save_mname;
+                pline("%s\82Í%s\82É\82È\82Á\82½\81I", oldname, newname);
+        }
     }
 
+    /* when polymorph trap/wand/potion produces a vampire, turn in into
+       a full-fledged vampshifter unless shape-changing is blocked */
+    if (mtmp->cham == NON_PM && mdat->mlet == S_VAMPIRE
+        && !Protection_from_shape_changers)
+        mtmp->cham = pm_to_cham(monsndx(mdat));
+
     possibly_unwield(mtmp, polyspot); /* might lose use of weapon */
     mon_break_armor(mtmp, polyspot);
     if (!(mtmp->misc_worn_check & W_ARMG))
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         mselftouch(mtmp, "No longer petrify-resistant, ",
                    !context.mon_moving);
 #else
@@ -3702,7 +4407,7 @@ boolean silent;
     if (ct) {
         if (!silent) { /* do we want pline msgs? */
             if (slct)
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                 pline_The("guard%s wake%s up!", slct > 1 ? "s" : "",
                           slct == 1 ? "s" : "");
 #else
@@ -3710,14 +4415,14 @@ boolean silent;
 #endif
             if (nct || sct) {
                 if (nct)
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                     pline_The("guard%s get%s angry!", nct == 1 ? "" : "s",
                               nct == 1 ? "s" : "");
 #else
                     pline("\94Ô\95º\82Í\93{\82Á\82½\81I");
 #endif
                 else if (!Blind)
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                     You_see("%sangry guard%s approaching!",
                             sct == 1 ? "an " : "", sct > 1 ? "s" : "");
 #else
@@ -3754,14 +4459,14 @@ short otyp;
 {
     short ap = mtmp->mappearance;
 
-    switch (mtmp->m_ap_type) {
+    switch (M_AP_TYPE(mtmp)) {
     case M_AP_NOTHING:
     case M_AP_FURNITURE:
     case M_AP_MONSTER:
         break;
     case M_AP_OBJECT:
         if (otyp == SPE_HEALING || otyp == SPE_EXTRA_HEALING) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
             pline("%s seems a more vivid %s than before.",
                   The(simple_typename(ap)),
                   c_obj_colors[objects[ap].oc_color]);
@@ -3790,14 +4495,20 @@ struct permonst *mdat;
         switch (mndx) {
         case PM_ROTHE:
         case PM_MINOTAUR:
+/*JP
             You("notice a bovine smell.");
+*/
+            You("\8b\8d\82Ì\82æ\82¤\82È\82É\82¨\82¢\82É\8bC\95t\82¢\82½\81D");
             msg_given = TRUE;
             break;
         case PM_CAVEMAN:
         case PM_CAVEWOMAN:
         case PM_BARBARIAN:
         case PM_NEANDERTHAL:
+/*JP
             You("smell body odor.");
+*/
+            pline("\91Ì\8fL\82Ì\82æ\82¤\82È\82É\82¨\82¢\82ª\82µ\82½\81D");
             msg_given = TRUE;
             break;
         /*
@@ -3820,7 +4531,10 @@ struct permonst *mdat;
         case PM_WERERAT:
         case PM_WEREWOLF:
         case PM_OWLBEAR:
+/*JP
             You("detect an odor reminiscent of an animal's den.");
+*/
+            pline("\93®\95¨\82Ì\82Ë\82®\82ç\82ð\8ev\82¢\8fo\82·\82æ\82¤\82È\82É\82¨\82¢\82ª\82µ\82½\81D");
             msg_given = TRUE;
             break;
         /*
@@ -3828,16 +4542,25 @@ struct permonst *mdat;
             break;
         */
         case PM_STEAM_VORTEX:
+/*JP
             You("smell steam.");
+*/
+            pline("\8fö\8bC\82Ì\82É\82¨\82¢\82ª\82µ\82½\81D");
             msg_given = TRUE;
             break;
         case PM_GREEN_SLIME:
+/*JP
             pline("%s stinks.", Something);
+*/
+            pline("\88«\8fL\82ª\82µ\82½\81D");
             msg_given = TRUE;
             break;
         case PM_VIOLET_FUNGUS:
         case PM_SHRIEKER:
+/*JP
             You("smell mushrooms.");
+*/
+            pline("\82«\82Ì\82±\82Ì\82É\82¨\82¢\82ª\82µ\82½\81D");
             msg_given = TRUE;
             break;
         /* These are here to avoid triggering the
@@ -3855,35 +4578,61 @@ struct permonst *mdat;
         if (nonspecific)
             switch (mdat->mlet) {
             case S_DOG:
+/*JP
                 You("notice a dog smell.");
+*/
+                You("\8c¢\82Ì\82É\82¨\82¢\82É\8bC\95t\82¢\82½\81D");
                 msg_given = TRUE;
                 break;
             case S_DRAGON:
+/*JP
                 You("smell a dragon!");
+*/
+                pline("\83h\83\89\83S\83\93\82Ì\82É\82¨\82¢\82ª\82·\82é\81I");
                 msg_given = TRUE;
                 break;
             case S_FUNGUS:
+/*JP
                 pline("%s smells moldy.", Something);
+*/
+                pline("\89½\82©\91Û\82Ì\82æ\82¤\82È\82É\82¨\82¢\82ª\82·\82é\81D");
                 msg_given = TRUE;
                 break;
             case S_UNICORN:
+#if 0 /*JP:T*/
                 You("detect a%s odor reminiscent of a stable.",
                     (mndx == PM_PONY) ? "n" : " strong");
+#else
+                pline("\94n\8f¬\89®\82ð\8ev\82¢\8fo\82·\82æ\82¤\82È%s\82É\82¨\82¢\82ª\82µ\82½\81D",
+                    (mndx == PM_PONY) ? "" : "\8b­\82¢");
+#endif
                 msg_given = TRUE;
                 break;
             case S_ZOMBIE:
+/*JP
                 You("smell rotting flesh.");
+*/
+                pline("\95\85\82Á\82½\93÷\82Ì\82É\82¨\82¢\82ª\82µ\82½\81D");
                 msg_given = TRUE;
                 break;
             case S_EEL:
+/*JP
                 You("smell fish.");
+*/
+                pline("\8b\9b\82Ì\82É\82¨\82¢\82ª\82µ\82½\81D");
                 msg_given = TRUE;
                 break;
             case S_ORC:
                 if (maybe_polyd(is_orc(youmonst.data), Race_if(PM_ORC)))
+/*JP
                     You("notice an attractive smell.");
+*/
+                    You("\96£\97Í\93I\82È\82É\82¨\82¢\82É\8bC\95t\82¢\82½\81D");
                 else
+/*JP
                     pline("A foul stench makes you feel a little nauseated.");
+*/
+                    pline("\82Þ\82©\82Â\82­\82æ\82¤\82È\88«\8fL\82Å\8f­\82µ\8bC\95ª\82ª\88«\82­\82È\82Á\82½\81D");
                 msg_given = TRUE;
                 break;
             default: