OSDN Git Service

update year to 2020
[jnethack/source.git] / src / artifact.c
index db6e4cc..561f08b 100644 (file)
@@ -1,10 +1,11 @@
-/* NetHack 3.6 artifact.c      $NHDT-Date: 1446369462 2015/11/01 09:17:42 $  $NHDT-Branch: master $:$NHDT-Revision: 1.96 $ */
+/* NetHack 3.6 artifact.c      $NHDT-Date: 1553363416 2019/03/23 17:50:16 $  $NHDT-Branch: NetHack-3.6.2-beta01 $:$NHDT-Revision: 1.129 $ */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/*-Copyright (c) Robert Patrick Rankin, 2013. */
 /* 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. */
 
 #include "hack.h"
@@ -26,16 +27,17 @@ extern boolean notonhead; /* for long worms */
 #define get_artifact(o) \
     (((o) && (o)->oartifact) ? &artilist[(int) (o)->oartifact] : 0)
 
-STATIC_DCL boolean
-FDECL(bane_applies, (const struct artifact *, struct monst *));
+STATIC_DCL boolean FDECL(bane_applies, (const struct artifact *,
+                                        struct monst *));
 STATIC_DCL int FDECL(spec_applies, (const struct artifact *, struct monst *));
 STATIC_DCL int FDECL(arti_invoke, (struct obj *));
-STATIC_DCL boolean
-FDECL(Mb_hit, (struct monst * magr, struct monst *mdef, struct obj *, int *,
-               int, BOOLEAN_P, char *));
+STATIC_DCL boolean FDECL(Mb_hit, (struct monst * magr, struct monst *mdef,
+                                struct obj *, int *, int, BOOLEAN_P, char *));
 STATIC_DCL unsigned long FDECL(abil_to_spfx, (long *));
 STATIC_DCL uchar FDECL(abil_to_adtyp, (long *));
+STATIC_DCL int FDECL(glow_strength, (int));
 STATIC_DCL boolean FDECL(untouchable, (struct obj *, BOOLEAN_P));
+STATIC_DCL int FDECL(count_surround_traps, (int, int));
 
 /* The amount added to the victim's total hit points to insure that the
    victim will be killed even after damage bonus/penalty adjustments.
@@ -470,12 +472,13 @@ boolean being_worn;
  */
 void
 set_artifact_intrinsic(otmp, on, wp_mask)
-register struct obj *otmp;
+struct obj *otmp;
 boolean on;
 long wp_mask;
 {
     long *mask = 0;
-    register const struct artifact *oart = get_artifact(otmp);
+    register const struct artifact *art, *oart = get_artifact(otmp);
+    register struct obj *obj;
     register uchar dtyp;
     register long spfx;
 
@@ -497,19 +500,21 @@ long wp_mask;
         mask = &EDisint_resistance;
     else if (dtyp == AD_DRST)
         mask = &EPoison_resistance;
+    else if (dtyp == AD_DRLI)
+        mask = &EDrain_resistance;
 
     if (mask && wp_mask == W_ART && !on) {
-        /* find out if some other artifact also confers this intrinsic */
-        /* if so, leave the mask alone */
-        register struct obj *obj;
-        for (obj = invent; obj; obj = obj->nobj)
+        /* find out if some other artifact also confers this intrinsic;
+           if so, leave the mask alone */
+        for (obj = invent; obj; obj = obj->nobj) {
             if (obj != otmp && obj->oartifact) {
-                register const struct artifact *art = get_artifact(obj);
-                if (art->cary.adtyp == dtyp) {
+                art = get_artifact(obj);
+                if (art && art->cary.adtyp == dtyp) {
                     mask = (long *) 0;
                     break;
                 }
             }
+        }
     }
     if (mask) {
         if (on)
@@ -522,11 +527,11 @@ long wp_mask;
     spfx = (wp_mask != W_ART) ? oart->spfx : oart->cspfx;
     if (spfx && wp_mask == W_ART && !on) {
         /* don't change any spfx also conferred by other artifacts */
-        register struct obj *obj;
         for (obj = invent; obj; obj = obj->nobj)
             if (obj != otmp && obj->oartifact) {
-                register const struct artifact *art = get_artifact(obj);
-                spfx &= ~art->cspfx;
+                art = get_artifact(obj);
+                if (art)
+                    spfx &= ~art->cspfx;
             }
     }
 
@@ -660,16 +665,17 @@ struct monst *mon;
         return 1;
 
     yours = (mon == &youmonst);
-    /* all quest artifacts are self-willed; it this ever changes, `badclass'
+    /* all quest artifacts are self-willed; if this ever changes, `badclass'
        will have to be extended to explicitly include quest artifacts */
     self_willed = ((oart->spfx & SPFX_INTEL) != 0);
     if (yours) {
         badclass = self_willed
                    && ((oart->role != NON_PM && !Role_if(oart->role))
                        || (oart->race != NON_PM && !Race_if(oart->race)));
-        badalign =
-            (oart->spfx & SPFX_RESTR) && oart->alignment != A_NONE
-            && (oart->alignment != u.ualign.type || u.ualign.record < 0);
+        badalign = ((oart->spfx & SPFX_RESTR) != 0
+                    && oart->alignment != A_NONE
+                    && (oart->alignment != u.ualign.type
+                        || u.ualign.record < 0));
     } else if (!is_covetous(mon->data) && !is_mplayer(mon->data)) {
         badclass = self_willed && oart->role != NON_PM
                    && oart != &artilist[ART_EXCALIBUR];
@@ -959,8 +965,18 @@ winid tmpwin; /* supplied by dodiscover() */
  * stun attack.  As of 3.4.1, those effects can occur but
  * will be slightly less likely than they were in 3.3.x.]
  */
+
+enum mb_effect_indices {
+    MB_INDEX_PROBE = 0,
+    MB_INDEX_STUN,
+    MB_INDEX_SCARE,
+    MB_INDEX_CANCEL,
+
+    NUM_MB_INDICES
+};
+
 #define MB_MAX_DIEROLL 8 /* rolls above this aren't magical */
-static const char *const mb_verb[2][4] = {
+static const char *const mb_verb[2][NUM_MB_INDICES] = {
 #if 0 /*JP*/
     { "probe", "stun", "scare", "cancel" },
     { "prod", "amaze", "tickle", "purge" },
@@ -970,10 +986,6 @@ static const char *const mb_verb[2][4] = {
     { "\97ã\82Ü\82µ", "\8bÁ\82©\82¹", "\82­\82·\82®\82Á", "\90´\82ß" },
 #endif
 };
-#define MB_INDEX_PROBE 0
-#define MB_INDEX_STUN 1
-#define MB_INDEX_SCARE 2
-#define MB_INDEX_CANCEL 3
 
 /* called when someone is being hit by Magicbane */
 STATIC_OVL boolean
@@ -986,10 +998,14 @@ boolean vis;               /* whether the action can be seen */
 char *hittee;              /* target's name: "you" or mon_nam(mdef) */
 {
     struct permonst *old_uasmon;
-    const char *verb, *fakename;
+    const char *verb;
     boolean youattack = (magr == &youmonst), youdefend = (mdef == &youmonst),
             resisted = FALSE, do_stun, do_confuse, result;
+#if 0 /*JP*/
+    int attack_indx, fakeidx, scare_dieroll = MB_MAX_DIEROLL / 2;
+#else
     int attack_indx, scare_dieroll = MB_MAX_DIEROLL / 2;
+#endif
 
     result = FALSE; /* no message given yet */
     /* the most severe effects are less likely at higher enchantment */
@@ -1061,26 +1077,26 @@ char *hittee;              /* target's name: "you" or mon_nam(mdef) */
                 if (youmonst.data != old_uasmon)
                     *dmgptr = 0; /* rehumanized, so no more damage */
                 if (u.uenmax > 0) {
+                    u.uenmax--;
+                    if (u.uen > 0)
+                        u.uen--;
+                    context.botl = TRUE;
 /*JP
                     You("lose magical energy!");
 */
                     You("\96\82\96@\82Ì\83G\83l\83\8b\83M\81[\82ð\8e¸\82Á\82½\81I");
-                    u.uenmax--;
-                    if (u.uen > 0)
-                        u.uen--;
-                    context.botl = 1;
                 }
             } else {
                 if (mdef->data == &mons[PM_CLAY_GOLEM])
                     mdef->mhp = 1; /* cancelled clay golems will die */
                 if (youattack && attacktype(mdef->data, AT_MAGC)) {
+                    u.uenmax++;
+                    u.uen++;
+                    context.botl = TRUE;
 /*JP
                     You("absorb magical energy!");
 */
                     You("\96\82\96@\82Ì\83G\83l\83\8b\83M\81[\82ð\8bz\82¢\82Æ\82Á\82½\81I");
-                    u.uenmax++;
-                    u.uen++;
-                    context.botl = 1;
                 }
             }
         }
@@ -1149,16 +1165,16 @@ char *hittee;              /* target's name: "you" or mon_nam(mdef) */
             mdef->mconf = 1;
     }
 
-#if 0 /*JP*//*\93ú\96{\8cê\82Å\82Í\95s\97v*/
-    /* now give message(s) describing side-effects;
-       don't let vtense() be fooled by assigned name ending in 's' */
-    fakename = youdefend ? "you" : "mon";
+    /* now give message(s) describing side-effects; Use fakename
+       so vtense() won't be fooled by assigned name ending in 's' */
+#if 0 /*JP*/
+    fakeidx = youdefend ? 1 : 0;
 #endif
     if (youattack || youdefend || vis) {
         (void) upstart(hittee); /* capitalize */
         if (resisted) {
 /*JP
-            pline("%s %s!", hittee, vtense(fakename, "resist"));
+            pline("%s %s!", hittee, vtense(fakename[fakeidx], "resist"));
 */
             pline("%s\82Í\96h\82¢\82¾\81I", hittee);
             shieldeff(youdefend ? u.ux : mdef->mx,
@@ -1168,14 +1184,14 @@ char *hittee;              /* target's name: "you" or mon_nam(mdef) */
             char buf[BUFSZ];
 
             buf[0] = '\0';
-#if 0 /*JP:T*/
+#if 0 /*JP*/
             if (do_stun)
                 Strcat(buf, "stunned");
             if (do_stun && do_confuse)
                 Strcat(buf, " and ");
             if (do_confuse)
                 Strcat(buf, "confused");
-            pline("%s %s %s%c", hittee, vtense(fakename, "are"), buf,
+            pline("%s %s %s%c", hittee, vtense(fakename[fakeidx], "are"), buf,
                   (do_stun && do_confuse) ? '!' : '.');
 #else
             if (do_stun && do_confuse)
@@ -1293,6 +1309,8 @@ int dieroll; /* needed for Magicbane and vorpal blades */
             pline("\8b\90\91å\82È\83n\83\93\83}\81[\82Í%s\82É\96½\92\86\82µ\82½%s", hittee,
                       !spec_dbon_applies ? "\81D" : "\81I\93d\8c\82\82ª\8fP\82Á\82½\81I");
 #endif
+        if (spec_dbon_applies)
+            wake_nearto(mdef->mx, mdef->my, 4 * 4);
         if (!rn2(5))
             (void) destroy_mitem(mdef, RING_CLASS, AD_ELEC);
         if (!rn2(5))
@@ -1426,11 +1444,13 @@ int dieroll; /* needed for Magicbane and vorpal blades */
                     return (boolean) (youattack || vis);
                 }
                 if (noncorporeal(mdef->data) || amorphous(mdef->data)) {
-/*JP
+#if 0 /*JP*/
                     pline("%s slices through %s %s.", wepdesc,
-*/
-                    pline("%s\82Í%s\82Ì%s\82ð\90Ø\82è\97\8e\82µ\82½\81D", wepdesc,
                           s_suffix(mon_nam(mdef)), mbodypart(mdef, NECK));
+#else
+                    pline("%s\82Í%s\82Ì%s\82ð\90Ø\82è\97\8e\82µ\82½\81D", wepdesc,
+                          mon_nam(mdef), mbodypart(mdef, NECK));
+#endif
                     return TRUE;
                 }
                 *dmgptr = 2 * mdef->mhp + FATAL_DAMAGE_MODIFIER;
@@ -1645,22 +1665,23 @@ struct obj *obj;
                 make_slimed(0L, (char *) 0);
             if (Blinded > creamed)
                 make_blinded(creamed, FALSE);
-            context.botl = 1;
+            context.botl = TRUE;
             break;
         }
         case ENERGY_BOOST: {
             int epboost = (u.uenmax + 1 - u.uen) / 2;
+
             if (epboost > 120)
                 epboost = 120; /* arbitrary */
             else if (epboost < 12)
                 epboost = u.uenmax - u.uen;
             if (epboost) {
+                u.uen += epboost;
+                context.botl = TRUE;
 /*JP
                 You_feel("re-energized.");
 */
                 You("\83G\83l\83\8b\83M\81[\82Å\96\9e\82½\82³\82ê\82½\81D");
-                u.uen += epboost;
-                context.botl = 1;
             } else
                 goto nothing_special;
             break;
@@ -1680,8 +1701,8 @@ struct obj *obj;
                 obj->age = 0;
                 return 0;
             }
-            b_effect =
-                obj->blessed && (Role_switch == oart->role || !oart->role);
+            b_effect = (obj->blessed && (oart->role == Role_switch
+                                         || oart->role == NON_PM));
             recharge(otmp, b_effect ? 1 : obj->cursed ? -1 : 0);
             update_inventory();
             break;
@@ -1782,14 +1803,14 @@ struct obj *obj;
             } else
                 otmp->quan += rnd(5);
             otmp->owt = weight(otmp);
-            otmp =
 #if 0 /*JP*/
-                hold_another_object(otmp, "Suddenly %s out.",
-                                    aobjnam(otmp, "fall"), (const char *) 0);
+            otmp = hold_another_object(otmp, "Suddenly %s out.",
+                                       aobjnam(otmp, "fall"), (char *) 0);
 #else
-                hold_another_object(otmp, "\93Ë\91R%s\82ª\97\8e\82¿\82½\81D",
-                                    xname(otmp), 0);
+            otmp = hold_another_object(otmp, "\93Ë\91R%s\82ª\97\8e\82¿\82½\81D",
+                                       xname(otmp), 0);
 #endif
+            nhUse(otmp);
             break;
         }
         }
@@ -1817,7 +1838,7 @@ struct obj *obj;
         }
 
         if ((eprop & ~W_ARTI) || iprop) {
       nothing_special:
+ nothing_special:
             /* you had the property from some other source too */
             if (carried(obj))
 /*JP
@@ -1970,6 +1991,7 @@ long *abil;
         { &EAntimagic, AD_MAGM },
         { &EDisint_resistance, AD_DISN },
         { &EPoison_resistance, AD_DRST },
+        { &EDrain_resistance, AD_DRLI },
     };
     int k;
 
@@ -2024,7 +2046,6 @@ long *abil;
     long wornmask = (W_ARM | W_ARMC | W_ARMH | W_ARMS
                      | W_ARMG | W_ARMF | W_ARMU
                      | W_AMUL | W_RINGL | W_RINGR | W_TOOL
-                     /* [do W_ART and W_ARTI actually belong here?] */
                      | W_ART | W_ARTI);
 
     if (u.twoweap)
@@ -2040,7 +2061,9 @@ long *abil;
 
             if (art) {
                 if (dtyp) {
-                    if (art->cary.adtyp == dtyp || art->defn.adtyp == dtyp)
+                    if (art->cary.adtyp == dtyp /* carried */
+                        || (art->defn.adtyp == dtyp /* defends while worn */
+                            && (obj->owornmask & ~(W_ART | W_ARTI))))
                         return obj;
                 }
                 if (spfx) {
@@ -2060,16 +2083,95 @@ long *abil;
     return (struct obj *) 0;
 }
 
+#if 1 /*JP*/
+/*JP colornames\82Í\90Ý\92è\83t\83@\83C\83\8b\82Å\8eg\82¤\82Ì\82Å\96|\96ó\82¹\82¸\81A
+     \93ú\96{\8cê\90ê\97p\82Ì\94z\97ñ\82ð\95Ê\82É\97p\88Ó\82·\82é\81B
+*/
+static const struct {
+    const char *name;
+    const int color;
+} colornames2[] = {
+    { "\8d\95\82¢", CLR_BLACK },
+    { "\90Ô\82¢", CLR_RED },
+    { "\97Î\90F\82Ì", CLR_GREEN },
+    { "\92\83\90F\82¢", CLR_BROWN },
+    { "\90Â\82¢", CLR_BLUE },
+    { "\83}\83[\83\93\83^\90F\82Ì", CLR_MAGENTA },
+    { "\83V\83A\83\93\90F\82Ì", CLR_CYAN },
+    { "\8aD\90F\82Ì", CLR_GRAY },
+    { "\8aD\90F\82Ì", CLR_GRAY },
+    { "\83I\83\8c\83\93\83W\90F\82Ì", CLR_ORANGE },
+    { "\92W\97Î\90F\82Ì", CLR_BRIGHT_GREEN },
+    { "\89©\90F\82¢", CLR_YELLOW },
+    { "\92W\90Â\90F\82Ì", CLR_BRIGHT_BLUE },
+    { "\96¾\82é\82¢\83}\83[\83\93\83^\90F\82Ì", CLR_BRIGHT_MAGENTA },
+    { "\96¾\82é\82¢\83V\83A\83\93\90F\82Ì", CLR_BRIGHT_CYAN },
+    { "\94\92\82¢", CLR_WHITE }
+};
+
+static const char *
+clr2colorname2(clr)
+int clr;
+{
+    int i;
+
+    for (i = 0; i < SIZE(colornames2); i++)
+        if (colornames2[i].color == clr)
+            return colornames2[i].name;
+    return (char *) 0;
+}
+#endif
+
 const char *
 glow_color(arti_indx)
 int arti_indx;
 {
     int colornum = artilist[arti_indx].acolor;
+#if 0 /*JP*/
     const char *colorstr = clr2colorname(colornum);
+#else
+    const char *colorstr = jconj_adj(clr2colorname2(colornum));
+#endif
 
     return hcolor(colorstr);
 }
 
+/* glow verb; [0] holds the value used when blind */
+static const char *glow_verbs[] = {
+/*JP
+    "quiver", "flicker", "glimmer", "gleam"
+*/
+    "\90k\82¦\82é", "\82Ü\82½\82½\82­", "\8cõ\82é", "\8bP\82­"
+};
+
+/* relative strength that Sting is glowing (0..3), to select verb */
+STATIC_OVL int
+glow_strength(count)
+int count;
+{
+    /* glow strength should also be proportional to proximity and
+       probably difficulty, but we don't have that information and
+       gathering it is more trouble than this would be worth */
+    return (count > 12) ? 3 : (count > 4) ? 2 : (count > 0);
+}
+
+const char *
+glow_verb(count, ingsfx)
+int count; /* 0 means blind rather than no applicable creatures */
+boolean ingsfx;
+{
+    static char resbuf[20];
+
+    Strcpy(resbuf, glow_verbs[glow_strength(count)]);
+    /* ing_suffix() will double the last consonant for all the words
+       we're using and none of them should have that, so bypass it */
+#if 0 /*JP*//*\93ú\96{\8cê\82Å\82Í\8eg\82í\82È\82¢*/
+    if (ingsfx)
+        Strcat(resbuf, "ing");
+#endif
+    return resbuf;
+}
+
 /* use for warning "glow" for Sting, Orcrist, and Grimtooth */
 void
 Sting_effects(orc_count)
@@ -2079,39 +2181,48 @@ int orc_count; /* new count (warn_obj_cnt is old count); -1 is a flag value */
         && (uwep->oartifact == ART_STING
             || uwep->oartifact == ART_ORCRIST
             || uwep->oartifact == ART_GRIMTOOTH)) {
+        int oldstr = glow_strength(warn_obj_cnt),
+            newstr = glow_strength(orc_count);
+
         if (orc_count == -1 && warn_obj_cnt > 0) {
             /* -1 means that blindness has just been toggled; give a
                'continue' message that eventual 'stop' message will match */
 #if 0 /*JP*/
             pline("%s is %s.", bare_artifactname(uwep),
-                  !Blind ? "glowing" : "quivering");
+                  glow_verb(Blind ? 0 : warn_obj_cnt, TRUE));
 #else
-            pline("%s\82Í%s\82Ă¢\82é\81D", bare_artifactname(uwep),
-                  !Blind ? "\8bP\82¢" : "\90k\82¦");
+            pline("%s\82Í%s\82¢\82é\81D", bare_artifactname(uwep),
+                  jconj(glow_verb(Blind ? 0 : warn_obj_cnt, TRUE), "\82Ä"));
 #endif
-        } else if (orc_count > 0 && warn_obj_cnt == 0) {
+        } else if (newstr > 0 && newstr != oldstr) {
             /* 'start' message */
             if (!Blind)
 #if 0 /*JP*/
-                pline("%s %s %s!", bare_artifactname(uwep),
-                      otense(uwep, "glow"), glow_color(uwep->oartifact));
+                pline("%s %s %s%c", bare_artifactname(uwep),
+                      otense(uwep, glow_verb(orc_count, FALSE)),
+                      glow_color(uwep->oartifact),
+                      (newstr > oldstr) ? '!' : '.');
 #else
-                pline("%s\82Í%s\8bP\82¢\82½\81I", bare_artifactname(uwep),
-                      glow_color(uwep->oartifact));
+                pline("%s\82Í%s%s%s", bare_artifactname(uwep),
+                      jconj_adj(glow_color(uwep->oartifact)),
+                      jconj(glow_verb(orc_count, FALSE), "\82½"),
+                      (newstr > oldstr) ? "\81I" : "\81D");
 #endif
-            else
-/*JP
-                pline("%s quivers slightly.", bare_artifactname(uwep));
-*/
+            else if (oldstr == 0) /* quivers */
+#if 0 /*JP*/
+                pline("%s %s slightly.", bare_artifactname(uwep),
+                      otense(uwep, glow_verb(0, FALSE)));
+#else
                 pline("%s\82Í\8f­\82µ\90k\82¦\82½\81D", bare_artifactname(uwep));
+#endif
         } else if (orc_count == 0 && warn_obj_cnt > 0) {
             /* 'stop' message */
 #if 0 /*JP*/
             pline("%s stops %s.", bare_artifactname(uwep),
-                  !Blind ? "glowing" : "quivering");
+                  glow_verb(Blind ? 0 : warn_obj_cnt, TRUE));
 #else
-            pline("%s\82Ì%s\82Í\8e~\82Ü\82Á\82½\81D", bare_artifactname(uwep),
-                  !Blind ? "\8bP\82«" : "\90k\82¦");
+            pline("%s\82Í%s\82Ì\82ð\82â\82ß\82½\81D", bare_artifactname(uwep),
+                  glow_verb(Blind ? 0 : warn_obj_cnt, TRUE));
 #endif
         }
     }
@@ -2130,8 +2241,7 @@ boolean loseit;    /* whether to drop it if hero can longer touch it */
     if (touch_artifact(obj, &youmonst)) {
         char buf[BUFSZ];
         int dmg = 0, tmp;
-        boolean ag =
-                    (objects[obj->otyp].oc_material == SILVER && Hate_silver),
+        boolean ag = (objects[obj->otyp].oc_material == SILVER && Hate_silver),
                 bane = bane_applies(get_artifact(obj), &youmonst);
 
         /* nothing else to do if hero can successfully handle this object */
@@ -2182,7 +2292,7 @@ boolean loseit;    /* whether to drop it if hero can longer touch it */
     if (loseit && obj) {
         if (Levitation) {
             freeinv(obj);
-            hitfloor(obj);
+            hitfloor(obj, TRUE);
         } else {
             /* dropx gives a message iff item lands on an altar */
             if (!IS_ALTAR(levl[u.ux][u.uy].typ))
@@ -2268,11 +2378,15 @@ int dropflag; /* 0==don't drop, 1==drop all, 2==drop weapon */
 
     dropit = (dropflag > 0); /* drop all or drop weapon */
     /* check secondary weapon first, before possibly unwielding primary */
-    if (u.twoweap)
+    if (u.twoweap) {
+        bypass_obj(uswapwep); /* so loop below won't process it again */
         (void) untouchable(uswapwep, dropit);
+    }
     /* check primary weapon next so that they're handled together */
-    if (uwep)
+    if (uwep) {
+        bypass_obj(uwep); /* so loop below won't process it again */
         (void) untouchable(uwep, dropit);
+    }
 
     /* in case someone is daft enough to add artifact or silver saddle */
     if (u.usteed && (obj = which_armor(u.usteed, W_SADDLE)) != 0) {
@@ -2310,4 +2424,115 @@ int dropflag; /* 0==don't drop, 1==drop all, 2==drop weapon */
         clear_bypasses(); /* reset upon final exit */
 }
 
+static int mkot_trap_warn_count = 0;
+
+STATIC_OVL int
+count_surround_traps(x, y)
+int x, y;
+{
+    struct rm *levp;
+    struct obj *otmp;
+    struct trap *ttmp;
+    int dx, dy, glyph, ret = 0;
+
+    for (dx = x - 1; dx < x + 2; ++dx)
+        for (dy = y - 1; dy < y + 2; ++dy) {
+            if (!isok(dx, dy))
+                continue;
+            /* If a trap is shown here, don't count it; the hero
+             * should be expecting it.  But if there is a trap here
+             * that's not shown, either undiscovered or covered by
+             * something, do count it.
+             */
+            glyph = glyph_at(dx, dy);
+            if (glyph_is_trap(glyph))
+                continue;
+            if ((ttmp = t_at(dx, dy)) != 0) {
+                ++ret;
+                continue;
+            }
+            levp = &levl[dx][dy];
+            if (IS_DOOR(levp->typ) && (levp->doormask & D_TRAPPED) != 0) {
+                ++ret;
+                continue;
+            }
+            for (otmp = level.objects[dx][dy]; otmp; otmp = otmp->nexthere)
+                if (Is_container(otmp) && otmp->otrapped) {
+                    ++ret; /* we're counting locations, so just */
+                    break; /* count the first one in a pile     */
+                }
+        }
+    /*
+     * [Shouldn't we also check inventory for a trapped container?
+     * Even if its trap has already been found, there's no 'tknown'
+     * flag to help hero remember that so we have nothing comparable
+     * to a shown glyph to justify skipping it.]
+     */
+    return ret;
+}
+
+/* sense adjacent traps if wielding MKoT without wearing gloves */
+void
+mkot_trap_warn()
+{
+#if 0 /*JP*/
+    static const char *const heat[7] = {
+        "cool", "slightly warm", "warm", "very warm",
+        "hot", "very hot", "like fire"
+    };
+#else
+    static const char *const heat[7] = {
+        "\97â\82½\82­", "\8f­\82µ\89·\82©\82­", "\89·\82©\82­", "\82Æ\82Ä\82à\89·\82©\82­",
+        "\94M\82­", "\82Æ\82Ä\82à\94M\82­", "\89\8a\82Ì\82æ\82¤\82É"
+    };
+#endif
+
+    if (!uarmg && uwep && uwep->oartifact == ART_MASTER_KEY_OF_THIEVERY) {
+        int idx, ntraps = count_surround_traps(u.ux, u.uy);
+
+        if (ntraps != mkot_trap_warn_count) {
+            idx = min(ntraps, SIZE(heat) - 1);
+/*JP
+            pline_The("Key feels %s%c", heat[idx], (ntraps > 3) ? '!' : '.');
+*/
+            pline_The("\8c®\82Í%s\8a´\82\82½%s", heat[idx], (ntraps > 3) ? "\81I" : "\81D");
+        }
+        mkot_trap_warn_count = ntraps;
+    } else
+        mkot_trap_warn_count = 0;
+}
+
+/* Master Key is magic key if its bless/curse state meets our criteria:
+   not cursed for rogues or blessed for non-rogues */
+boolean
+is_magic_key(mon, obj)
+struct monst *mon; /* if null, non-rogue is assumed */
+struct obj *obj;
+{
+    if (((obj && obj->oartifact == ART_MASTER_KEY_OF_THIEVERY)
+         && ((mon == &youmonst) ? Role_if(PM_ROGUE)
+                                : (mon && mon->data == &mons[PM_ROGUE])))
+        ? !obj->cursed : obj->blessed)
+        return TRUE;
+    return FALSE;
+}
+
+/* figure out whether 'mon' (usually youmonst) is carrying the magic key */
+struct obj *
+has_magic_key(mon)
+struct monst *mon; /* if null, hero assumed */
+{
+    struct obj *o;
+    short key = artilist[ART_MASTER_KEY_OF_THIEVERY].otyp;
+
+    if (!mon)
+        mon = &youmonst;
+    for (o = ((mon == &youmonst) ? invent : mon->minvent); o;
+         o = nxtobj(o, key, FALSE)) {
+        if (is_magic_key(mon, o))
+            return o;
+    }
+    return (struct obj *) 0;
+}
+
 /*artifact.c*/