OSDN Git Service

update year to 2022
[jnethack/source.git] / src / potion.c
index a3d7399..d2f8a00 100644 (file)
@@ -1,10 +1,11 @@
-/* NetHack 3.6 potion.c        $NHDT-Date: 1446861768 2015/11/07 02:02:48 $  $NHDT-Branch: master $:$NHDT-Revision: 1.121 $ */
+/* NetHack 3.6 potion.c        $NHDT-Date: 1573848199 2019/11/15 20:03:19 $  $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.167 $ */
 /* 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-2022            */
 /* JNetHack may be freely redistributed.  See license for details. */
 
 #include "hack.h"
@@ -96,7 +97,7 @@ boolean talk;
 
     if (!xtime && old) {
         if (talk)
-#if 0 /*JP*/
+#if 0 /*JP:T*/
             You_feel("%s now.",
                      Hallucination ? "less wobbly" : "a bit steadier");
 #else
@@ -115,7 +116,7 @@ boolean talk;
 /*JP
                 You("%s...", stagger(youmonst.data, "stagger"));
 */
-                You("%s\81D\81D\81D", stagger(youmonst.data, "\82­\82ç\82­\82ç\82µ\82½"));
+                You("\82­\82ç\82­\82ç\82µ\82½\81D\81D\81D");
         }
     }
     if ((!xtime && old) || (xtime && !old))
@@ -124,6 +125,9 @@ boolean talk;
     set_itimeout(&HStun, xtime);
 }
 
+/* Sick is overloaded with both fatal illness and food poisoning (via
+   u.usick_type bit mask), but delayed killer can only support one or
+   the other at a time.  They should become separate intrinsics.... */
 void
 make_sick(xtime, cause, talk, type)
 long xtime;
@@ -131,9 +135,10 @@ const char *cause; /* sickness cause */
 boolean talk;
 int type;
 {
+    struct kinfo *kptr;
     long old = Sick;
 
-#if 0
+#if 0   /* tell player even if hero is unconscious */
     if (Unaware)
         talk = FALSE;
 #endif
@@ -178,11 +183,20 @@ int type;
         context.botl = TRUE;
     }
 
+    kptr = find_delayed_killer(SICK);
     if (Sick) {
         exercise(A_CON, FALSE);
-        delayed_killer(SICK, KILLED_BY_AN, cause);
+        /* setting delayed_killer used to be unconditional, but that's
+           not right when make_sick(0) is called to cure food poisoning
+           if hero was also fatally ill; this is only approximate */
+        if (xtime || !old || !kptr) {
+            int kpfx = ((cause && !strcmp(cause, "#wizintrinsic"))
+                        ? KILLED_BY : KILLED_BY_AN);
+
+            delayed_killer(SICK, kpfx, cause);
+        }
     } else
-        dealloc_killer(find_delayed_killer(SICK));
+        dealloc_killer(kptr);
 }
 
 void
@@ -192,18 +206,25 @@ const char *msg;
 {
     long old = Slimed;
 
-#if 0
+#if 0   /* tell player even if hero is unconscious */
     if (Unaware)
         msg = 0;
 #endif
-    if ((!xtime && old) || (xtime && !old)) {
+    set_itimeout(&Slimed, xtime);
+    if ((xtime != 0L) ^ (old != 0L)) {
+        context.botl = TRUE;
         if (msg)
-            pline1(msg);
-        context.botl = 1;
+            pline("%s", msg);
     }
-    set_itimeout(&Slimed, xtime);
-    if (!Slimed)
+    if (!Slimed) {
         dealloc_killer(find_delayed_killer(SLIMED));
+        /* fake appearance is set late in turn-to-slime countdown */
+        if (U_AP_TYPE == M_AP_MONSTER
+            && youmonst.mappearance == PM_GREEN_SLIME) {
+            youmonst.m_ap_type = M_AP_NOTHING;
+            youmonst.mappearance = 0;
+        }
+    }
 }
 
 /* start or stop petrification */
@@ -216,16 +237,16 @@ const char *killername;
 {
     long old = Stoned;
 
-#if 0
+#if 0   /* tell player even if hero is unconscious */
     if (Unaware)
         msg = 0;
 #endif
-    if ((!xtime && old) || (xtime && !old)) {
+    set_itimeout(&Stoned, xtime);
+    if ((xtime != 0L) ^ (old != 0L)) {
+        context.botl = TRUE;
         if (msg)
-            pline1(msg);
-        /* context.botl = 1;   --- Stoned is not a status line item */
+            pline("%s", msg);
     }
-    set_itimeout(&Stoned, xtime);
     if (!Stoned)
         dealloc_killer(find_delayed_killer(STONED));
     else if (!old)
@@ -242,14 +263,14 @@ boolean talk;
     if (Unaware)
         talk = FALSE;
 
+    set_itimeout(&Vomiting, xtime);
+    context.botl = TRUE;
     if (!xtime && old)
         if (talk)
 /*JP
             You_feel("much less nauseated now.");
 */
             You("\93f\82«\8bC\82ª\82¨\82³\82Ü\82Á\82½\81D");
-
-    set_itimeout(&Vomiting, xtime);
 }
 
 /*JP
@@ -268,7 +289,9 @@ boolean talk;
 {
     long old = Blinded;
     boolean u_could_see, can_see_now;
+#if 0 /*JP*/
     const char *eyes;
+#endif
 
     /* we need to probe ahead in case the Eyes of the Overworld
        are or will be overriding blindness */
@@ -358,32 +381,43 @@ boolean talk;
     set_itimeout(&Blinded, xtime);
 
     if (u_could_see ^ can_see_now) { /* one or the other but not both */
-        context.botl = 1;
-        vision_full_recalc = 1; /* blindness just got toggled */
-        /* this vision recalculation used to be deferred until
-           moveloop(), but that made it possible for vision
-           irregularities to occur (cited case was force bolt
-           hitting adjacent potion of blindness and then a
-           secret door; hero was blinded by vapors but then
-           got the message "a door appears in the wall") */
-        vision_recalc(0);
-        if (Blind_telepat || Infravision)
-            see_monsters();
-
-        /* avoid either of the sequences
-           "Sting starts glowing", [become blind], "Sting stops quivering" or
-           "Sting starts quivering", [regain sight], "Sting stops glowing"
-           by giving "Sting is quivering" when becoming blind or
-           "Sting is glowing" when regaining sight so that the eventual
-           "stops" message matches */
-        if (warn_obj_cnt && uwep && (EWarn_of_mon & W_WEP) != 0L)
-            Sting_effects(-1);
-        /* update dknown flag for inventory picked up while blind */
-        if (can_see_now)
-            learn_unseen_invent();
+        toggle_blindness();
     }
 }
 
+/* blindness has just started or just ended--caller enforces that;
+   called by Blindf_on(), Blindf_off(), and make_blinded() */
+void
+toggle_blindness()
+{
+    boolean Stinging = (uwep && (EWarn_of_mon & W_WEP) != 0L);
+
+    /* blindness has just been toggled */
+    context.botl = TRUE; /* status conditions need update */
+    vision_full_recalc = 1; /* vision has changed */
+    /* this vision recalculation used to be deferred until moveloop(),
+       but that made it possible for vision irregularities to occur
+       (cited case was force bolt hitting an adjacent potion of blindness
+       and then a secret door; hero was blinded by vapors but then got the
+       message "a door appears in the wall" because wall spot was IN_SIGHT) */
+    vision_recalc(0);
+    if (Blind_telepat || Infravision || Stinging)
+        see_monsters(); /* also counts EWarn_of_mon monsters */
+    /*
+     * Avoid either of the sequences
+     * "Sting starts glowing", [become blind], "Sting stops quivering" or
+     * "Sting starts quivering", [regain sight], "Sting stops glowing"
+     * by giving "Sting is quivering" when becoming blind or
+     * "Sting is glowing" when regaining sight so that the eventual
+     * "stops" message matches the most recent "Sting is ..." one.
+     */
+    if (Stinging)
+        Sting_effects(-1);
+    /* update dknown flag for inventory picked up while blind */
+    if (!Blind)
+        learn_unseen_invent();
+}
+
 boolean
 make_hallucinated(xtime, talk, mask)
 long xtime; /* nonzero if this is an attempt to turn on hallucination */
@@ -397,7 +431,7 @@ long mask; /* nonzero if resistance status should change by mask */
     if (Unaware)
         talk = FALSE;
 
-#if 0 /*JP*/
+#if 0 /*JP:T*/
     message = (!xtime) ? "Everything %s SO boring now."
                        : "Oh wow!  Everything %s so cosmic!";
 #else
@@ -427,7 +461,7 @@ long mask; /* nonzero if resistance status should change by mask */
             if (!haseyes(youmonst.data)) {
                 strange_feeling((struct obj *) 0, (char *) 0);
             } else if (Blind) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                 const char *eyes = body_part(EYE);
 
                 if (eyecount(youmonst.data) != 1)
@@ -464,7 +498,7 @@ long mask; /* nonzero if resistance status should change by mask */
         (eg. Qt windowport's equipped items display) */
         update_inventory();
 
-        context.botl = 1;
+        context.botl = TRUE;
         if (talk)
             pline(message, verb);
     }
@@ -477,32 +511,47 @@ long xtime;
 boolean talk;
 {
     long old = HDeaf;
-    boolean toggled = FALSE;
 
     if (Unaware)
         talk = FALSE;
 
-    if (!xtime && old) {
-        if (talk)
-/*JP
-            You("can hear again.");
-*/
-            You("\82Ü\82½\95·\82±\82¦\82é\82æ\82¤\82É\82È\82Á\82½\81D");
-        toggled = TRUE;
-    } else if (xtime && !old) {
+    set_itimeout(&HDeaf, xtime);
+    if ((xtime != 0L) ^ (old != 0L)) {
+        context.botl = TRUE;
         if (talk)
 /*JP
-            You("are unable to hear anything.");
+            You(old ? "can hear again." : "are unable to hear anything.");
 */
-            You("\89½\82à\95·\82±\82¦\82È\82­\82È\82Á\82½\81D");
-        toggled = TRUE;
+            You(old ? "\82Ü\82½\95·\82±\82¦\82é\82æ\82¤\82É\82È\82Á\82½\81D" : "\89½\82à\95·\82±\82¦\82È\82­\82È\82Á\82½\81D");
     }
-    /* deafness isn't presently shown on status line, but
-       request a status update in case that changes someday */
-    if (toggled)
-        context.botl = TRUE;
+}
 
-    set_itimeout(&HDeaf, xtime);
+/* set or clear "slippery fingers" */
+void
+make_glib(xtime)
+int xtime;
+{
+    set_itimeout(&Glib, xtime);
+    /* may change "(being worn)" to "(being worn; slippery)" or vice versa */
+    if (uarmg)
+        update_inventory();
+}
+
+void
+self_invis_message()
+{
+#if 0 /*JP:T*/
+    pline("%s %s.",
+          Hallucination ? "Far out, man!  You"
+                        : "Gee!  All of a sudden, you",
+          See_invisible ? "can see right through yourself"
+                        : "can't see yourself");
+#else
+    pline("%s\82 \82È\82½\82Í%s\81D",
+          Hallucination ? "\83\8f\81[\83I\81I" : "\82°\81I\93Ë\91R",
+          See_invisible ? "\8e©\95ª\8e©\90g\82ª\82¿\82á\82ñ\82Æ\8c©\82¦\82È\82­\82È\82Á\82½"
+                        : "\8e©\95ª\8e©\90g\82ª\8c©\82¦\82È\82­\82È\82Á\82½");
+#endif
 }
 
 STATIC_OVL void
@@ -524,7 +573,7 @@ ghost_from_bottle()
         pline("\95r\82ð\8aJ\82¯\82é\82Æ\81C\89½\82©\82ª\8fo\82Ä\82«\82½\81D");
         return;
     }
-#if 0 /*JP*/
+#if 0 /*JP:T*/
     pline("As you open the bottle, an enormous %s emerges!",
           Hallucination ? rndmonnam(NULL) : (const char *) "ghost");
 #else
@@ -657,7 +706,7 @@ register struct obj *otmp;
 
     if (nothing) {
         unkn++;
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         You("have a %s feeling for a moment, then it passes.",
             Hallucination ? "normal" : "peculiar");
 #else
@@ -694,7 +743,7 @@ register struct obj *otmp;
             break;
         } else {
             /* unlike unicorn horn, overrides Fixed_abil */
-#if 0 /*JP*/
+#if 0 /*JP:T*/
             pline("Wow!  This makes you feel %s!",
                   (otmp->blessed)
                       ? (unfixable_trouble_count(FALSE) ? "better" : "great")
@@ -708,8 +757,8 @@ register struct obj *otmp;
             i = rn2(A_MAX); /* start at a random point */
             for (ii = 0; ii < A_MAX; ii++) {
                 lim = AMAX(i);
-                if (i == A_STR && u.uhs >= 3)
-                    --lim; /* WEAK */
+                /* this used to adjust 'lim' for A_STR when u.uhs was
+                   WEAK or worse, but that's handled via ATEMP(A_STR) now */
                 if (ABASE(i) < lim) {
                     ABASE(i) = lim;
                     context.botl = 1;
@@ -720,21 +769,32 @@ register struct obj *otmp;
                 if (++i >= A_MAX)
                     i = 0;
             }
+
+            /* when using the potion (not the spell) also restore lost levels,
+               to make the potion more worth keeping around for players with
+               the spell or with a unihorn; this is better than full healing
+               in that it can restore all of them, not just half, and a
+               blessed potion restores them all at once */
+            if (otmp->otyp == POT_RESTORE_ABILITY && u.ulevel < u.ulevelmax) {
+                do {
+                    pluslvl(FALSE);
+                } while (u.ulevel < u.ulevelmax && otmp->blessed);
+            }
         }
         break;
     case POT_HALLUCINATION:
         if (Hallucination || Halluc_resistance)
             nothing++;
-        (void) make_hallucinated(
-            itimeout_incr(HHallucination, rn1(200, 600 - 300 * bcsign(otmp))),
-            TRUE, 0L);
+        (void) make_hallucinated(itimeout_incr(HHallucination,
+                                          rn1(200, 600 - 300 * bcsign(otmp))),
+                                 TRUE, 0L);
         break;
     case POT_WATER:
         if (!otmp->blessed && !otmp->cursed) {
 /*JP
-            pline("This tastes like water.");
+            pline("This tastes like %s.", hliquid("water"));
 */
-            pline("\90\85\82Ì\82æ\82¤\82È\96¡\82ª\82·\82é\81D");
+            pline("%s\82Ì\82æ\82¤\82È\96¡\82ª\82·\82é\81D", hliquid("\90\85"));
             u.uhunger += rnd(10);
             newuhs(FALSE);
             break;
@@ -744,9 +804,9 @@ register struct obj *otmp;
             || u.ualign.type == A_CHAOTIC) {
             if (otmp->blessed) {
 /*JP
-                pline("This burns like acid!");
+                pline("This burns like %s!", hliquid("acid"));
 */
-                pline("\8e_\82Ì\82æ\82¤\82É\90ã\82ª\82Ð\82è\82Ð\82è\82·\82é\81I");
+                pline("%s\82Ì\82æ\82¤\82É\90ã\82ª\82Ð\82è\82Ð\82è\82·\82é\81I", hliquid("\8e_"));
                 exercise(A_CON, FALSE);
                 if (u.ulycn >= LOW_PM) {
 /*JP
@@ -756,7 +816,7 @@ register struct obj *otmp;
                          makeplural(mons[u.ulycn].mname));
                     if (youmonst.data == &mons[u.ulycn])
                         you_unwere(FALSE);
-                    u.ulycn = NON_PM; /* cure lycanthropy */
+                    set_ulycn(NON_PM); /* cure lycanthropy */
                 }
 /*JP
                 losehp(Maybe_Half_Phys(d(2, 6)), "potion of holy water",
@@ -788,9 +848,9 @@ register struct obj *otmp;
             } else {
                 if (u.ualign.type == A_LAWFUL) {
 /*JP
-                    pline("This burns like acid!");
+                    pline("This burns like %s!", hliquid("acid"));
 */
-                    pline("\8e_\82Ì\82æ\82¤\82É\90ã\82ª\82Ð\82è\82Ð\82è\82·\82é\81I");
+                    pline("%s\82Ì\82æ\82¤\82É\90ã\82ª\82Ð\82è\82Ð\82è\82·\82é\81I", hliquid("\8e_"));
 /*JP
                     losehp(Maybe_Half_Phys(d(2, 6)), "potion of unholy water",
 */
@@ -809,14 +869,14 @@ register struct obj *otmp;
         break;
     case POT_BOOZE:
         unkn++;
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         pline("Ooph!  This tastes like %s%s!",
               otmp->odiluted ? "watered down " : "",
               Hallucination ? "dandelion wine" : "liquid fire");
 #else
         pline("\82¤\82¥\82Á\82Õ\81I\82±\82ê\82Í%s%s\82Ì\82æ\82¤\82È\96¡\82ª\82·\82é\81I",
               otmp->odiluted ? "\90\85\82Å\94\96\82ß\82½" : "",
-              Hallucination ? "\83^\83\93\83|\83|\83\8f\83C\83\93" : "\94R\97¿\83I\83C\83\8b");
+              Hallucination ? "\82½\82ñ\82Û\82Û\82Ì\82¨\8eð" : "\94R\97¿\83I\83C\83\8b");
 #endif
         if (!otmp->blessed)
             make_confused(itimeout_incr(HConfusion, d(3, 8)), FALSE);
@@ -899,7 +959,7 @@ register struct obj *otmp;
 
         unkn++;
         if (otmp->cursed)
-#if 0 /*JP*/
+#if 0 /*JP:T*/
             pline("Yecch!  This tastes %s.",
                   Hallucination ? "overripe" : "rotten");
 #else
@@ -907,7 +967,7 @@ register struct obj *otmp;
                   Hallucination ? "\8fn\82µ\82·\82¬\82½" : "\95\85\82Á\82½");
 #endif
         else
-#if 0 /*JP*/
+#if 0 /*JP:T*/
             pline(
                 Hallucination
                     ? "This tastes like 10%% real %s%s all-natural beverage."
@@ -965,7 +1025,7 @@ register struct obj *otmp;
 */
                 You("\82»\82Ì\8fê\82Å\93®\82¯\82È\82­\82È\82Á\82½\81I");
             else
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                 Your("%s are frozen to the %s!", makeplural(body_part(FOOT)),
                      surface(u.ux, u.uy));
 #else
@@ -1069,7 +1129,7 @@ register struct obj *otmp;
                 char contaminant[BUFSZ];
                 int typ = rn2(A_MAX);
 
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                 Sprintf(contaminant, "%s%s",
                         (Poison_resistance) ? "mildly " : "",
                         (otmp->fromsink) ? "contaminated tap water"
@@ -1151,9 +1211,9 @@ register struct obj *otmp;
         }
         break;
     case POT_SPEED:
+        /* skip when mounted; heal_legs() would heal steed's legs */
         if (Wounded_legs && !otmp->cursed && !u.usteed) {
-            /* heal_legs() would heal steeds legs */
-            heal_legs();
+            heal_legs(0);
             unkn++;
             break;
         }
@@ -1263,47 +1323,67 @@ register struct obj *otmp;
         break;
     case POT_LEVITATION:
     case SPE_LEVITATION:
-        if (otmp->cursed)
-            HLevitation &= ~I_SPECIAL;
+        /*
+         * BLevitation will be set if levitation is blocked due to being
+         * inside rock (currently or formerly in phazing xorn form, perhaps)
+         * but it doesn't prevent setting or incrementing Levitation timeout
+         * (which will take effect after escaping from the rock if it hasn't
+         * expired by then).
+         */
         if (!Levitation && !BLevitation) {
             /* kludge to ensure proper operation of float_up() */
             set_itimeout(&HLevitation, 1L);
             float_up();
-            /* reverse kludge */
-            set_itimeout(&HLevitation, 0L);
-            if (otmp->cursed) {
-                if ((u.ux == xupstair && u.uy == yupstair)
-                    || (sstairs.up && u.ux == sstairs.sx
-                        && u.uy == sstairs.sy)
-                    || (xupladder && u.ux == xupladder
-                        && u.uy == yupladder)) {
-                    (void) doup();
-                } else if (has_ceiling(&u.uz)) {
-                    int dmg = uarmh ? 1 : rnd(10);
+            /* This used to set timeout back to 0, then increment it below
+               for blessed and uncursed effects.  But now we leave it so
+               that cursed effect yields "you float down" on next turn.
+               Blessed and uncursed get one extra turn duration. */
+        } else /* already levitating, or can't levitate */
+            nothing++;
 
-#if 0 /*JP*/
-                    You("hit your %s on the %s.", body_part(HEAD),
-                        ceiling(u.ux, u.uy));
+        if (otmp->cursed) {
+            /* 'already levitating' used to block the cursed effect(s)
+               aside from ~I_SPECIAL; it was not clear whether that was
+               intentional; either way, it no longer does (as of 3.6.1) */
+            HLevitation &= ~I_SPECIAL; /* can't descend upon demand */
+            if (BLevitation) {
+                ; /* rising via levitation is blocked */
+            } else if ((u.ux == xupstair && u.uy == yupstair)
+                    || (sstairs.up && u.ux == sstairs.sx && u.uy == sstairs.sy)
+                    || (xupladder && u.ux == xupladder && u.uy == yupladder)) {
+                (void) doup();
+                /* in case we're already Levitating, which would have
+                   resulted in incrementing 'nothing' */
+                nothing = 0; /* not nothing after all */
+            } else if (has_ceiling(&u.uz)) {
+                int dmg = rnd(!uarmh ? 10 : !is_metallic(uarmh) ? 6 : 3);
+
+#if 0 /*JP:T*/
+                You("hit your %s on the %s.", body_part(HEAD),
+                    ceiling(u.ux, u.uy));
 #else
-                    You("%s\82ð%s\82É\82Ô\82Â\82¯\82½\81D", body_part(HEAD),
-                        ceiling(u.ux,u.uy));
+                You("%s\82ð%s\82É\82Ô\82Â\82¯\82½\81D", body_part(HEAD),
+                    ceiling(u.ux,u.uy));
 #endif
 /*JP
-                    losehp(Maybe_Half_Phys(dmg), "colliding with the ceiling",
+                losehp(Maybe_Half_Phys(dmg), "colliding with the ceiling",
 */
-                    losehp(Maybe_Half_Phys(dmg), "\93V\88ä\82É\93ª\82ð\82Ô\82Â\82¯\82Ä",
-                           KILLED_BY);
-                }
-            } /*cursed*/
-        } else
-            nothing++;
-        if (otmp->blessed) {
+                losehp(Maybe_Half_Phys(dmg), "\93V\88ä\82É\93ª\82ð\82Ô\82Â\82¯\82Ä",
+                       KILLED_BY);
+                nothing = 0; /* not nothing after all */
+            }
+        } else if (otmp->blessed) {
+            /* at this point, timeout is already at least 1 */
             incr_itimeout(&HLevitation, rn1(50, 250));
+            /* can descend at will (stop levitating via '>') provided timeout
+               is the only factor (ie, not also wearing Lev ring or boots) */
             HLevitation |= I_SPECIAL;
-        } else
+        } else /* timeout is already at least 1 */
             incr_itimeout(&HLevitation, rn1(140, 10));
-        if (Levitation)
-            spoteffects(FALSE); /* for sinks */
+
+        if (Levitation && IS_SINK(levl[u.ux][u.uy].typ))
+            spoteffects(FALSE);
+        /* levitating blocks flying */
         float_vs_flight();
         break;
     case POT_GAIN_ENERGY: { /* M. Stephenson */
@@ -1389,7 +1469,7 @@ register struct obj *otmp;
         } else {
             int dmg;
 
-#if 0 /*JP*/
+#if 0 /*JP:T*/
             pline("This burns%s!",
                   otmp->blessed ? " a little" : otmp->cursed ? " a lot"
                                                              : " like acid");
@@ -1458,8 +1538,14 @@ register boolean curesick, cureblind;
                 u.uhp = (u.uhpmax += nxtra);
         }
     }
-    if (cureblind)
+    if (cureblind) {
+        /* 3.6.1: it's debatible whether healing magic should clean off
+           mundane 'dirt', but if it doesn't, blindness isn't cured */
+        u.ucreamed = 0;
         make_blinded(0L, TRUE);
+        /* heal deafness too */
+        make_deaf(0L, TRUE);
+    }
     if (curesick) {
         make_vomiting(0L, TRUE);
         make_sick(0L, (char *) 0, TRUE, SICK_ALL);
@@ -1474,7 +1560,7 @@ struct obj *obj;
 const char *txt;
 {
     if (flags.beginner || !txt)
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         You("have a %s feeling for a moment, then it passes.",
             Hallucination ? "normal" : "strange");
 #else
@@ -1494,7 +1580,7 @@ const char *txt;
     useup(obj);
 }
 
-#if 0 /*JP*/
+#if 0 /*JP:T*/
 const char *bottlenames[] = { "bottle", "phial", "flagon", "carafe",
                               "flask",  "jar",   "vial" };
 #else
@@ -1566,19 +1652,28 @@ const char *objphrase; /* "Your widget glows" or "Steed's saddle glows" */
             glowcolor = hcolor(glowcolor);
             /*JP:3.6.0\8e\9e\93_\82Å\82Í\93®\8e\8c\82Í"glow"\82¾\82¯\82È\82Ì\82Å\8c\88\82ß\8c\82\82¿*/
             if (altfmt)
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                 pline("%s with %s aura.", objphrase, an(glowcolor));
 #else
                 pline("%s\82Í%s\83I\81[\83\89\82É\82Â\82Â\82Ü\82ê\82½\81D", objphrase, glowcolor);
 #endif
             else
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                 pline("%s %s.", objphrase, glowcolor);
 #else
                 pline("%s\82Í%s\8bP\82¢\82½\81D", objphrase, jconj_adj(glowcolor));
 #endif
             iflags.last_msg = PLNMSG_OBJ_GLOWS;
             targobj->bknown = !Hallucination;
+        } else {
+            /* didn't see what happened:  forget the BUC state if that was
+               known unless the bless/curse state of the water is known;
+               without this, hero would know the new state even without
+               seeing the glow; priest[ess] will immediately relearn it */
+            if (!potion->bknown || !potion->dknown)
+                targobj->bknown = 0;
+            /* [should the bknown+dknown exception require that water
+               be discovered or at least named?] */
         }
         /* potions of water are the only shop goods whose price depends
            on their curse/bless state */
@@ -1599,32 +1694,39 @@ const char *objphrase; /* "Your widget glows" or "Steed's saddle glows" */
     return res;
 }
 
+/* potion obj hits monster mon, which might be youmonst; obj always used up */
 void
-potionhit(mon, obj, your_fault)
-register struct monst *mon;
-register struct obj *obj;
-boolean your_fault;
+potionhit(mon, obj, how)
+struct monst *mon;
+struct obj *obj;
+int how;
 {
     const char *botlnam = bottlename();
     boolean isyou = (mon == &youmonst);
-    int distance;
+    int distance, tx, ty;
     struct obj *saddle = (struct obj *) 0;
-    boolean hit_saddle = FALSE;
+    boolean hit_saddle = FALSE, your_fault = (how <= POTHIT_HERO_THROW);
 
     if (isyou) {
+        tx = u.ux, ty = u.uy;
         distance = 0;
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         pline_The("%s crashes on your %s and breaks into shards.", botlnam,
                   body_part(HEAD));
 #else
         pline("%s\82ª\82 \82È\82½\82Ì%s\82Ì\8fã\82Å\89ó\82ê\94j\95Ð\82Æ\82È\82Á\82½\81D", botlnam,
-              body_part(HEAD));
+                  body_part(HEAD));
 #endif
-/*JP
-        losehp(Maybe_Half_Phys(rnd(2)), "thrown potion", KILLED_BY_AN);
-*/
+#if 0 /*JP*/
+        losehp(Maybe_Half_Phys(rnd(2)),
+               (how == POTHIT_OTHER_THROW) ? "propelled potion" /* scatter */
+                                           : "thrown potion",
+               KILLED_BY_AN);
+#else /*\82Ç\82¿\82ç\82à\81u\93\8a\82°\82ç\82ê\82½\81v\82Å\82æ\82¢*/
         losehp(Maybe_Half_Phys(rnd(2)), "\93\8a\82°\82ç\82ê\82½\96ò\82Å", KILLED_BY_AN);
+#endif
     } else {
+        tx = mon->mx, ty = mon->my;
         /* sometimes it hits the saddle */
         if (((mon->misc_worn_check & W_SADDLE)
              && (saddle = which_armor(mon, W_SADDLE)))
@@ -1633,36 +1735,40 @@ boolean your_fault;
                     && ((rnl(10) > 7 && obj->cursed)
                         || (rnl(10) < 4 && obj->blessed) || !rn2(3)))))
             hit_saddle = TRUE;
-        distance = distu(mon->mx, mon->my);
-        if (!cansee(mon->mx, mon->my))
+        distance = distu(tx, ty);
+        if (!cansee(tx, ty)) {
 /*JP
             pline("Crash!");
 */
             pline("\83K\83V\83\83\83\93\81I");
-        else {
+        else {
             char *mnam = mon_nam(mon);
             char buf[BUFSZ];
 
             if (hit_saddle && saddle) {
-/*JP
+#if 0 /*JP:T*/
                 Sprintf(buf, "%s saddle",
-*/
-                Sprintf(buf, "%s\82Ì\88Æ",
                         s_suffix(x_monnam(mon, ARTICLE_THE, (char *) 0,
                                           (SUPPRESS_IT | SUPPRESS_SADDLE),
                                           FALSE)));
+#else
+                Sprintf(buf, "%s\82Ì\88Æ",
+                        x_monnam(mon, ARTICLE_THE, (char *) 0,
+                                          (SUPPRESS_IT | SUPPRESS_SADDLE),
+                                          FALSE));
+#endif
             } else if (has_head(mon->data)) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                 Sprintf(buf, "%s %s", s_suffix(mnam),
                         (notonhead ? "body" : "head"));
 #else
-                Sprintf(buf, "%s\82Ì%s", s_suffix(mnam),
+                Sprintf(buf, "%s\82Ì%s", mnam,
                         (notonhead ? "\91Ì" : "\93ª"));
 #endif
             } else {
                 Strcpy(buf, mnam);
             }
-#if 0 /*JP*/
+#if 0 /*JP:T*/
             pline_The("%s crashes on %s and breaks into shards.", botlnam,
                       buf);
 #else
@@ -1675,7 +1781,7 @@ boolean your_fault;
     }
 
     /* oil doesn't instantly evaporate; Neither does a saddle hit */
-    if (obj->otyp != POT_OIL && !hit_saddle && cansee(mon->mx, mon->my))
+    if (obj->otyp != POT_OIL && !hit_saddle && cansee(tx, ty))
 /*JP
         pline("%s.", Tobjnam(obj, "evaporate"));
 */
@@ -1698,7 +1804,8 @@ boolean your_fault;
         case POT_ACID:
             if (!Acid_resistance) {
                 int dmg;
-#if 0 /*JP*/
+
+#if 0 /*JP:T*/
                 pline("This burns%s!",
                       obj->blessed ? " a little"
                                    : obj->cursed ? " a lot" : "");
@@ -1718,7 +1825,7 @@ boolean your_fault;
     } else if (hit_saddle && saddle) {
         char *mnam, buf[BUFSZ], saddle_glows[BUFSZ];
         boolean affected = FALSE;
-        boolean useeit = !Blind && canseemon(mon) && cansee(mon->mx, mon->my);
+        boolean useeit = !Blind && canseemon(mon) && cansee(tx, ty);
 
         mnam = x_monnam(mon, ARTICLE_THE, (char *) 0,
                         (SUPPRESS_IT | SUPPRESS_SADDLE), FALSE);
@@ -1742,20 +1849,25 @@ boolean your_fault;
 */
             pline("%s\82Í\94G\82ê\82½\81D", buf);
     } else {
-        boolean angermon = TRUE;
+        boolean angermon = your_fault, cureblind = FALSE;
 
-        if (!your_fault)
-            angermon = FALSE;
         switch (obj->otyp) {
-        case POT_HEALING:
-        case POT_EXTRA_HEALING:
         case POT_FULL_HEALING:
+            cureblind = TRUE;
+            /*FALLTHRU*/
+        case POT_EXTRA_HEALING:
+            if (!obj->cursed)
+                cureblind = TRUE;
+            /*FALLTHRU*/
+        case POT_HEALING:
+            if (obj->blessed)
+                cureblind = TRUE;
             if (mon->data == &mons[PM_PESTILENCE])
                 goto do_illness;
-        /*FALLTHRU*/
+            /*FALLTHRU*/
         case POT_RESTORE_ABILITY:
         case POT_GAIN_ABILITY:
       do_healing:
+ do_healing:
             angermon = FALSE;
             if (mon->mhp < mon->mhpmax) {
                 mon->mhp = mon->mhpmax;
@@ -1765,6 +1877,8 @@ boolean your_fault;
 */
                     pline("%s\82Í\8c³\8bC\82É\82È\82Á\82½\82æ\82¤\82É\8c©\82¦\82é\81D", Monnam(mon));
             }
+            if (cureblind)
+                mcureblindness(mon, canseemon(mon));
             break;
         case POT_SICKNESS:
             if (mon->data == &mons[PM_PESTILENCE])
@@ -1775,14 +1889,14 @@ boolean your_fault;
                 /* most common case */
                 || resists_poison(mon)) {
                 if (canseemon(mon))
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                     pline("%s looks unharmed.", Monnam(mon));
 #else
                     pline("%s\82Í\82È\82ñ\82Æ\82à\82È\82¢\82æ\82¤\82¾\81D", Monnam(mon));
 #endif
                 break;
             }
       do_illness:
+ do_illness:
             if ((mon->mhpmax > 3) && !resist(mon, POTION_CLASS, 0, NOTELL))
                 mon->mhpmax /= 2;
             if ((mon->mhp > 2) && !resist(mon, POTION_CLASS, 0, NOTELL))
@@ -1800,10 +1914,15 @@ boolean your_fault;
             if (!resist(mon, POTION_CLASS, 0, NOTELL))
                 mon->mconf = TRUE;
             break;
-        case POT_INVISIBILITY:
+        case POT_INVISIBILITY: {
+            boolean sawit = canspotmon(mon);
+
             angermon = FALSE;
             mon_set_minvis(mon);
+            if (sawit && !canspotmon(mon) && cansee(mon->mx, mon->my))
+                map_invisible(mon->mx, mon->my);
             break;
+        }
         case POT_SLEEPING:
             /* wakeup() doesn't rouse victims of temporary sleep */
             if (sleep_monst(mon, rnd(12), POTION_CLASS)) {
@@ -1828,7 +1947,7 @@ boolean your_fault;
             break;
         case POT_BLINDNESS:
             if (haseyes(mon->data)) {
-                register int btmp = 64 + rn2(32)
+                int btmp = 64 + rn2(32)
                             + rn2(32) * !resist(mon, POTION_CLASS, 0, NOTELL);
 
                 btmp += mon->mblinded;
@@ -1840,7 +1959,7 @@ boolean your_fault;
             if (is_undead(mon->data) || is_demon(mon->data)
                 || is_were(mon->data) || is_vampshifter(mon)) {
                 if (obj->blessed) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                     pline("%s %s in pain!", Monnam(mon),
                           is_silent(mon->data) ? "writhes" : "shrieks");
 #else
@@ -1848,10 +1967,10 @@ boolean your_fault;
                           is_silent(mon->data) ? "\82É\90g\82à\82¾\82¦\82µ\82½" : "\82Ì\8b©\82Ñ\90º\82ð\82 \82°\82½");
 #endif
                     if (!is_silent(mon->data))
-                        wake_nearto(mon->mx, mon->my, mon->data->mlevel * 10);
+                        wake_nearto(tx, ty, mon->data->mlevel * 10);
                     mon->mhp -= d(2, 6);
                     /* should only be by you */
-                    if (mon->mhp < 1)
+                    if (DEADMONSTER(mon))
                         killed(mon);
                     else if (is_were(mon->data) && !is_human(mon->data))
                         new_were(mon); /* revert to human */
@@ -1880,17 +1999,17 @@ boolean your_fault;
                     pline("%s\82Í\8eK\82Ñ\82½\81D", Monnam(mon));
                 mon->mhp -= d(1, 6);
                 /* should only be by you */
-                if (mon->mhp < 1)
+                if (DEADMONSTER(mon))
                     killed(mon);
             }
             break;
         case POT_OIL:
             if (obj->lamplit)
-                explode_oil(obj, mon->mx, mon->my);
+                explode_oil(obj, tx, ty);
             break;
         case POT_ACID:
             if (!resists_acid(mon) && !resist(mon, POTION_CLASS, 0, NOTELL)) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                 pline("%s %s in pain!", Monnam(mon),
                       is_silent(mon->data) ? "writhes" : "shrieks");
 #else
@@ -1898,9 +2017,9 @@ boolean your_fault;
                       is_silent(mon->data) ? "\82É\90g\82à\82¾\82¦\82µ\82½" : "\82Ì\8b©\82Ñ\90º\82ð\82 \82°\82½");
 #endif
                 if (!is_silent(mon->data))
-                    wake_nearto(mon->mx, mon->my, mon->data->mlevel * 10);
+                    wake_nearto(tx, ty, mon->data->mlevel * 10);
                 mon->mhp -= d(obj->cursed ? 2 : 1, obj->blessed ? 4 : 8);
-                if (mon->mhp < 1) {
+                if (DEADMONSTER(mon)) {
                     if (your_fault)
                         killed(mon);
                     else
@@ -1920,27 +2039,36 @@ boolean your_fault;
             break;
         */
         }
-        if (angermon)
-            wakeup(mon);
-        else
-            mon->msleeping = 0;
+        /* target might have been killed */
+        if (!DEADMONSTER(mon)) {
+            if (angermon)
+                wakeup(mon, TRUE);
+            else
+                mon->msleeping = 0;
+        }
     }
 
     /* Note: potionbreathe() does its own docall() */
-    if ((distance == 0 || ((distance < 3) && rn2(5)))
+    if ((distance == 0 || (distance < 3 && rn2(5)))
         && (!breathless(youmonst.data) || haseyes(youmonst.data)))
         potionbreathe(obj);
     else if (obj->dknown && !objects[obj->otyp].oc_name_known
-             && !objects[obj->otyp].oc_uname && cansee(mon->mx, mon->my))
+             && !objects[obj->otyp].oc_uname && cansee(tx, ty))
         docall(obj);
+
     if (*u.ushops && obj->unpaid) {
         struct monst *shkp = shop_keeper(*in_rooms(u.ux, u.uy, SHOPBASE));
 
-        if (shkp)
+        /* neither of the first two cases should be able to happen;
+           only the hero should ever have an unpaid item, and only
+           when inside a tended shop */
+        if (!shkp) /* if shkp was killed, unpaid ought to cleared already */
+            obj->unpaid = 0;
+        else if (context.mon_moving) /* obj thrown by monster */
+            subfrombill(obj, shkp);
+        else /* obj thrown by hero */
             (void) stolen_value(obj, u.ux, u.uy, (boolean) shkp->mpeaceful,
                                 FALSE);
-        else
-            obj->unpaid = 0;
     }
     obfree(obj, (struct obj *) 0);
 }
@@ -1950,7 +2078,13 @@ void
 potionbreathe(obj)
 register struct obj *obj;
 {
-    register int i, ii, isdone, kn = 0;
+    int i, ii, isdone, kn = 0;
+    boolean cureblind = FALSE;
+
+    /* potion of unholy water might be wielded; prevent
+       you_were() -> drop_weapon() from dropping it so that it
+       remains in inventory where our caller expects it to be */
+    obj->in_use = 1;
 
     switch (obj->otyp) {
     case POT_RESTORE_ABILITY:
@@ -1962,7 +2096,7 @@ register struct obj *obj;
 */
                 pline("\83E\83Q\83F\81I\96ò\82Í\82à\82Ì\82·\82²\82¢\93õ\82¢\82ª\82·\82é\81I");
             else if (haseyes(youmonst.data)) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                 const char *eyes = body_part(EYE);
 
                 if (eyecount(youmonst.data) != 1)
@@ -1992,18 +2126,27 @@ register struct obj *obj;
             u.mh++, context.botl = 1;
         if (u.uhp < u.uhpmax)
             u.uhp++, context.botl = 1;
+        cureblind = TRUE;
         /*FALLTHRU*/
     case POT_EXTRA_HEALING:
         if (Upolyd && u.mh < u.mhmax)
             u.mh++, context.botl = 1;
         if (u.uhp < u.uhpmax)
             u.uhp++, context.botl = 1;
+        if (!obj->cursed)
+            cureblind = TRUE;
         /*FALLTHRU*/
     case POT_HEALING:
         if (Upolyd && u.mh < u.mhmax)
             u.mh++, context.botl = 1;
         if (u.uhp < u.uhpmax)
             u.uhp++, context.botl = 1;
+        if (obj->blessed)
+            cureblind = TRUE;
+        if (cureblind) {
+            make_blinded(0L, !u.ucreamed);
+            make_deaf(0L, TRUE);
+        }
         exercise(A_CON, TRUE);
         break;
     case POT_SICKNESS:
@@ -2041,7 +2184,7 @@ register struct obj *obj;
     case POT_INVISIBILITY:
         if (!Blind && !Invis) {
             kn++;
-#if 0 /*JP*/
+#if 0 /*JP:T*/
             pline("For an instant you %s!",
                   See_invisible ? "could see right through yourself"
                                 : "couldn't see yourself");
@@ -2139,7 +2282,7 @@ register struct obj *obj;
         break;
      */
     }
-    /* note: no obfree() */
+    /* note: no obfree() -- that's our caller's responsibility */
     if (obj->dknown) {
         if (kn)
             makeknown(obj->otyp);
@@ -2154,41 +2297,33 @@ STATIC_OVL short
 mixtype(o1, o2)
 register struct obj *o1, *o2;
 {
+    int o1typ = o1->otyp, o2typ = o2->otyp;
+
     /* cut down on the number of cases below */
     if (o1->oclass == POTION_CLASS
-        && (o2->otyp == POT_GAIN_LEVEL || o2->otyp == POT_GAIN_ENERGY
-            || o2->otyp == POT_HEALING || o2->otyp == POT_EXTRA_HEALING
-            || o2->otyp == POT_FULL_HEALING || o2->otyp == POT_ENLIGHTENMENT
-            || o2->otyp == POT_FRUIT_JUICE)) {
-        struct obj *swp;
-
-        swp = o1;
-        o1 = o2;
-        o2 = swp;
+        && (o2typ == POT_GAIN_LEVEL || o2typ == POT_GAIN_ENERGY
+            || o2typ == POT_HEALING || o2typ == POT_EXTRA_HEALING
+            || o2typ == POT_FULL_HEALING || o2typ == POT_ENLIGHTENMENT
+            || o2typ == POT_FRUIT_JUICE)) {
+        /* swap o1 and o2 */
+        o1typ = o2->otyp;
+        o2typ = o1->otyp;
     }
 
-    switch (o1->otyp) {
+    switch (o1typ) {
     case POT_HEALING:
-        switch (o2->otyp) {
-        case POT_SPEED:
-        case POT_GAIN_LEVEL:
-        case POT_GAIN_ENERGY:
+        if (o2typ == POT_SPEED)
             return POT_EXTRA_HEALING;
-        }
+        /*FALLTHRU*/
     case POT_EXTRA_HEALING:
-        switch (o2->otyp) {
-        case POT_GAIN_LEVEL:
-        case POT_GAIN_ENERGY:
-            return POT_FULL_HEALING;
-        }
     case POT_FULL_HEALING:
-        switch (o2->otyp) {
-        case POT_GAIN_LEVEL:
-        case POT_GAIN_ENERGY:
-            return POT_GAIN_ABILITY;
-        }
+        if (o2typ == POT_GAIN_LEVEL || o2typ == POT_GAIN_ENERGY)
+            return (o1typ == POT_HEALING) ? POT_EXTRA_HEALING
+                   : (o1typ == POT_EXTRA_HEALING) ? POT_FULL_HEALING
+                     : POT_GAIN_ABILITY;
+        /*FALLTHRU*/
     case UNICORN_HORN:
-        switch (o2->otyp) {
+        switch (o2typ) {
         case POT_SICKNESS:
             return POT_FRUIT_JUICE;
         case POT_HALLUCINATION:
@@ -2198,12 +2333,12 @@ register struct obj *o1, *o2;
         }
         break;
     case AMETHYST: /* "a-methyst" == "not intoxicated" */
-        if (o2->otyp == POT_BOOZE)
+        if (o2typ == POT_BOOZE)
             return POT_FRUIT_JUICE;
         break;
     case POT_GAIN_LEVEL:
     case POT_GAIN_ENERGY:
-        switch (o2->otyp) {
+        switch (o2typ) {
         case POT_CONFUSION:
             return (rn2(3) ? POT_BOOZE : POT_ENLIGHTENMENT);
         case POT_HEALING:
@@ -2219,9 +2354,10 @@ register struct obj *o1, *o2;
         }
         break;
     case POT_FRUIT_JUICE:
-        switch (o2->otyp) {
+        switch (o2typ) {
         case POT_SICKNESS:
             return POT_SICKNESS;
+        case POT_ENLIGHTENMENT:
         case POT_SPEED:
             return POT_BOOZE;
         case POT_GAIN_LEVEL:
@@ -2230,7 +2366,7 @@ register struct obj *o1, *o2;
         }
         break;
     case POT_ENLIGHTENMENT:
-        switch (o2->otyp) {
+        switch (o2typ) {
         case POT_LEVITATION:
             if (rn2(3))
                 return POT_GAIN_LEVEL;
@@ -2243,19 +2379,26 @@ register struct obj *o1, *o2;
         break;
     }
 
-    return 0;
+    return STRANGE_OBJECT;
 }
 
 /* #dip command */
 int
 dodip()
 {
+/*JP
+    static const char Dip_[] = "Dip ";
+*/
+    static const char Dip_[] = "\90Z\82·";
     register struct obj *potion, *obj;
     struct obj *singlepotion;
     uchar here;
     char allowall[2];
     short mixture;
-    char qbuf[QBUFSZ], qtoo[QBUFSZ];
+    char qbuf[QBUFSZ], obuf[QBUFSZ];
+#if 0 /*JP*/
+    const char *shortestname; /* last resort obj name for prompt */
+#endif
 
     allowall[0] = ALL_CLASSES;
     allowall[1] = '\0';
@@ -2268,33 +2411,47 @@ dodip()
         return 0;
 
 #if 0 /*JP*/
-    Sprintf(qbuf, "dip %s into", thesimpleoname(obj));
-#else /*JP:\89p\8cê\82Å\82Í\89½\82ð\90Z\82·\82©\82ð\8aÜ\82ß\82Ä\82¢\82é\82ª\93ú\96{\8cê\82Å\82Í\8f\88\97\9d\82Ì\93s\8d\87\82Å\82Æ\82è\82 \82¦\82¸\8fÈ\97ª*/
-    Sprintf(qbuf, "dip into");
+    shortestname = (is_plural(obj) || pair_of(obj)) ? "them" : "it";
 #endif
+    /*
+     * Bypass safe_qbuf() since it doesn't handle varying suffix without
+     * an awful lot of support work.  Format the object once, even though
+     * the fountain and pool prompts offer a lot more room for it.
+     * 3.6.0 used thesimpleoname() unconditionally, which posed no risk
+     * of buffer overflow but drew bug reports because it omits user-
+     * supplied type name.
+     * getobj: "What do you want to dip <the object> into? [xyz or ?*] "
+     */
+    Strcpy(obuf, short_oname(obj, doname, thesimpleoname,
+                             /* 128 - (24 + 54 + 1) leaves 49 for <object> */
+                             QBUFSZ - sizeof "What do you want to dip \
+ into? [abdeghjkmnpqstvwyzBCEFHIKLNOQRTUWXZ#-# or ?*] "));
+
     here = levl[u.ux][u.uy].typ;
     /* Is there a fountain to dip into here? */
     if (IS_FOUNTAIN(here)) {
-        /* "Dip <the object> into the fountain?" */
 #if 0 /*JP*/
-        Sprintf(qtoo, "%s the fountain?", qbuf);
+        Sprintf(qbuf, "%s%s into the fountain?", Dip_,
+                flags.verbose ? obuf : shortestname);
 #else
-        Sprintf(qtoo, "\90ò\82É%s\81H", qbuf);
+        Sprintf(qbuf, "\90ò\82É%s\81H", Dip_);
 #endif
-        if (yn(upstart(qtoo)) == 'y') {
+        /* "Dip <the object> into the fountain?" */
+        if (yn(qbuf) == 'y') {
             dipfountain(obj);
             return 1;
         }
     } else if (is_pool(u.ux, u.uy)) {
         const char *pooltype = waterbody_name(u.ux, u.uy);
 
-        /* "Dip <the object> into the {pool, moat, &c}?" */
 #if 0 /*JP*/
-        Sprintf(qtoo, "%s the %s?", qbuf, pooltype);
-#else
-        Sprintf(qtoo, "%s\82É%s\81H", pooltype, qbuf);
+        Sprintf(qbuf, "%s%s into the %s?", Dip_,
+                flags.verbose ? obuf : shortestname, pooltype);
+#else /*JP:\89p\8cê\82Å\82Í\89½\82ð\90Z\82·\82©\82ð\8aÜ\82ß\82Ä\82¢\82é\82ª\93ú\96{\8cê\82Å\82Í\8f\88\97\9d\82Ì\93s\8d\87\82Å\82Æ\82è\82 \82¦\82¸\8fÈ\97ª*/
+        Sprintf(qbuf, "%s\82É%s\81H", pooltype, Dip_);
 #endif
-        if (yn(upstart(qtoo)) == 'y') {
+        /* "Dip <the object> into the {pool, moat, &c}?" */
+        if (yn(qbuf) == 'y') {
             if (Levitation) {
                 floating_above(pooltype);
             } else if (u.usteed && !is_swimmer(u.usteed->data)
@@ -2310,8 +2467,14 @@ dodip()
         }
     }
 
-    /* "What do you want to dip <the object> into?" */
-    potion = getobj(beverages, qbuf); /* "dip into" */
+#if 0 /*JP*/
+    /* "What do you want to dip <the object> into? [xyz or ?*] " */
+    Sprintf(qbuf, "dip %s into", flags.verbose ? obuf : shortestname);
+#else
+    /* "What do you want to dip into? [xyz or ?*] " */
+    Sprintf(qbuf, "dip into");
+#endif
+    potion = getobj(beverages, qbuf);
     if (!potion)
         return 0;
     if (potion == obj && potion->quan == 1L) {
@@ -2341,26 +2504,22 @@ dodip()
                            5, 95)) {
             pline1(nothing_happens);
         } else {
-            boolean was_wep, was_swapwep, was_quiver;
             short save_otyp = obj->otyp;
 
             /* KMH, conduct */
             u.uconduct.polypiles++;
 
-            was_wep = (obj == uwep);
-            was_swapwep = (obj == uswapwep);
-            was_quiver = (obj == uquiver);
-
             obj = poly_obj(obj, STRANGE_OBJECT);
 
-            if (was_wep)
-                setuwep(obj);
-            else if (was_swapwep)
-                setuswapwep(obj);
-            else if (was_quiver)
-                setuqwep(obj);
-
-            if (obj->otyp != save_otyp) {
+            /*
+             * obj might be gone:
+             *  poly_obj() -> set_wear() -> Amulet_on() -> useup()
+             * if obj->otyp is worn amulet and becomes AMULET_OF_CHANGE.
+             */
+            if (!obj) {
+                makeknown(POT_POLYMORPH);
+                return 1;
+            } else if (obj->otyp != save_otyp) {
                 makeknown(POT_POLYMORPH);
                 useup(potion);
                 prinv((char *) 0, obj, 0L);
@@ -2376,28 +2535,33 @@ dodip()
         potion->in_use = FALSE; /* didn't go poof */
         return 1;
     } else if (obj->oclass == POTION_CLASS && obj->otyp != potion->otyp) {
-        long amt = obj->quan;
+        int amt = (int) obj->quan;
+        boolean magic;
+
+        mixture = mixtype(obj, potion);
 
+        magic = (mixture != STRANGE_OBJECT) ? objects[mixture].oc_magic
+            : (objects[obj->otyp].oc_magic || objects[potion->otyp].oc_magic);
 #if 0 /*JP*/
-        Strcpy(qbuf, "The");
+        Strcpy(qbuf, "The"); /* assume full stack */
 #else
         Strcpy(qbuf, "");
 #endif
-        if (amt > (objects[potion->otyp].oc_magic ? 2L : 9L)) {
+        if (amt > (magic ? 3 : 7)) {
             /* trying to dip multiple potions will usually affect only a
-               subset; pick an amount between 2 and min(N,9), inclusive */
-            amt -= 1L;
-            do {
-                amt = (long) rnd((int) amt);
-            } while (amt >= 9L);
-            amt += 1L;
-            if (amt < obj->quan) {
-                obj = splitobj(obj, amt);
-#if 0 /*JP*/
+               subset; pick an amount between 3 and 8, inclusive, for magic
+               potion result, between 7 and N for non-magic */
+            if (magic)
+                amt = rnd(min(amt, 8) - (3 - 1)) + (3 - 1); /* 1..6 + 2 */
+            else
+                amt = rnd(amt - (7 - 1)) + (7 - 1); /* 1..(N-6) + 6 */
+
+            if ((long) amt < obj->quan) {
+                obj = splitobj(obj, (long) amt);
+/*JP
                 Sprintf(qbuf, "%ld of the", obj->quan);
-#else
+*/
                 Sprintf(qbuf, "%ld\96{\82Ì", obj->quan);
-#endif
             }
         }
         /* [N of] the {obj(s)} mix(es) with [one of] {the potion}... */
@@ -2410,6 +2574,8 @@ dodip()
               thesimpleoname(potion),
               (potion->quan > 1L) ? "\82Ì\88ê\82Â" : "");
 #endif
+        /* get rid of 'dippee' before potential perm_invent updates */
+        useup(potion); /* now gone */
         /* Mixing potions is dangerous...
            KMH, balance patch -- acid is particularly unstable */
         if (obj->cursed || obj->otyp == POT_ACID || !rn2(10)) {
@@ -2419,16 +2585,15 @@ dodip()
                to 'amt' because that's not implemented] */
             obj->in_use = 1;
 /*JP
-            pline("BOOM!  They explode!");
+            pline("%sThey explode!", !Deaf ? "BOOM!  " : "");
 */
-            pline("\83o\81[\83\93\81I\94\9a\94­\82µ\82½\81I");
+            pline("%s\94\9a\94­\82µ\82½\81I", !Deaf ? "\83o\81[\83\93\81I" : "");
             wake_nearto(u.ux, u.uy, (BOLT_LIM + 1) * (BOLT_LIM + 1));
             exercise(A_STR, FALSE);
             if (!breathless(youmonst.data) || haseyes(youmonst.data))
                 potionbreathe(obj);
             useupall(obj);
-            useup(potion);
-            losehp((int) (amt + rnd(9)), /* not physical damage */
+            losehp(amt + rnd(9), /* not physical damage */
 /*JP
                    "alchemic blast", KILLED_BY_AN);
 */
@@ -2440,9 +2605,11 @@ dodip()
         if (Blind || Hallucination)
             obj->dknown = 0;
 
-        if ((mixture = mixtype(obj, potion)) != 0) {
+        if (mixture != STRANGE_OBJECT) {
             obj->otyp = mixture;
         } else {
+            struct obj *otmp;
+
             switch (obj->odiluted ? 1 : rnd(8)) {
             case 1:
                 obj->otyp = POT_WATER;
@@ -2451,21 +2618,20 @@ dodip()
             case 3:
                 obj->otyp = POT_SICKNESS;
                 break;
-            case 4: {
-                struct obj *otmp = mkobj(POTION_CLASS, FALSE);
-
+            case 4:
+                otmp = mkobj(POTION_CLASS, FALSE);
                 obj->otyp = otmp->otyp;
                 obfree(otmp, (struct obj *) 0);
                 break;
-            }
             default:
                 useupall(obj);
-                useup(potion);
-                if (!Blind)
-/*JP
-                    pline_The("mixture glows brightly and evaporates.");
-*/
-                    pline("\8d¬\82º\82é\82Æ\96ò\82Í\96¾\82é\82­\8bP\82«\81C\8fö\94­\82µ\82½\81D");
+#if 0 /*JP:T*/
+                pline_The("mixture %sevaporates.",
+                          !Blind ? "glows brightly and " : "");
+#else
+                pline_The("\8d¬\82º\82é\82Æ\96ò\82Í%s\8fö\94­\82µ\82½\81D",
+                          !Blind ? "\96¾\82é\82­\8bP\82«\81C" : "");
+#endif
                 return 1;
             }
         }
@@ -2484,7 +2650,6 @@ dodip()
                       hcolor(OBJ_DESCR(objects[obj->otyp])));
         }
 
-        useup(potion);
         /* this is required when 'obj' was split off from a bigger stack,
            so that 'obj' will now be assigned its own inventory slot;
            it has a side-effect of merging 'obj' into another compatible
@@ -2492,7 +2657,7 @@ dodip()
            been made in order to get the merge result for both cases;
            as a consequence, mixing while Fumbling drops the mixture */
         freeinv(obj);
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         (void) hold_another_object(obj, "You drop %s!", doname(obj),
                                    (const char *) 0);
 #else
@@ -2503,22 +2668,29 @@ dodip()
     }
 
     if (potion->otyp == POT_ACID && obj->otyp == CORPSE
-        && obj->corpsenm == PM_LICHEN && !Blind) {
-#if 0 /*JP*/
+        && obj->corpsenm == PM_LICHEN) {
+#if 0 /*JP:T*/
         pline("%s %s %s around the edges.", The(cxname(obj)),
-              otense(obj, "turn"),
-              potion->odiluted ? hcolor(NH_ORANGE) : hcolor(NH_RED));
+              otense(obj, "turn"), Blind ? "wrinkled"
+                                   : potion->odiluted ? hcolor(NH_ORANGE)
+                                     : hcolor(NH_RED));
 #else
         pline("%s\82Í\82Ó\82¿\82ª%s\82È\82Á\82½\81D", The(cxname(obj)),
-              jconj_adj(potion->odiluted ? hcolor(NH_ORANGE) : hcolor(NH_RED)));
+              Blind ? "\82µ\82í\82µ\82í\82É"
+              : potion->odiluted ? hcolor_adv(NH_ORANGE)
+                        : hcolor_adv(NH_RED));
 #endif
         potion->in_use = FALSE; /* didn't go poof */
+        if (potion->dknown
+            && !objects[potion->otyp].oc_name_known
+            && !objects[potion->otyp].oc_uname)
+            docall(potion);
         return 1;
     }
 
     if (potion->otyp == POT_WATER && obj->otyp == TOWEL) {
 /*JP
-        pline_The("towel soak up!");
+        pline_The("towel soaks it up!");
 */
         pline_The("\83^\83I\83\8b\82Í\90\85\82ð\8bz\82¢\8d\9e\82ñ\82¾\81I");
         /* wetting towel already done via water_damage() in H2Opotion_dip */
@@ -2569,8 +2741,8 @@ dodip()
             pline_The("potion spills and covers your %s with oil.",
 */
             pline("\96û\82Í\94ò\82Ñ\8eU\82è\82 \82È\82½\82Ì%s\82É\82©\82©\82Á\82½\81D",
-                      makeplural(body_part(FINGER)));
-            incr_itimeout(&Glib, d(2, 10));
+                      fingers_or_gloves(TRUE));
+            make_glib((int) (Glib & TIMEOUT) + d(2, 10));
         } else if (obj->oclass != WEAPON_CLASS && !is_weptool(obj)) {
             /* the following cases apply only to weapons */
             goto more_dips;
@@ -2581,23 +2753,31 @@ dodip()
         } else if ((!is_rustprone(obj) && !is_corrodeable(obj))
                    || is_ammo(obj) || (!obj->oeroded && !obj->oeroded2)) {
             /* uses up potion, doesn't set obj->greased */
-#if 0 /*JP*/
-            pline("%s %s with an oily sheen.", Yname2(obj),
-                  otense(obj, "gleam"));
+            if (!Blind)
+#if 0 /*JP:T*/
+                pline("%s %s with an oily sheen.", Yname2(obj),
+                      otense(obj, "gleam"));
 #else
                 pline("%s\82Í\96û\82Ì\8cõ\91ò\82Å\82«\82ç\82è\82Æ\8cõ\82Á\82½\81D", Yname2(obj));
 #endif
+            else /*if (!uarmg)*/
+/*JP
+                pline("%s %s oily.", Yname2(obj), otense(obj, "feel"));
+*/
+                pline("%s\82Í\96û\82Á\82Û\82¢\81D", Yname2(obj));
         } else {
-#if 0 /*JP*/
-            pline("%s %s less %s.", Yname2(obj), otense(obj, "are"),
+#if 0 /*JP:T*/
+            pline("%s %s less %s.", Yname2(obj),
+                  otense(obj, !Blind ? "are" : "feel"),
                   (obj->oeroded && obj->oeroded2)
                       ? "corroded and rusty"
                       : obj->oeroded ? "rusty" : "corroded");
 #else
-            pline("%s\82Ì%s\82ª\8eæ\82ê\82½\81D", Yname2(obj),
+            pline("%s\82Ì%s\82ª\8eæ\82ê\82½%s\81D", Yname2(obj),
                   (obj->oeroded && obj->oeroded2)
                       ? "\95\85\90H\82Æ\8eK"
-                      : obj->oeroded ? "\8eK" : "\95\85\90H");
+                      : obj->oeroded ? "\8eK" : "\95\85\90H",
+                  !Blind ? "" : "\82æ\82¤\82¾");
 #endif
             if (obj->oeroded > 0)
                 obj->oeroded--;
@@ -2606,11 +2786,12 @@ dodip()
             wisx = TRUE;
         }
         exercise(A_WIS, wisx);
-        makeknown(potion->otyp);
+        if (potion->dknown)
+            makeknown(potion->otyp);
         useup(potion);
         return 1;
     }
-more_dips:
+ more_dips:
 
     /* Allow filling of MAGIC_LAMPs to prevent identification by player */
     if ((obj->otyp == OIL_LAMP || obj->otyp == MAGIC_LAMP)
@@ -2639,13 +2820,17 @@ more_dips:
 */
             You("%s\82É\96û\82ð\93ü\82ê\82½\81D", yname(obj));
             check_unpaid(potion);        /* Yendorian Fuel Tax */
-            obj->age += 2 * potion->age; /* burns more efficiently */
+            /* burns more efficiently in a lamp than in a bottle;
+               diluted potion provides less benefit but we don't attempt
+               to track that the lamp now also has some non-oil in it */
+            obj->age += (!potion->odiluted ? 4L : 3L) * potion->age / 2L;
             if (obj->age > 1500L)
                 obj->age = 1500L;
             useup(potion);
             exercise(A_WIS, TRUE);
         }
-        makeknown(POT_OIL);
+        if (potion->dknown)
+            makeknown(POT_OIL);
         obj->spe = 1;
         update_inventory();
         return 1;
@@ -2653,7 +2838,7 @@ more_dips:
 
     potion->in_use = FALSE; /* didn't go poof */
     if ((obj->otyp == UNICORN_HORN || obj->otyp == AMETHYST)
-        && (mixture = mixtype(obj, potion)) != 0) {
+        && (mixture = mixtype(obj, potion)) != STRANGE_OBJECT) {
         char oldbuf[BUFSZ], newbuf[BUFSZ];
         short old_otyp = potion->otyp;
         boolean old_dknown = FALSE;
@@ -2686,26 +2871,41 @@ more_dips:
             singlepotion->dknown = FALSE;
         } else {
             singlepotion->dknown = !Hallucination;
-#if 0 /*JP*/
+            *newbuf = '\0';
             if (mixture == POT_WATER && singlepotion->dknown)
+/*JP
                 Sprintf(newbuf, "clears");
-            else
+*/
+                Sprintf(newbuf, "\93§\96¾");
+            else if (!Blind)
+#if 0 /*JP*/
                 Sprintf(newbuf, "turns %s",
                         hcolor(OBJ_DESCR(objects[mixture])));
-            pline_The("%spotion%s %s.", oldbuf,
-                      more_than_one ? " that you dipped into" : "", newbuf);
 #else
-            if (mixture == POT_WATER && singlepotion->dknown)
-                Sprintf(newbuf, "\93§\96¾");
-            else
                 Sprintf(newbuf, "%s\96ò",
                         hcolor(OBJ_DESCR(objects[mixture])));
-            pline_The("%s%s\96ò\82Í%s\82É\82È\82Á\82½\81D.", more_than_one ? "\90Z\82µ\82½" : "",
-                      oldbuf, newbuf);
 #endif
-            if (!objects[old_otyp].oc_uname
-                && !objects[old_otyp].oc_name_known && old_dknown) {
+            if (*newbuf)
+#if 0 /*JP:T*/
+                pline_The("%spotion%s %s.", oldbuf,
+                          more_than_one ? " that you dipped into" : "",
+                          newbuf);
+#else
+                pline_The("%s%s\96ò\82Í%s\82É\82È\82Á\82½\81D.",
+                          more_than_one ? "\90Z\82µ\82½" : "",
+                          oldbuf, newbuf);
+#endif
+            else
+/*JP
+                pline("Something happens.");
+*/
+                pline("\89½\82©\82ª\8bN\82«\82½\81D");
+
+            if (old_dknown
+                && !objects[old_otyp].oc_name_known
+                && !objects[old_otyp].oc_uname) {
                 struct obj fakeobj;
+
                 fakeobj = zeroobj;
                 fakeobj.dknown = 1;
                 fakeobj.otyp = old_otyp;
@@ -2714,12 +2914,14 @@ more_dips:
             }
         }
         obj_extract_self(singlepotion);
-        singlepotion =
+        singlepotion = hold_another_object(singlepotion,
 /*JP
-            hold_another_object(singlepotion, "You juggle and drop %s!",
+                                           "You juggle and drop %s!",
 */
-            hold_another_object(singlepotion, "\82¨\8eè\8bÊ\82µ\82Ä%s\82ð\97\8e\82Æ\82µ\82Ä\82µ\82Ü\82Á\82½\81I",
-                                doname(singlepotion), (const char *) 0);
+                                           "\82¨\8eè\8bÊ\82µ\82Ä%s\82ð\97\8e\82Æ\82µ\82Ä\82µ\82Ü\82Á\82½\81I",
+                                           doname(singlepotion),
+                                           (const char *) 0);
+        nhUse(singlepotion);
         update_inventory();
         return 1;
     }
@@ -2730,8 +2932,9 @@ more_dips:
     pline("\96Ê\94\92\82¢\81D\81D\81D");
     return 1;
 
-poof:
-    if (!objects[potion->otyp].oc_name_known
+ poof:
+    if (potion->dknown
+        && !objects[potion->otyp].oc_name_known
         && !objects[potion->otyp].oc_uname)
         docall(potion);
     useup(potion);
@@ -2865,14 +3068,14 @@ struct monst *mon,  /* monster being split */
 
     reason[0] = '\0';
     if (mtmp)
-#if 0 /*JP*/
+#if 0 /*JP:T*/
         Sprintf(reason, " from %s heat",
                 (mtmp == &youmonst) ? the_your[1]
                                     : (const char *) s_suffix(mon_nam(mtmp)));
 #else
         Sprintf(reason, "%s\82Ì\94M\82Å",
                 (mtmp == &youmonst) ? the_your[1]
-                                    : (const char *) s_suffix(mon_nam(mtmp)));
+                                    : (const char *) mon_nam(mtmp));
 #endif
 
     if (mon == &youmonst) {