OSDN Git Service

upgrade to 3.6.1
[jnethack/source.git] / src / potion.c
index 483ecbd..7c5bbb7 100644 (file)
@@ -1,5 +1,6 @@
-/* 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: 1520797133 2018/03/11 19:38:53 $  $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.144 $ */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/*-Copyright (c) Robert Patrick Rankin, 2013. */
 /* NetHack may be freely redistributed.  See license for details. */
 
 /* JNetHack Copyright */
@@ -196,12 +197,12 @@ const char *msg;
     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;
     }
-    set_itimeout(&Slimed, xtime);
     if (!Slimed)
         dealloc_killer(find_delayed_killer(SLIMED));
 }
@@ -220,12 +221,12 @@ const char *killername;
     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 */
     }
-    set_itimeout(&Stoned, xtime);
     if (!Stoned)
         dealloc_killer(find_delayed_killer(STONED));
     else if (!old)
@@ -242,14 +243,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
@@ -360,7 +361,7 @@ boolean talk;
     set_itimeout(&Blinded, xtime);
 
     if (u_could_see ^ can_see_now) { /* one or the other but not both */
-        context.botl = 1;
+        context.botl = TRUE;
         vision_full_recalc = 1; /* blindness just got toggled */
         /* this vision recalculation used to be deferred until
            moveloop(), but that made it possible for vision
@@ -466,7 +467,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);
     }
@@ -479,32 +480,36 @@ 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);
+void
+self_invis_message()
+{
+#if 0 /*JP*/
+    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
@@ -710,8 +715,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;
@@ -722,6 +727,18 @@ 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:
@@ -734,9 +751,9 @@ register struct obj *otmp;
     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;
@@ -746,9 +763,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
@@ -758,7 +775,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",
@@ -790,9 +807,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",
 */
@@ -1265,47 +1282,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), "\93V\88ä\82É\93ª\82ð\82Ô\82Â\82¯\82Ä",
-                           KILLED_BY);
-                }
-            } /*cursed*/
-        } else
-            nothing++;
-        if (otmp->blessed) {
+                       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 */
@@ -1460,8 +1497,12 @@ 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);
+    }
     if (curesick) {
         make_vomiting(0L, TRUE);
         make_sick(0L, (char *) 0, TRUE, SICK_ALL);
@@ -1601,32 +1642,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)))
@@ -1635,30 +1683,34 @@ 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*/
                 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*/
                 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 {
@@ -1677,7 +1729,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"));
 */
@@ -1700,7 +1752,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" : "");
@@ -1720,7 +1773,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);
@@ -1744,17 +1797,22 @@ 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:
@@ -1767,6 +1825,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])
@@ -1802,10 +1862,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)) {
@@ -1830,7 +1895,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;
@@ -1850,7 +1915,7 @@ 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)
@@ -1888,7 +1953,7 @@ boolean your_fault;
             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)) {
@@ -1900,7 +1965,7 @@ 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 (your_fault)
@@ -1922,27 +1987,36 @@ boolean your_fault;
             break;
         */
         }
-        if (angermon)
-            wakeup(mon);
-        else
-            mon->msleeping = 0;
+        /* target might have been killed */
+        if (mon->mhp > 0) {
+            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);
 }
@@ -1952,7 +2026,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:
@@ -1994,18 +2074,25 @@ 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);
         exercise(A_CON, TRUE);
         break;
     case POT_SICKNESS:
@@ -2141,7 +2228,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);
@@ -2224,6 +2311,7 @@ register struct obj *o1, *o2;
         switch (o2->otyp) {
         case POT_SICKNESS:
             return POT_SICKNESS;
+        case POT_ENLIGHTENMENT:
         case POT_SPEED:
             return POT_BOOZE;
         case POT_GAIN_LEVEL:
@@ -2245,19 +2333,24 @@ 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];
+    const char *shortestname; /* last resort obj name for prompt */
 
     allowall[0] = ALL_CLASSES;
     allowall[1] = '\0';
@@ -2269,34 +2362,50 @@ dodip()
     if (inaccessible_equipment(obj, "\82ð\90Z\82·", FALSE))
         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");
+#if 0 /*JP:T*/
+    shortestname = (is_plural(obj) || pair_of(obj)) ? "them" : "it";
+#else
+    shortestname = "\82»\82ê";
 #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)
@@ -2312,8 +2421,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) {
@@ -2378,23 +2493,29 @@ 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);
+               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);
 #if 0 /*JP*/
                 Sprintf(qbuf, "%ld of the", obj->quan);
 #else
@@ -2430,7 +2551,7 @@ dodip()
                 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);
 */
@@ -2442,7 +2563,7 @@ dodip()
         if (Blind || Hallucination)
             obj->dknown = 0;
 
-        if ((mixture = mixtype(obj, potion)) != 0) {
+        if (mixture != STRANGE_OBJECT) {
             obj->otyp = mixture;
         } else {
             switch (obj->odiluted ? 1 : rnd(8)) {
@@ -2655,7 +2776,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;
@@ -2874,7 +2995,7 @@ struct monst *mon,  /* monster being split */
 #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) {