]> granicus.if.org Git - nethack/commitdiff
fix #2242 and #2292 - levitation exceptions (trunk only)
authornethack.rankin <nethack.rankin>
Sat, 15 Oct 2011 03:00:45 +0000 (03:00 +0000)
committernethack.rankin <nethack.rankin>
Sat, 15 Oct 2011 03:00:45 +0000 (03:00 +0000)
     From a bug report.  The first report complained about levitation
allowing you to move through water on the Plane of Water, something that's
come up in the newsgroup lots of times (mostly about how levitation is
the best way to get around, only occasionally wondering why it works:
water walking doesn't work there because there's no surface, so where are
you levitating such that you're kept dry?)  The second report complained
about being told you were floating up if you put on a ring of levitation
while stuck inside a wall (perhaps after being stranded when polymorph
into xorn form ended).

     This implements intrinsic blocking for levitation and also for
flying.  Being inside solid rock (or closed door) anywhere and being in
water on the Plane of Water are the things that do it for levitation;
those two and levitating are what do it for flying.  Entering such
terrain turns off ability to float/fly, and leaving there turns it back
on; starting levitation blocks flight, ending it unblocks (levitation
has always overridden flying's ability to reach the floor).  Being able
to phase through rock doesn't prevent levitation and flight from being
blocked while in rock; you aren't floating or flying in that situation.

doc/fixes35.0
include/youprop.h
src/cmd.c
src/do_wear.c
src/hack.c
src/polyself.c
src/potion.c
src/trap.c

index e237863350c1324322880efb0287b54cc22d9add..af2e71856beb9098a90496a866ced47c1a1b8ae7 100644 (file)
@@ -392,6 +392,8 @@ if hero was blind, killing the vault guard while in his temporary corridor
        would leave hero encased in solid rock without informing player
 if hero dragged iron ball into temporary corridor and then killed vault guard,
        the portion of corridor currently in existence would become permanent
+on Plane of Water, restrict levitation and flying to air bubbles;
+       elsewhere, restrict them such that they don't work inside solid rock
 
 
 Platform- and/or Interface-Specific Fixes
index 19fe7a73e215430debd4651b2ef46f5ec72341f0..be82f0af9babdf37f0509e01b8cafec8ae3d82b2 100644 (file)
 
 #define HLevitation            u.uprops[LEVITATION].intrinsic
 #define ELevitation            u.uprops[LEVITATION].extrinsic
-#define Levitation             (HLevitation || ELevitation)
+#define BLevitation            u.uprops[LEVITATION].blocked
+#define Levitation             ((HLevitation || ELevitation) && !BLevitation)
        /* Can't touch surface, can't go under water; overrides all others */
 #define Lev_at_will            (((HLevitation & I_SPECIAL) != 0L || \
                                 (ELevitation & W_ARTI) != 0L) && \
 
 #define HFlying                        u.uprops[FLYING].intrinsic
 #define EFlying                        u.uprops[FLYING].extrinsic
+#define BFlying                        u.uprops[FLYING].blocked
 #ifdef STEED
-# define Flying                        (HFlying || EFlying || \
-                                (u.usteed && is_flyer(u.usteed->data)))
+# define Flying                        ((HFlying || EFlying || \
+                                 (u.usteed && is_flyer(u.usteed->data))) && \
+                                !BFlying)
 #else
-# define Flying                        (HFlying || EFlying)
+# define Flying                        ((HFlying || EFlying) && !BFlying)
 #endif
        /* May touch surface; does not override any others */
 
index fd7df4d9e85f94faf4dfee6911ac17227fd252a3..3feac20cfd6704d62d1ef3d5fffc907c8db4a70b 100644 (file)
--- a/src/cmd.c
+++ b/src/cmd.c
@@ -1415,12 +1415,8 @@ int final;
                you_are("levitating, at will", "");
            else
                enl_msg(youtoo, are, were, "levitating", from_what(LEVITATION));
-       }
-       if (Flying) {
-           if (Levitation)
-               you_can("also fly", from_what(FLYING));
-           else
-               enl_msg(youtoo, are, were, "flying", from_what(FLYING));
+       } else if (Flying) {    /* can only fly when not levitating */
+           enl_msg(youtoo, are, were, "flying", from_what(FLYING));
        }
        if (Underwater) {
            you_are("underwater", "");
@@ -1621,6 +1617,8 @@ attributes_enlightenment(mode, final)
 int mode;
 int final;
 {
+       static NEARDATA const char
+           if_surroundings_permitted[] = " if surroundings permitted";
        int ltmp, armpro;
        char buf[BUFSZ];
 
@@ -1762,6 +1760,33 @@ int final;
        if (Teleportation) you_can("teleport",from_what(TELEPORT));
        if (Teleport_control)
                you_have("teleport control",from_what(TELEPORT_CONTROL));
+       /* actively levitating handled earlier as a status condition */
+       if (BLevitation) {      /* levitation is blocked */
+           long save_BLev = BLevitation;
+
+           BLevitation = 0L;
+           if (Levitation)
+               enl_msg(You_, "would levitate", "would have levitated",
+                       if_surroundings_permitted, "");
+           BLevitation = save_BLev;
+       }
+       /* actively flying handled earlier as a status condition */
+       if (BFlying) {          /* flight is blocked */
+           long save_BFly = BFlying;
+
+           BFlying = 0L;
+           if (Flying) {
+               Sprintf(buf, "%s%s%s",
+                       (save_BFly & I_SPECIAL) ?
+                               " if you weren't levitating" : "",
+                       ((save_BFly & (FROMOUTSIDE|I_SPECIAL)) ==
+                               (FROMOUTSIDE|I_SPECIAL)) ? " and" : "",
+                       (save_BFly & FROMOUTSIDE) ?
+                               if_surroundings_permitted : (const char *)"");
+               enl_msg(You_, "would fly", "would have flown", buf, "");
+           }
+           BFlying = save_BFly;
+       }
        /* actively walking on water handled earlier as a status condition */
        if (Wwalking && !walking_on_water())
            you_can("walk on water",from_what(WWALKING));
index a5d8858f1dfab7f18cc3d795f14c7851c44636c6..9555671039a807e31da55b5d7d4c6f18c8ec6617 100644 (file)
@@ -190,7 +190,7 @@ Boots_on(VOID_ARGS)
                        incr_itimeout(&HFumbling, rnd(20));
                break;
        case LEVITATION_BOOTS:
-               if (!oldprop && !HLevitation) {
+               if (!oldprop && !HLevitation && !BLevitation) {
                        makeknown(uarmf->otyp);
                        float_up();
                        spoteffects(FALSE);
@@ -241,7 +241,8 @@ Boots_off(VOID_ARGS)
                        HFumbling = EFumbling = 0;
                break;
        case LEVITATION_BOOTS:
-               if (!oldprop && !HLevitation && !context.takeoff.cancelled_don) {
+               if (!oldprop && !HLevitation && !BLevitation &&
+                   !context.takeoff.cancelled_don) {
                        (void) float_down(0L, 0L);
                        makeknown(otyp);
                }
@@ -875,7 +876,7 @@ register struct obj *obj;
                }
                break;
        case RIN_LEVITATION:
-               if (!oldprop && !HLevitation) {
+               if (!oldprop && !HLevitation && !BLevitation) {
                    float_up();
                    learnring(obj, TRUE);
                    spoteffects(FALSE); /* for sinks */
@@ -987,8 +988,10 @@ boolean gone;
                }
                break;
        case RIN_LEVITATION:
-               (void) float_down(0L, 0L);
-               if (!Levitation) learnring(obj, TRUE);
+               if (!BLevitation) {
+                   (void) float_down(0L, 0L);
+                   if (!Levitation) learnring(obj, TRUE);
+               }
                break;
        case RIN_GAIN_STRENGTH:
                which = A_STR;
index 9cd0d4b15c4e0b530aeb7968406c8c610489cfbd..65526c64a74320b80348a5e84dae8612603cd1a7 100644 (file)
@@ -14,6 +14,7 @@ STATIC_DCL void NDECL(dosinkfall);
 #endif
 STATIC_DCL boolean FDECL(findtravelpath, (BOOLEAN_P));
 STATIC_DCL boolean FDECL(trapmove, (int,int,struct trap *));
+STATIC_DCL void NDECL(switch_terrain);
 STATIC_DCL struct monst *FDECL(monstinroom, (struct permonst *,int));
 STATIC_DCL boolean FDECL(doorless_door, (int,int));
 STATIC_DCL void FDECL(move_update, (BOOLEAN_P));
@@ -1640,6 +1641,40 @@ invocation_message()
        }
 }
 
+/* moving onto different terrain;
+   might be going into solid rock, inhibiting levitation or flight,
+   or coming back out of such, reinstating levitation/flying */
+STATIC_OVL void
+switch_terrain()
+{
+    struct rm *lev = &levl[u.ux][u.uy];
+    boolean blocklev = (IS_ROCK(lev->typ) || closed_door(u.ux, u.uy) ||
+                       (Is_waterlevel(&u.uz) && lev->typ == WATER));
+
+    if (blocklev) {
+       /* called from spoteffects(), skip float_down() */
+       if (Levitation) You_cant("levitate in here.");
+       BLevitation |= FROMOUTSIDE;
+    } else if (BLevitation) {
+       BLevitation &= ~FROMOUTSIDE;
+       if (Levitation) float_up();
+    }
+    /* the same terrain that blocks levitation also blocks flight */
+    if (blocklev) {
+       if (Flying) You_cant("fly in here.");
+       BFlying |= FROMOUTSIDE;
+    } else if (BFlying) {
+       BFlying &= ~FROMOUTSIDE;
+       /* in case BFlying got set due to levitation which then went away
+          while blocked; there'd be no float_down() with reset of BFlying */
+       if (!HLevitation && !ELevitation) BFlying &= ~I_SPECIAL;
+       /* [minor bug: we don't know whether this is beginning flight or
+          resuming it; that could be tracked so that this message could
+          be adjusted to "resume flying", but isn't worth the effort...] */
+       if (Flying) You("start flying.");
+    }
+}
+
 /* extracted from spoteffects; called by spoteffects to check for entering or
    leaving a pool of water/lava, and by moveloop to check for staying on one */
 boolean
@@ -1741,6 +1776,10 @@ boolean pick;
        spotterrain = levl[u.ux][u.uy].typ;
        spotloc.x = u.ux, spotloc.y = u.uy;
 
+       /* moving onto different terrain might cause Levitation to toggle */
+       if (spotterrain != levl[u.ux0][u.uy0].typ || !on_level(&u.uz, &u.uz0))
+           switch_terrain();
+
        if (pooleffects(TRUE)) goto spotdone;
 
        check_special_room(FALSE);
index 16fc9f0ac876862fbaf4284d9e0994d4e535aa3e..66b95b3a047b4c5985ae135728255fa55cd8d33e 100644 (file)
@@ -88,6 +88,11 @@ set_uasmon()
        PROPSET(PASSES_WALLS, passes_walls(mdat));
        PROPSET(REGENERATION, regenerates(mdat));
        PROPSET(REFLECTING, (mdat == &mons[PM_SILVER_DRAGON]));
+       /* levitation overrides flight */
+       if (HLevitation || ELevitation)
+           BFlying |= I_SPECIAL;
+       else
+           BFlying &= ~I_SPECIAL;
 
 #undef PROPSET
 
index a0ad86e4c85551d9de62f9ded8d8244aee2be70f..7a4413e754dfe2eec166d630cd771ef56f957b42 100644 (file)
@@ -904,33 +904,36 @@ peffects(otmp)
        case POT_LEVITATION:
        case SPE_LEVITATION:
                if (otmp->cursed) HLevitation &= ~I_SPECIAL;
-               if(!Levitation) {
-                       /* kludge to ensure proper operation of float_up() */
-                       set_itimeout(&HLevitation, 1L);
-                       float_up();
-                       /* reverse kludge */
-                       set_itimeout(&HLevitation, 0L);
-                       if (otmp->cursed && !Is_waterlevel(&u.uz)) {
-       if((u.ux != xupstair || u.uy != yupstair)
-          && (u.ux != sstairs.sx || u.uy != sstairs.sy || !sstairs.up)
-          && (!xupladder || u.ux != xupladder || u.uy != yupladder)
-       ) {
-                                       int dmg = uarmh ? 1 : rnd(10);
-                                       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);
-                               } else (void) doup();
+               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);
+
+                           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);
                        }
+                   } /*cursed*/
                } else
-                       nothing++;
+                   nothing++;
                if (otmp->blessed) {
                    incr_itimeout(&HLevitation, rn1(50,250));
                    HLevitation |= I_SPECIAL;
-               } else incr_itimeout(&HLevitation, rn1(140,10));
-               spoteffects(FALSE);     /* for sinks */
+               } else
+                   incr_itimeout(&HLevitation, rn1(140,10));
+               if (Levitation) spoteffects(FALSE);     /* for sinks */
                break;
        case POT_GAIN_ENERGY:                   /* M. Stephenson */
                {       register int num;
index 7d383e9fe180629779b45b95295837f75c6f8191..0982cb7de3a88e2b989d420df910321ec85e97d1 100644 (file)
@@ -2609,8 +2609,10 @@ float_up()
                                body_part(LEG));
                }
        }
+#if 0
        else if(Is_waterlevel(&u.uz))
                pline("It feels as though you've lost some weight.");
+#endif
        else if(u.uinwater)
                spoteffects(TRUE);
        else if(u.uswallow)
@@ -2637,6 +2639,8 @@ float_up()
            }
        }
 #endif
+       if (Flying) You("are no longer able to control your flight.");
+       BFlying |= I_SPECIAL;
        return;
 }
 
@@ -2667,6 +2671,14 @@ long hmask, emask;     /* might cancel timeout */
        ELevitation &= ~emask;
        if(Levitation) return(0); /* maybe another ring/potion/boots */
        nomul(0);       /* stop running or resting */
+       if (BFlying) {
+           /* controlled flight no longer overridden by levitation */
+           BFlying &= ~I_SPECIAL;
+           if (Flying) {
+               You("have stopped levitating and are now flying.");
+               return 1;
+           }
+       }
        if(u.uswallow) {
            You("float down, but you are still %s.",
                is_animal(u.ustuck->data) ? "swallowed" : "engulfed");