]> granicus.if.org Git - nethack/commitdiff
Split potion effects into separate functions
authorPasi Kallinen <paxed@alt.org>
Wed, 7 Jul 2021 09:54:35 +0000 (12:54 +0300)
committerPasi Kallinen <paxed@alt.org>
Wed, 7 Jul 2021 09:54:35 +0000 (12:54 +0300)
src/potion.c

index d9abc90a7ae6943d3de2c82b495dcb87a8a45c1e..a743d1a566fb233affb3d6e7c1d61563282d6c3d 100644 (file)
@@ -9,6 +9,31 @@ static long itimeout(long);
 static long itimeout_incr(long, int);
 static void ghost_from_bottle(void);
 static int drink_ok(struct obj *);
+static void peffect_restore_ability(struct obj *);
+static void peffect_hallucination(struct obj *);
+static void peffect_water(struct obj *);
+static void peffect_booze(struct obj *);
+static void peffect_enlightenment(struct obj *);
+static void peffect_invisibility(struct obj *);
+static void peffect_see_invisible(struct obj *);
+static void peffect_paralysis(struct obj *);
+static void peffect_sleeping(struct obj *);
+static int peffect_monster_detection(struct obj *);
+static int peffect_object_detection(struct obj *);
+static void peffect_sickness(struct obj *);
+static void peffect_confusion(struct obj *);
+static void peffect_gain_ability(struct obj *);
+static void peffect_speed(struct obj *);
+static void peffect_blindness(struct obj *);
+static void peffect_gain_level(struct obj *);
+static void peffect_healing(struct obj *);
+static void peffect_extra_healing(struct obj *);
+static void peffect_full_healing(struct obj *);
+static void peffect_levitation(struct obj *);
+static void peffect_gain_energy(struct obj *);
+static void peffect_oil(struct obj *);
+static void peffect_acid(struct obj *);
+static void peffect_polymorph(struct obj *);
 static boolean H2Opotion_dip(struct obj *, struct obj *, boolean,
                              const char *);
 static short mixtype(struct obj *, struct obj *);
@@ -569,595 +594,749 @@ dopotion(struct obj *otmp)
     return 1;
 }
 
-int
-peffects(struct obj *otmp)
+static void
+peffect_restore_ability(struct obj *otmp)
 {
-    register int i, ii, lim;
-
-    switch (otmp->otyp) {
-    case POT_RESTORE_ABILITY:
-    case SPE_RESTORE_ABILITY:
-        g.potion_unkn++;
-        if (otmp->cursed) {
-            pline("Ulch!  This makes you feel mediocre!");
-            break;
-        } else {
-            /* unlike unicorn horn, overrides Fixed_abil */
-            pline("Wow!  This makes you feel %s!",
-                  (otmp->blessed)
-                      ? (unfixable_trouble_count(FALSE) ? "better" : "great")
-                      : "good");
-            i = rn2(A_MAX); /* start at a random point */
-            for (ii = 0; ii < A_MAX; ii++) {
-                lim = AMAX(i);
-                /* 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;
-                    g.context.botl = 1;
-                    /* only first found if not blessed */
-                    if (!otmp->blessed)
-                        break;
-                }
-                if (++i >= A_MAX)
-                    i = 0;
+    g.potion_unkn++;
+    if (otmp->cursed) {
+        pline("Ulch!  This makes you feel mediocre!");
+        return;
+    } else {
+        int i, ii;
+
+        /* unlike unicorn horn, overrides Fixed_abil */
+        pline("Wow!  This makes you feel %s!",
+              (otmp->blessed)
+              ? (unfixable_trouble_count(FALSE) ? "better" : "great")
+              : "good");
+        i = rn2(A_MAX); /* start at a random point */
+        for (ii = 0; ii < A_MAX; ii++) {
+            int lim = AMAX(i);
+
+            /* 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;
+                g.context.botl = 1;
+                /* only first found if not blessed */
+                if (!otmp->blessed)
+                    break;
             }
+            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);
-            }
+        /* 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 (Halluc_resistance) {
-            g.potion_nothing++;
-            break;
-        } else if (Hallucination) {
-            g.potion_nothing++;
+    }
+}
+
+static void
+peffect_hallucination(struct obj *otmp)
+{
+    if (Halluc_resistance) {
+        g.potion_nothing++;
+        return;
+    } else if (Hallucination) {
+        g.potion_nothing++;
+    }
+    (void) make_hallucinated(itimeout_incr(HHallucination,
+                                           rn1(200, 600 - 300 * bcsign(otmp))),
+                             TRUE, 0L);
+    if ((otmp->blessed && !rn2(3)) || (!otmp->cursed && !rn2(6))) {
+        You("perceive yourself...");
+        display_nhwindow(WIN_MESSAGE, FALSE);
+        enlightenment(MAGICENLIGHTENMENT, ENL_GAMEINPROGRESS);
+        Your("awareness re-normalizes.");
+        exercise(A_WIS, TRUE);
+    }
+}
+
+static void
+peffect_water(struct obj *otmp)
+{
+    if (!otmp->blessed && !otmp->cursed) {
+        pline("This tastes like %s.", hliquid("water"));
+        u.uhunger += rnd(10);
+        newuhs(FALSE);
+        return;
+    }
+    g.potion_unkn++;
+    if (is_undead(g.youmonst.data) || is_demon(g.youmonst.data)
+        || u.ualign.type == A_CHAOTIC) {
+        if (otmp->blessed) {
+            pline("This burns like %s!", hliquid("acid"));
+            exercise(A_CON, FALSE);
+            if (u.ulycn >= LOW_PM) {
+                Your("affinity to %s disappears!",
+                     makeplural(mons[u.ulycn].pmnames[NEUTRAL]));
+                if (g.youmonst.data == &mons[u.ulycn])
+                    you_unwere(FALSE);
+                set_ulycn(NON_PM); /* cure lycanthropy */
+            }
+            losehp(Maybe_Half_Phys(d(2, 6)), "potion of holy water",
+                   KILLED_BY_AN);
+        } else if (otmp->cursed) {
+            You_feel("quite proud of yourself.");
+            healup(d(2, 6), 0, 0, 0);
+            if (u.ulycn >= LOW_PM && !Upolyd)
+                you_were();
+            exercise(A_CON, TRUE);
         }
-        (void) make_hallucinated(itimeout_incr(HHallucination,
-                                          rn1(200, 600 - 300 * bcsign(otmp))),
-                                 TRUE, 0L);
-        if ((otmp->blessed && !rn2(3)) || (!otmp->cursed && !rn2(6))) {
-            You("perceive yourself...");
-            display_nhwindow(WIN_MESSAGE, FALSE);
-            enlightenment(MAGICENLIGHTENMENT, ENL_GAMEINPROGRESS);
-            Your("awareness re-normalizes.");
+    } else {
+        if (otmp->blessed) {
+            You_feel("full of awe.");
+            make_sick(0L, (char *) 0, TRUE, SICK_ALL);
             exercise(A_WIS, TRUE);
+            exercise(A_CON, TRUE);
+            if (u.ulycn >= LOW_PM)
+                you_unwere(TRUE); /* "Purified" */
+            /* make_confused(0L, TRUE); */
+        } else {
+            if (u.ualign.type == A_LAWFUL) {
+                pline("This burns like %s!", hliquid("acid"));
+                losehp(Maybe_Half_Phys(d(2, 6)), "potion of unholy water",
+                       KILLED_BY_AN);
+            } else
+                You_feel("full of dread.");
+            if (u.ulycn >= LOW_PM && !Upolyd)
+                you_were();
+            exercise(A_CON, FALSE);
         }
-        break;
-    case POT_WATER:
-        if (!otmp->blessed && !otmp->cursed) {
-            pline("This tastes like %s.", hliquid("water"));
-            u.uhunger += rnd(10);
-            newuhs(FALSE);
-            break;
+    }
+}
+
+static void
+peffect_booze(struct obj *otmp)
+{
+    g.potion_unkn++;
+    pline("Ooph!  This tastes like %s%s!",
+          otmp->odiluted ? "watered down " : "",
+          Hallucination ? "dandelion wine" : "liquid fire");
+    if (!otmp->blessed)
+        make_confused(itimeout_incr(HConfusion, d(3, 8)), FALSE);
+    /* the whiskey makes us feel better */
+    if (!otmp->odiluted)
+        healup(1, 0, FALSE, FALSE);
+    u.uhunger += 10 * (2 + bcsign(otmp));
+    newuhs(FALSE);
+    exercise(A_WIS, FALSE);
+    if (otmp->cursed) {
+        You("pass out.");
+        g.multi = -rnd(15);
+        g.nomovemsg = "You awake with a headache.";
+    }
+}
+
+static void
+peffect_enlightenment(struct obj *otmp)
+{
+    if (otmp->cursed) {
+        g.potion_unkn++;
+        You("have an uneasy feeling...");
+        exercise(A_WIS, FALSE);
+    } else {
+        if (otmp->blessed) {
+            (void) adjattrib(A_INT, 1, FALSE);
+            (void) adjattrib(A_WIS, 1, FALSE);
         }
+        do_enlightenment_effect();
+    }
+}
+
+static void
+peffect_invisibility(struct obj *otmp)
+{
+    boolean is_spell = (otmp->oclass == SPBOOK_CLASS);
+
+    /* spell cannot penetrate mummy wrapping */
+    if (is_spell && BInvis && uarmc->otyp == MUMMY_WRAPPING) {
+        You_feel("rather itchy under %s.", yname(uarmc));
+        return;
+    }
+    if (Invis || Blind || BInvis) {
+        g.potion_nothing++;
+    } else {
+        self_invis_message();
+    }
+    if (otmp->blessed)
+        HInvis |= FROMOUTSIDE;
+    else
+        incr_itimeout(&HInvis, rn1(15, 31));
+    newsym(u.ux, u.uy); /* update position */
+    if (otmp->cursed) {
+        pline("For some reason, you feel your presence is known.");
+        aggravate();
+    }
+}
+
+static void
+peffect_see_invisible(struct obj *otmp)
+{
+    int msg = Invisible && !Blind;
+
+    g.potion_unkn++;
+    if (otmp->cursed)
+        pline("Yecch!  This tastes %s.",
+              Hallucination ? "overripe" : "rotten");
+    else
+        pline(
+              Hallucination
+              ? "This tastes like 10%% real %s%s all-natural beverage."
+              : "This tastes like %s%s.",
+              otmp->odiluted ? "reconstituted " : "", fruitname(TRUE));
+    if (otmp->otyp == POT_FRUIT_JUICE) {
+        u.uhunger += (otmp->odiluted ? 5 : 10) * (2 + bcsign(otmp));
+        newuhs(FALSE);
+        return;
+    }
+    if (!otmp->cursed) {
+        /* Tell them they can see again immediately, which
+         * will help them identify the potion...
+         */
+        make_blinded(0L, TRUE);
+    }
+    if (otmp->blessed)
+        HSee_invisible |= FROMOUTSIDE;
+    else
+        incr_itimeout(&HSee_invisible, rn1(100, 750));
+    set_mimic_blocking(); /* do special mimic handling */
+    see_monsters();       /* see invisible monsters */
+    newsym(u.ux, u.uy);   /* see yourself! */
+    if (msg && !Blind) {  /* Blind possible if polymorphed */
+        You("can see through yourself, but you are visible!");
+        g.potion_unkn--;
+    }
+}
+
+static void
+peffect_paralysis(struct obj *otmp)
+{
+    if (Free_action) {
+        You("stiffen momentarily.");
+    } else {
+        if (Levitation || Is_airlevel(&u.uz) || Is_waterlevel(&u.uz))
+            You("are motionlessly suspended.");
+        else if (u.usteed)
+            You("are frozen in place!");
+        else
+            Your("%s are frozen to the %s!", makeplural(body_part(FOOT)),
+                 surface(u.ux, u.uy));
+        nomul(-(rn1(10, 25 - 12 * bcsign(otmp))));
+        g.multi_reason = "frozen by a potion";
+        g.nomovemsg = You_can_move_again;
+        exercise(A_DEX, FALSE);
+    }
+}
+
+static void
+peffect_sleeping(struct obj *otmp)
+{
+    if (Sleep_resistance || Free_action) {
+        monstseesu(M_SEEN_SLEEP);
+        You("yawn.");
+    } else {
+        You("suddenly fall asleep!");
+        fall_asleep(-rn1(10, 25 - 12 * bcsign(otmp)), TRUE);
+    }
+}
+
+static int
+peffect_monster_detection(struct obj *otmp)
+{
+    if (otmp->blessed) {
+        int i, x, y;
+
+        if (Detect_monsters)
+            g.potion_nothing++;
         g.potion_unkn++;
-        if (is_undead(g.youmonst.data) || is_demon(g.youmonst.data)
-            || u.ualign.type == A_CHAOTIC) {
-            if (otmp->blessed) {
-                pline("This burns like %s!", hliquid("acid"));
-                exercise(A_CON, FALSE);
-                if (u.ulycn >= LOW_PM) {
-                    Your("affinity to %s disappears!",
-                         makeplural(mons[u.ulycn].pmnames[NEUTRAL]));
-                    if (g.youmonst.data == &mons[u.ulycn])
-                        you_unwere(FALSE);
-                    set_ulycn(NON_PM); /* cure lycanthropy */
+        /* after a while, repeated uses become less effective */
+        if ((HDetect_monsters & TIMEOUT) >= 300L)
+            i = 1;
+        else
+            i = rn1(40, 21);
+        incr_itimeout(&HDetect_monsters, i);
+        for (x = 1; x < COLNO; x++) {
+            for (y = 0; y < ROWNO; y++) {
+                if (levl[x][y].glyph == GLYPH_INVISIBLE) {
+                    unmap_object(x, y);
+                    newsym(x, y);
                 }
-                losehp(Maybe_Half_Phys(d(2, 6)), "potion of holy water",
-                       KILLED_BY_AN);
-            } else if (otmp->cursed) {
-                You_feel("quite proud of yourself.");
-                healup(d(2, 6), 0, 0, 0);
-                if (u.ulycn >= LOW_PM && !Upolyd)
-                    you_were();
-                exercise(A_CON, TRUE);
+                if (MON_AT(x, y))
+                    g.potion_unkn = 0;
             }
+        }
+        /* if swallowed or underwater, fall through to uncursed case */
+        if (!u.uswallow && !Underwater) {
+            see_monsters();
+            if (g.potion_unkn)
+                You_feel("lonely.");
+            return 0;
+        }
+    }
+    if (monster_detect(otmp, 0))
+        return 1; /* nothing detected */
+    exercise(A_WIS, TRUE);
+    return 0;
+}
+
+static int
+peffect_object_detection(struct obj *otmp)
+{
+    if (object_detect(otmp, 0))
+        return 1; /* nothing detected */
+    exercise(A_WIS, TRUE);
+    return 0;
+}
+
+static void
+peffect_sickness(struct obj *otmp)
+{
+    pline("Yecch!  This stuff tastes like poison.");
+    if (otmp->blessed) {
+        pline("(But in fact it was mildly stale %s.)", fruitname(TRUE));
+        if (!Role_if(PM_HEALER)) {
+            /* NB: blessed otmp->fromsink is not possible */
+            losehp(1, "mildly contaminated potion", KILLED_BY_AN);
+        }
+    } else {
+        if (Poison_resistance)
+            pline("(But in fact it was biologically contaminated %s.)",
+                  fruitname(TRUE));
+        if (Role_if(PM_HEALER)) {
+            pline("Fortunately, you have been immunized.");
         } else {
-            if (otmp->blessed) {
-                You_feel("full of awe.");
-                make_sick(0L, (char *) 0, TRUE, SICK_ALL);
-                exercise(A_WIS, TRUE);
-                exercise(A_CON, TRUE);
-                if (u.ulycn >= LOW_PM)
-                    you_unwere(TRUE); /* "Purified" */
-                /* make_confused(0L, TRUE); */
-            } else {
-                if (u.ualign.type == A_LAWFUL) {
-                    pline("This burns like %s!", hliquid("acid"));
-                    losehp(Maybe_Half_Phys(d(2, 6)), "potion of unholy water",
+            char contaminant[BUFSZ];
+            int typ = rn2(A_MAX);
+
+            Sprintf(contaminant, "%s%s",
+                    (Poison_resistance) ? "mildly " : "",
+                    (otmp->fromsink) ? "contaminated tap water"
+                    : "contaminated potion");
+            if (!Fixed_abil) {
+                poisontell(typ, FALSE);
+                (void) adjattrib(typ, Poison_resistance ? -1 : -rn1(4, 3),
+                                 1);
+            }
+            if (!Poison_resistance) {
+                if (otmp->fromsink)
+                    losehp(rnd(10) + 5 * !!(otmp->cursed), contaminant,
+                           KILLED_BY);
+                else
+                    losehp(rnd(10) + 5 * !!(otmp->cursed), contaminant,
                            KILLED_BY_AN);
+            } else {
+                /* rnd loss is so that unblessed poorer than blessed */
+                losehp(1 + rn2(2), contaminant,
+                       (otmp->fromsink) ? KILLED_BY : KILLED_BY_AN);
+            }
+            exercise(A_CON, FALSE);
+        }
+    }
+    if (Hallucination) {
+        You("are shocked back to your senses!");
+        (void) make_hallucinated(0L, FALSE, 0L);
+    }
+}
+
+static void
+peffect_confusion(struct obj *otmp)
+{
+    if (!Confusion) {
+        if (Hallucination) {
+            pline("What a trippy feeling!");
+            g.potion_unkn++;
+        } else
+            pline("Huh, What?  Where am I?");
+    } else
+        g.potion_nothing++;
+    make_confused(itimeout_incr(HConfusion,
+                                rn1(7, 16 - 8 * bcsign(otmp))),
+                  FALSE);
+}
+
+static void
+peffect_gain_ability(struct obj *otmp)
+{
+    if (otmp->cursed) {
+        pline("Ulch!  That potion tasted foul!");
+        g.potion_unkn++;
+    } else if (Fixed_abil) {
+        g.potion_nothing++;
+    } else {      /* If blessed, increase all; if not, try up to */
+        int itmp; /* 6 times to find one which can be increased. */
+        int ii, i = -1;   /* increment to 0 */
+        for (ii = A_MAX; ii > 0; ii--) {
+            i = (otmp->blessed ? i + 1 : rn2(A_MAX));
+            /* only give "your X is already as high as it can get"
+               message on last attempt (except blessed potions) */
+            itmp = (otmp->blessed || ii == 1) ? 0 : -1;
+            if (adjattrib(i, 1, itmp) && !otmp->blessed)
+                break;
+        }
+    }
+}
+
+static void
+peffect_speed(struct obj *otmp)
+{
+    boolean is_speed = (otmp->otyp == POT_SPEED);
+
+    /* skip when mounted; heal_legs() would heal steed's legs */
+    if (is_speed && Wounded_legs && !otmp->cursed && !u.usteed) {
+        heal_legs(0);
+        g.potion_unkn++;
+        return;
+    }
+
+    if (!Very_fast) { /* wwf@doe.carleton.ca */
+        You("are suddenly moving %sfaster.", Fast ? "" : "much ");
+    } else {
+        Your("%s get new energy.", makeplural(body_part(LEG)));
+        g.potion_unkn++;
+    }
+    exercise(A_DEX, TRUE);
+    incr_itimeout(&HFast, rn1(10, 100 + 60 * bcsign(otmp)));
+}
+
+static void
+peffect_blindness(struct obj *otmp)
+{
+    if (Blind)
+        g.potion_nothing++;
+    make_blinded(itimeout_incr(Blinded,
+                               rn1(200, 250 - 125 * bcsign(otmp))),
+                 (boolean) !Blind);
+}
+
+static void
+peffect_gain_level(struct obj *otmp)
+{
+    if (otmp->cursed) {
+        g.potion_unkn++;
+        /* they went up a level */
+        if ((ledger_no(&u.uz) == 1 && u.uhave.amulet)
+            || Can_rise_up(u.ux, u.uy, &u.uz)) {
+            const char *riseup = "rise up, through the %s!";
+
+            if (ledger_no(&u.uz) == 1) {
+                You(riseup, ceiling(u.ux, u.uy));
+                goto_level(&earth_level, FALSE, FALSE, FALSE);
+            } else {
+                register int newlev = depth(&u.uz) - 1;
+                d_level newlevel;
+
+                get_level(&newlevel, newlev);
+                if (on_level(&newlevel, &u.uz)) {
+                    pline("It tasted bad.");
+                    return;
                 } else
-                    You_feel("full of dread.");
-                if (u.ulycn >= LOW_PM && !Upolyd)
-                    you_were();
-                exercise(A_CON, FALSE);
+                    You(riseup, ceiling(u.ux, u.uy));
+                goto_level(&newlevel, FALSE, FALSE, FALSE);
             }
+        } else
+            You("have an uneasy feeling.");
+        return;
+    }
+    pluslvl(FALSE);
+    /* blessed potions place you at a random spot in the
+       middle of the new level instead of the low point */
+    if (otmp->blessed)
+        u.uexp = rndexp(TRUE);
+}
+
+static void
+peffect_healing(struct obj *otmp)
+{
+    You_feel("better.");
+    healup(d(6 + 2 * bcsign(otmp), 4), !otmp->cursed ? 1 : 0,
+           !!otmp->blessed, !otmp->cursed);
+    exercise(A_CON, TRUE);
+}
+
+static void
+peffect_extra_healing(struct obj *otmp)
+{
+    You_feel("much better.");
+    healup(d(6 + 2 * bcsign(otmp), 8),
+           otmp->blessed ? 5 : !otmp->cursed ? 2 : 0, !otmp->cursed,
+           TRUE);
+    (void) make_hallucinated(0L, TRUE, 0L);
+    exercise(A_CON, TRUE);
+    exercise(A_STR, TRUE);
+}
+
+static void
+peffect_full_healing(struct obj *otmp)
+{
+    You_feel("completely healed.");
+    healup(400, 4 + 4 * bcsign(otmp), !otmp->cursed, TRUE);
+    /* Restore one lost level if blessed */
+    if (otmp->blessed && u.ulevel < u.ulevelmax) {
+        /* when multiple levels have been lost, drinking
+           multiple potions will only get half of them back */
+        u.ulevelmax -= 1;
+        pluslvl(FALSE);
+    }
+    (void) make_hallucinated(0L, TRUE, 0L);
+    exercise(A_STR, TRUE);
+    exercise(A_CON, TRUE);
+}
+
+static void
+peffect_levitation(struct obj *otmp)
+{
+    /*
+     * 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();
+        /* 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 */
+        g.potion_nothing++;
+
+    if (otmp->cursed) {
+        stairway *stway;
+
+        /* '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 ((stway = stairway_at(u.ux, u.uy)) != 0 && stway->up) {
+            (void) doup();
+            /* in case we're already Levitating, which would have
+               resulted in incrementing 'nothing' */
+            g.potion_nothing = 0; /* not nothing after all */
+        } else if (has_ceiling(&u.uz)) {
+            int dmg = rnd(!uarmh ? 10 : !is_metallic(uarmh) ? 6 : 3);
+
+            You("hit your %s on the %s.", body_part(HEAD),
+                ceiling(u.ux, u.uy));
+            losehp(Maybe_Half_Phys(dmg), "colliding with the ceiling",
+                   KILLED_BY);
+            g.potion_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 /* timeout is already at least 1 */
+        incr_itimeout(&HLevitation, rn1(140, 10));
+
+    if (Levitation && IS_SINK(levl[u.ux][u.uy].typ))
+        spoteffects(FALSE);
+    /* levitating blocks flying */
+    float_vs_flight();
+}
+
+static void
+peffect_gain_energy(struct obj *otmp)
+{
+    int num;
+
+    if (otmp->cursed)
+        You_feel("lackluster.");
+    else
+        pline("Magical energies course through your body.");
+
+    /* old: num = rnd(5) + 5 * otmp->blessed + 1;
+     *      blessed:  +7..11 max & current (+9 avg)
+     *      uncursed: +2.. 6 max & current (+4 avg)
+     *      cursed:   -2.. 6 max & current (-4 avg)
+     * new: (3.6.0)
+     *      blessed:  +3..18 max (+10.5 avg), +9..54 current (+31.5 avg)
+     *      uncursed: +2..12 max (+ 7   avg), +6..36 current (+21   avg)
+     *      cursed:   -1.. 6 max (- 3.5 avg), -3..18 current (-10.5 avg)
+     */
+    num = d(otmp->blessed ? 3 : !otmp->cursed ? 2 : 1, 6);
+    if (otmp->cursed)
+        num = -num; /* subtract instead of add when cursed */
+    u.uenmax += num;
+    if (u.uenmax <= 0)
+        u.uenmax = 0;
+    u.uen += 3 * num;
+    if (u.uen > u.uenmax)
+        u.uen = u.uenmax;
+    else if (u.uen <= 0)
+        u.uen = 0;
+    g.context.botl = 1;
+    exercise(A_WIS, TRUE);
+}
+
+static void
+peffect_oil(struct obj *otmp)
+{
+    boolean good_for_you = FALSE, vulnerable;
+
+    if (otmp->lamplit) {
+        if (likes_fire(g.youmonst.data)) {
+            pline("Ahh, a refreshing drink.");
+            good_for_you = TRUE;
+        } else {
+            /*
+             * Note: if poly'd into green slime, hero ought to take
+             * extra damage, but drinking potions in that form isn't
+             * possible so there's no need to try to handle that.
+             */
+            You("burn your %s.", body_part(FACE));
+            /* fire damage */
+            vulnerable = !Fire_resistance || Cold_resistance;
+            losehp(d(vulnerable ? 4 : 2, 4),
+                   "quaffing a burning potion of oil",
+                   KILLED_BY);
         }
+        /*
+         * This is slightly iffy because the burning isn't being
+         * spread across the body.  But the message is "the slime
+         * that covers you burns away" and having that follow
+         * "you burn your face" seems consistent enough.
+         */
+        burn_away_slime();
+    } else if (otmp->cursed) {
+        pline("This tastes like castor oil.");
+    } else {
+        pline("That was smooth!");
+    }
+    exercise(A_WIS, good_for_you);
+}
+
+static void
+peffect_acid(struct obj *otmp)
+{
+    if (Acid_resistance) {
+        /* Not necessarily a creature who _likes_ acid */
+        pline("This tastes %s.", Hallucination ? "tangy" : "sour");
+    } else {
+        int dmg;
+
+        pline("This burns%s!",
+              otmp->blessed ? " a little" : otmp->cursed ? " a lot"
+                                                         : " like acid");
+        dmg = d(otmp->cursed ? 2 : 1, otmp->blessed ? 4 : 8);
+        losehp(Maybe_Half_Phys(dmg), "potion of acid", KILLED_BY_AN);
+        exercise(A_CON, FALSE);
+    }
+    if (Stoned)
+        fix_petrification();
+    g.potion_unkn++; /* holy/unholy water can burn like acid too */
+}
+
+static void
+peffect_polymorph(struct obj *otmp UNUSED)
+{
+    You_feel("a little %s.", Hallucination ? "normal" : "strange");
+    if (!Unchanging)
+        polyself(0);
+}
+
+int
+peffects(struct obj *otmp)
+{
+    switch (otmp->otyp) {
+    case POT_RESTORE_ABILITY:
+    case SPE_RESTORE_ABILITY:
+        peffect_restore_ability(otmp);
+        break;
+    case POT_HALLUCINATION:
+        peffect_hallucination(otmp);
+        break;
+    case POT_WATER:
+        peffect_water(otmp);
         break;
     case POT_BOOZE:
-        g.potion_unkn++;
-        pline("Ooph!  This tastes like %s%s!",
-              otmp->odiluted ? "watered down " : "",
-              Hallucination ? "dandelion wine" : "liquid fire");
-        if (!otmp->blessed)
-            make_confused(itimeout_incr(HConfusion, d(3, 8)), FALSE);
-        /* the whiskey makes us feel better */
-        if (!otmp->odiluted)
-            healup(1, 0, FALSE, FALSE);
-        u.uhunger += 10 * (2 + bcsign(otmp));
-        newuhs(FALSE);
-        exercise(A_WIS, FALSE);
-        if (otmp->cursed) {
-            You("pass out.");
-            g.multi = -rnd(15);
-            g.nomovemsg = "You awake with a headache.";
-        }
+        peffect_booze(otmp);
         break;
     case POT_ENLIGHTENMENT:
-        if (otmp->cursed) {
-            g.potion_unkn++;
-            You("have an uneasy feeling...");
-            exercise(A_WIS, FALSE);
-        } else {
-            if (otmp->blessed) {
-                (void) adjattrib(A_INT, 1, FALSE);
-                (void) adjattrib(A_WIS, 1, FALSE);
-            }
-            do_enlightenment_effect();
-        }
+        peffect_enlightenment(otmp);
         break;
     case SPE_INVISIBILITY:
-        /* spell cannot penetrate mummy wrapping */
-        if (BInvis && uarmc->otyp == MUMMY_WRAPPING) {
-            You_feel("rather itchy under %s.", yname(uarmc));
-            break;
-        }
-        /* FALLTHRU */
     case POT_INVISIBILITY:
-        if (Invis || Blind || BInvis) {
-            g.potion_nothing++;
-        } else {
-            self_invis_message();
-        }
-        if (otmp->blessed)
-            HInvis |= FROMOUTSIDE;
-        else
-            incr_itimeout(&HInvis, rn1(15, 31));
-        newsym(u.ux, u.uy); /* update position */
-        if (otmp->cursed) {
-            pline("For some reason, you feel your presence is known.");
-            aggravate();
-        }
+        peffect_invisibility(otmp);
         break;
     case POT_SEE_INVISIBLE: /* tastes like fruit juice in Rogue */
-    case POT_FRUIT_JUICE: {
-        int msg = Invisible && !Blind;
-
-        g.potion_unkn++;
-        if (otmp->cursed)
-            pline("Yecch!  This tastes %s.",
-                  Hallucination ? "overripe" : "rotten");
-        else
-            pline(
-                Hallucination
-                    ? "This tastes like 10%% real %s%s all-natural beverage."
-                    : "This tastes like %s%s.",
-                otmp->odiluted ? "reconstituted " : "", fruitname(TRUE));
-        if (otmp->otyp == POT_FRUIT_JUICE) {
-            u.uhunger += (otmp->odiluted ? 5 : 10) * (2 + bcsign(otmp));
-            newuhs(FALSE);
-            break;
-        }
-        if (!otmp->cursed) {
-            /* Tell them they can see again immediately, which
-             * will help them identify the potion...
-             */
-            make_blinded(0L, TRUE);
-        }
-        if (otmp->blessed)
-            HSee_invisible |= FROMOUTSIDE;
-        else
-            incr_itimeout(&HSee_invisible, rn1(100, 750));
-        set_mimic_blocking(); /* do special mimic handling */
-        see_monsters();       /* see invisible monsters */
-        newsym(u.ux, u.uy);   /* see yourself! */
-        if (msg && !Blind) {  /* Blind possible if polymorphed */
-            You("can see through yourself, but you are visible!");
-            g.potion_unkn--;
-        }
+    case POT_FRUIT_JUICE:
+        peffect_see_invisible(otmp);
         break;
-    }
     case POT_PARALYSIS:
-        if (Free_action) {
-            You("stiffen momentarily.");
-        } else {
-            if (Levitation || Is_airlevel(&u.uz) || Is_waterlevel(&u.uz))
-                You("are motionlessly suspended.");
-            else if (u.usteed)
-                You("are frozen in place!");
-            else
-                Your("%s are frozen to the %s!", makeplural(body_part(FOOT)),
-                     surface(u.ux, u.uy));
-            nomul(-(rn1(10, 25 - 12 * bcsign(otmp))));
-            g.multi_reason = "frozen by a potion";
-            g.nomovemsg = You_can_move_again;
-            exercise(A_DEX, FALSE);
-        }
+        peffect_paralysis(otmp);
         break;
     case POT_SLEEPING:
-        if (Sleep_resistance || Free_action) {
-            monstseesu(M_SEEN_SLEEP);
-            You("yawn.");
-        } else {
-            You("suddenly fall asleep!");
-            fall_asleep(-rn1(10, 25 - 12 * bcsign(otmp)), TRUE);
-        }
+        peffect_sleeping(otmp);
         break;
     case POT_MONSTER_DETECTION:
     case SPE_DETECT_MONSTERS:
-        if (otmp->blessed) {
-            int x, y;
-
-            if (Detect_monsters)
-                g.potion_nothing++;
-            g.potion_unkn++;
-            /* after a while, repeated uses become less effective */
-            if ((HDetect_monsters & TIMEOUT) >= 300L)
-                i = 1;
-            else
-                i = rn1(40, 21);
-            incr_itimeout(&HDetect_monsters, i);
-            for (x = 1; x < COLNO; x++) {
-                for (y = 0; y < ROWNO; y++) {
-                    if (levl[x][y].glyph == GLYPH_INVISIBLE) {
-                        unmap_object(x, y);
-                        newsym(x, y);
-                    }
-                    if (MON_AT(x, y))
-                        g.potion_unkn = 0;
-                }
-            }
-            /* if swallowed or underwater, fall through to uncursed case */
-            if (!u.uswallow && !Underwater) {
-                see_monsters();
-                if (g.potion_unkn)
-                    You_feel("lonely.");
-                break;
-            }
-        }
-        if (monster_detect(otmp, 0))
-            return 1; /* nothing detected */
-        exercise(A_WIS, TRUE);
+        if (peffect_monster_detection(otmp))
+            return 1;
         break;
     case POT_OBJECT_DETECTION:
     case SPE_DETECT_TREASURE:
-        if (object_detect(otmp, 0))
-            return 1; /* nothing detected */
-        exercise(A_WIS, TRUE);
+        if (peffect_object_detection(otmp))
+            return 1;
         break;
     case POT_SICKNESS:
-        pline("Yecch!  This stuff tastes like poison.");
-        if (otmp->blessed) {
-            pline("(But in fact it was mildly stale %s.)", fruitname(TRUE));
-            if (!Role_if(PM_HEALER)) {
-                /* NB: blessed otmp->fromsink is not possible */
-                losehp(1, "mildly contaminated potion", KILLED_BY_AN);
-            }
-        } else {
-            if (Poison_resistance)
-                pline("(But in fact it was biologically contaminated %s.)",
-                      fruitname(TRUE));
-            if (Role_if(PM_HEALER)) {
-                pline("Fortunately, you have been immunized.");
-            } else {
-                char contaminant[BUFSZ];
-                int typ = rn2(A_MAX);
-
-                Sprintf(contaminant, "%s%s",
-                        (Poison_resistance) ? "mildly " : "",
-                        (otmp->fromsink) ? "contaminated tap water"
-                                         : "contaminated potion");
-                if (!Fixed_abil) {
-                    poisontell(typ, FALSE);
-                    (void) adjattrib(typ, Poison_resistance ? -1 : -rn1(4, 3),
-                                     1);
-                }
-                if (!Poison_resistance) {
-                    if (otmp->fromsink)
-                        losehp(rnd(10) + 5 * !!(otmp->cursed), contaminant,
-                               KILLED_BY);
-                    else
-                        losehp(rnd(10) + 5 * !!(otmp->cursed), contaminant,
-                               KILLED_BY_AN);
-                } else {
-                    /* rnd loss is so that unblessed poorer than blessed */
-                    losehp(1 + rn2(2), contaminant,
-                           (otmp->fromsink) ? KILLED_BY : KILLED_BY_AN);
-                }
-                exercise(A_CON, FALSE);
-            }
-        }
-        if (Hallucination) {
-            You("are shocked back to your senses!");
-            (void) make_hallucinated(0L, FALSE, 0L);
-        }
+        peffect_sickness(otmp);
         break;
     case POT_CONFUSION:
-        if (!Confusion) {
-            if (Hallucination) {
-                pline("What a trippy feeling!");
-                g.potion_unkn++;
-            } else
-                pline("Huh, What?  Where am I?");
-        } else
-            g.potion_nothing++;
-        make_confused(itimeout_incr(HConfusion,
-                                    rn1(7, 16 - 8 * bcsign(otmp))),
-                      FALSE);
+        peffect_confusion(otmp);
         break;
     case POT_GAIN_ABILITY:
-        if (otmp->cursed) {
-            pline("Ulch!  That potion tasted foul!");
-            g.potion_unkn++;
-        } else if (Fixed_abil) {
-            g.potion_nothing++;
-        } else {      /* If blessed, increase all; if not, try up to */
-            int itmp; /* 6 times to find one which can be increased. */
-
-            i = -1;   /* increment to 0 */
-            for (ii = A_MAX; ii > 0; ii--) {
-                i = (otmp->blessed ? i + 1 : rn2(A_MAX));
-                /* only give "your X is already as high as it can get"
-                   message on last attempt (except blessed potions) */
-                itmp = (otmp->blessed || ii == 1) ? 0 : -1;
-                if (adjattrib(i, 1, itmp) && !otmp->blessed)
-                    break;
-            }
-        }
+        peffect_gain_ability(otmp);
         break;
     case POT_SPEED:
-        /* skip when mounted; heal_legs() would heal steed's legs */
-        if (Wounded_legs && !otmp->cursed && !u.usteed) {
-            heal_legs(0);
-            g.potion_unkn++;
-            break;
-        }
-        /* FALLTHRU */
     case SPE_HASTE_SELF:
-        if (!Very_fast) { /* wwf@doe.carleton.ca */
-            You("are suddenly moving %sfaster.", Fast ? "" : "much ");
-        } else {
-            Your("%s get new energy.", makeplural(body_part(LEG)));
-            g.potion_unkn++;
-        }
-        exercise(A_DEX, TRUE);
-        incr_itimeout(&HFast, rn1(10, 100 + 60 * bcsign(otmp)));
+        peffect_speed(otmp);
         break;
     case POT_BLINDNESS:
-        if (Blind)
-            g.potion_nothing++;
-        make_blinded(itimeout_incr(Blinded,
-                                   rn1(200, 250 - 125 * bcsign(otmp))),
-                     (boolean) !Blind);
+        peffect_blindness(otmp);
         break;
     case POT_GAIN_LEVEL:
-        if (otmp->cursed) {
-            g.potion_unkn++;
-            /* they went up a level */
-            if ((ledger_no(&u.uz) == 1 && u.uhave.amulet)
-                || Can_rise_up(u.ux, u.uy, &u.uz)) {
-                const char *riseup = "rise up, through the %s!";
-
-                if (ledger_no(&u.uz) == 1) {
-                    You(riseup, ceiling(u.ux, u.uy));
-                    goto_level(&earth_level, FALSE, FALSE, FALSE);
-                } else {
-                    register int newlev = depth(&u.uz) - 1;
-                    d_level newlevel;
-
-                    get_level(&newlevel, newlev);
-                    if (on_level(&newlevel, &u.uz)) {
-                        pline("It tasted bad.");
-                        break;
-                    } else
-                        You(riseup, ceiling(u.ux, u.uy));
-                    goto_level(&newlevel, FALSE, FALSE, FALSE);
-                }
-            } else
-                You("have an uneasy feeling.");
-            break;
-        }
-        pluslvl(FALSE);
-        /* blessed potions place you at a random spot in the
-           middle of the new level instead of the low point */
-        if (otmp->blessed)
-            u.uexp = rndexp(TRUE);
+        peffect_gain_level(otmp);
         break;
     case POT_HEALING:
-        You_feel("better.");
-        healup(d(6 + 2 * bcsign(otmp), 4), !otmp->cursed ? 1 : 0,
-               !!otmp->blessed, !otmp->cursed);
-        exercise(A_CON, TRUE);
+        peffect_healing(otmp);
         break;
     case POT_EXTRA_HEALING:
-        You_feel("much better.");
-        healup(d(6 + 2 * bcsign(otmp), 8),
-               otmp->blessed ? 5 : !otmp->cursed ? 2 : 0, !otmp->cursed,
-               TRUE);
-        (void) make_hallucinated(0L, TRUE, 0L);
-        exercise(A_CON, TRUE);
-        exercise(A_STR, TRUE);
+        peffect_extra_healing(otmp);
         break;
     case POT_FULL_HEALING:
-        You_feel("completely healed.");
-        healup(400, 4 + 4 * bcsign(otmp), !otmp->cursed, TRUE);
-        /* Restore one lost level if blessed */
-        if (otmp->blessed && u.ulevel < u.ulevelmax) {
-            /* when multiple levels have been lost, drinking
-               multiple potions will only get half of them back */
-            u.ulevelmax -= 1;
-            pluslvl(FALSE);
-        }
-        (void) make_hallucinated(0L, TRUE, 0L);
-        exercise(A_STR, TRUE);
-        exercise(A_CON, TRUE);
+        peffect_full_healing(otmp);
         break;
     case POT_LEVITATION:
     case SPE_LEVITATION:
-        /*
-         * 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();
-            /* 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 */
-            g.potion_nothing++;
-
-        if (otmp->cursed) {
-            stairway *stway;
-
-            /* '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 ((stway = stairway_at(u.ux, u.uy)) != 0 && stway->up) {
-                (void) doup();
-                /* in case we're already Levitating, which would have
-                   resulted in incrementing 'nothing' */
-                g.potion_nothing = 0; /* not nothing after all */
-            } else if (has_ceiling(&u.uz)) {
-                int dmg = rnd(!uarmh ? 10 : !is_metallic(uarmh) ? 6 : 3);
-
-                You("hit your %s on the %s.", body_part(HEAD),
-                    ceiling(u.ux, u.uy));
-                losehp(Maybe_Half_Phys(dmg), "colliding with the ceiling",
-                       KILLED_BY);
-                g.potion_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 /* timeout is already at least 1 */
-            incr_itimeout(&HLevitation, rn1(140, 10));
-
-        if (Levitation && IS_SINK(levl[u.ux][u.uy].typ))
-            spoteffects(FALSE);
-        /* levitating blocks flying */
-        float_vs_flight();
+        peffect_levitation(otmp);
         break;
-    case POT_GAIN_ENERGY: { /* M. Stephenson */
-        int num;
-
-        if (otmp->cursed)
-            You_feel("lackluster.");
-        else
-            pline("Magical energies course through your body.");
-
-        /* old: num = rnd(5) + 5 * otmp->blessed + 1;
-         *      blessed:  +7..11 max & current (+9 avg)
-         *      uncursed: +2.. 6 max & current (+4 avg)
-         *      cursed:   -2.. 6 max & current (-4 avg)
-         * new: (3.6.0)
-         *      blessed:  +3..18 max (+10.5 avg), +9..54 current (+31.5 avg)
-         *      uncursed: +2..12 max (+ 7   avg), +6..36 current (+21   avg)
-         *      cursed:   -1.. 6 max (- 3.5 avg), -3..18 current (-10.5 avg)
-         */
-        num = d(otmp->blessed ? 3 : !otmp->cursed ? 2 : 1, 6);
-        if (otmp->cursed)
-            num = -num; /* subtract instead of add when cursed */
-        u.uenmax += num;
-        if (u.uenmax <= 0)
-            u.uenmax = 0;
-        u.uen += 3 * num;
-        if (u.uen > u.uenmax)
-            u.uen = u.uenmax;
-        else if (u.uen <= 0)
-            u.uen = 0;
-        g.context.botl = 1;
-        exercise(A_WIS, TRUE);
+    case POT_GAIN_ENERGY: /* M. Stephenson */
+        peffect_gain_energy(otmp);
         break;
-    }
-    case POT_OIL: { /* P. Winner */
-        boolean good_for_you = FALSE, vulnerable;
-
-        if (otmp->lamplit) {
-            if (likes_fire(g.youmonst.data)) {
-                pline("Ahh, a refreshing drink.");
-                good_for_you = TRUE;
-            } else {
-                /*
-                 * Note: if poly'd into green slime, hero ought to take
-                 * extra damage, but drinking potions in that form isn't
-                 * possible so there's no need to try to handle that.
-                 */
-                You("burn your %s.", body_part(FACE));
-                /* fire damage */
-                vulnerable = !Fire_resistance || Cold_resistance;
-                losehp(d(vulnerable ? 4 : 2, 4),
-                       "quaffing a burning potion of oil",
-                       KILLED_BY);
-            }
-            /*
-             * This is slightly iffy because the burning isn't being
-             * spread across the body.  But the message is "the slime
-             * that covers you burns away" and having that follow
-             * "you burn your face" seems consistent enough.
-             */
-            burn_away_slime();
-        } else if (otmp->cursed) {
-            pline("This tastes like castor oil.");
-        } else {
-            pline("That was smooth!");
-        }
-        exercise(A_WIS, good_for_you);
+    case POT_OIL: /* P. Winner */
+        peffect_oil(otmp);
         break;
-    }
     case POT_ACID:
-        if (Acid_resistance) {
-            /* Not necessarily a creature who _likes_ acid */
-            pline("This tastes %s.", Hallucination ? "tangy" : "sour");
-        } else {
-            int dmg;
-
-            pline("This burns%s!",
-                  otmp->blessed ? " a little" : otmp->cursed ? " a lot"
-                                                             : " like acid");
-            dmg = d(otmp->cursed ? 2 : 1, otmp->blessed ? 4 : 8);
-            losehp(Maybe_Half_Phys(dmg), "potion of acid", KILLED_BY_AN);
-            exercise(A_CON, FALSE);
-        }
-        if (Stoned)
-            fix_petrification();
-        g.potion_unkn++; /* holy/unholy water can burn like acid too */
+        peffect_acid(otmp);
         break;
     case POT_POLYMORPH:
-        You_feel("a little %s.", Hallucination ? "normal" : "strange");
-        if (!Unchanging)
-            polyself(0);
+        peffect_polymorph(otmp);
         break;
     default:
         impossible("What a funny potion! (%u)", otmp->otyp);