OSDN Git Service

update year to 2020
[jnethack/source.git] / src / potion.c
index c05ceca..66111f4 100644 (file)
@@ -1,11 +1,11 @@
-/* NetHack 3.6 potion.c        $NHDT-Date: 1520797133 2018/03/11 19:38:53 $  $NHDT-Branch: NetHack-3.6.0 $:$NHDT-Revision: 1.144 $ */
+/* 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-2019            */
+/* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2020            */
 /* JNetHack may be freely redistributed.  See license for details. */
 
 #include "hack.h"
@@ -97,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
@@ -125,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;
@@ -132,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
@@ -179,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
@@ -193,7 +206,7 @@ const char *msg;
 {
     long old = Slimed;
 
-#if 0
+#if 0   /* tell player even if hero is unconscious */
     if (Unaware)
         msg = 0;
 #endif
@@ -201,10 +214,17 @@ const char *msg;
     if ((xtime != 0L) ^ (old != 0L)) {
         context.botl = TRUE;
         if (msg)
-            pline1(msg);
+            pline("%s", msg);
     }
-    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 */
@@ -217,7 +237,7 @@ const char *killername;
 {
     long old = Stoned;
 
-#if 0
+#if 0   /* tell player even if hero is unconscious */
     if (Unaware)
         msg = 0;
 #endif
@@ -225,7 +245,7 @@ const char *killername;
     if ((xtime != 0L) ^ (old != 0L)) {
         context.botl = TRUE;
         if (msg)
-            pline1(msg);
+            pline("%s", msg);
     }
     if (!Stoned)
         dealloc_killer(find_delayed_killer(STONED));
@@ -361,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 = 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
-           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 */
@@ -400,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
@@ -430,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)
@@ -495,10 +526,21 @@ boolean talk;
     }
 }
 
+/* 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*/
+#if 0 /*JP:T*/
     pline("%s %s.",
           Hallucination ? "Far out, man!  You"
                         : "Gee!  All of a sudden, you",
@@ -531,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
@@ -664,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
@@ -701,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")
@@ -733,8 +775,7 @@ register struct obj *otmp;
                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) {
+            if (otmp->otyp == POT_RESTORE_ABILITY && u.ulevel < u.ulevelmax) {
                 do {
                     pluslvl(FALSE);
                 } while (u.ulevel < u.ulevelmax && otmp->blessed);
@@ -744,9 +785,9 @@ register struct obj *otmp;
     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) {
@@ -828,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);
@@ -918,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
@@ -926,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."
@@ -984,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
@@ -1088,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"
@@ -1170,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;
         }
@@ -1325,9 +1366,9 @@ register struct obj *otmp;
                     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Ä",
+                losehp(Maybe_Half_Phys(dmg), "\93V\88ä\82É\93ª\82ð\82Ô\82Â\82¯\82Ä",
                        KILLED_BY);
                 nothing = 0; /* not nothing after all */
             }
@@ -1428,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");
@@ -1502,6 +1543,8 @@ register boolean curesick, cureblind;
            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);
@@ -1517,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
@@ -1537,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
@@ -1609,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 */
@@ -1694,7 +1746,7 @@ int how;
             char buf[BUFSZ];
 
             if (hit_saddle && saddle) {
-#if 0 /*JP*/
+#if 0 /*JP:T*/
                 Sprintf(buf, "%s saddle",
                         s_suffix(x_monnam(mon, ARTICLE_THE, (char *) 0,
                                           (SUPPRESS_IT | SUPPRESS_SADDLE),
@@ -1706,7 +1758,7 @@ int how;
                                           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
@@ -1716,7 +1768,7 @@ int how;
             } else {
                 Strcpy(buf, mnam);
             }
-#if 0 /*JP*/
+#if 0 /*JP:T*/
             pline_The("%s crashes on %s and breaks into shards.", botlnam,
                       buf);
 #else
@@ -1815,7 +1867,7 @@ int how;
             /*FALLTHRU*/
         case POT_RESTORE_ABILITY:
         case POT_GAIN_ABILITY:
       do_healing:
+ do_healing:
             angermon = FALSE;
             if (mon->mhp < mon->mhpmax) {
                 mon->mhp = mon->mhpmax;
@@ -1837,14 +1889,14 @@ int how;
                 /* 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))
@@ -1907,7 +1959,7 @@ int how;
             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
@@ -1918,7 +1970,7 @@ int how;
                         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 */
@@ -1947,7 +1999,7 @@ int how;
                     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;
@@ -1957,7 +2009,7 @@ int how;
             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
@@ -1967,7 +2019,7 @@ int how;
                 if (!is_silent(mon->data))
                     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
@@ -1988,7 +2040,7 @@ int how;
         */
         }
         /* target might have been killed */
-        if (mon->mhp > 0) {
+        if (!DEADMONSTER(mon)) {
             if (angermon)
                 wakeup(mon, TRUE);
             else
@@ -2044,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)
@@ -2091,8 +2143,10 @@ register struct obj *obj;
             u.uhp++, context.botl = 1;
         if (obj->blessed)
             cureblind = TRUE;
-        if (cureblind)
+        if (cureblind) {
             make_blinded(0L, !u.ucreamed);
+            make_deaf(0L, TRUE);
+        }
         exercise(A_CON, TRUE);
         break;
     case POT_SICKNESS:
@@ -2130,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");
@@ -2243,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:
@@ -2287,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:
@@ -2308,7 +2354,7 @@ register struct obj *o1, *o2;
         }
         break;
     case POT_FRUIT_JUICE:
-        switch (o2->otyp) {
+        switch (o2typ) {
         case POT_SICKNESS:
             return POT_SICKNESS;
         case POT_ENLIGHTENMENT:
@@ -2320,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;
@@ -2458,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);
@@ -2516,11 +2558,10 @@ dodip()
 
             if ((long) amt < obj->quan) {
                 obj = splitobj(obj, (long) amt);
-#if 0 /*JP*/
+/*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}... */
@@ -2533,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)) {
@@ -2542,15 +2585,14 @@ 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(amt + rnd(9), /* not physical damage */
 /*JP
                    "alchemic blast", KILLED_BY_AN);
@@ -2566,6 +2608,8 @@ dodip()
         if (mixture != STRANGE_OBJECT) {
             obj->otyp = mixture;
         } else {
+            struct obj *otmp;
+
             switch (obj->odiluted ? 1 : rnd(8)) {
             case 1:
                 obj->otyp = POT_WATER;
@@ -2574,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;
             }
         }
@@ -2607,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
@@ -2615,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
@@ -2626,16 +2668,23 @@ 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É"
+              : jconj_adj(potion->odiluted ? hcolor(NH_ORANGE)
+                        : hcolor(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;
     }
 
@@ -2692,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;
@@ -2704,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--;
@@ -2729,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)
@@ -2762,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;
@@ -2809,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;
@@ -2837,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;
     }
@@ -2853,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);
@@ -2988,7 +3068,7 @@ 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)));