]> granicus.if.org Git - nethack/commitdiff
Rework stairs structure
authorPasi Kallinen <paxed@alt.org>
Mon, 9 Nov 2020 16:50:02 +0000 (18:50 +0200)
committerPasi Kallinen <paxed@alt.org>
Fri, 13 Nov 2020 18:27:17 +0000 (20:27 +0200)
Use a linked list to store stair and ladder information, instead
of having fixed up/down stairs/ladders and a single "special" (branch)
stair.

Breaks saves and bones.

Adds information to migrating objects and monsters for the dungeon
and level where they are migrating from.

34 files changed:
doc/fixes37.0
include/decl.h
include/dungeon.h
include/extern.h
include/monst.h
include/obj.h
include/patchlevel.h
include/rm.h
src/allmain.c
src/apply.c
src/cmd.c
src/decl.c
src/dig.c
src/do.c
src/dog.c
src/dokick.c
src/dungeon.c
src/invent.c
src/mail.c
src/makemon.c
src/mklev.c
src/mkmaze.c
src/mkobj.c
src/mkroom.c
src/mon.c
src/muse.c
src/potion.c
src/restore.c
src/save.c
src/shk.c
src/sp_lev.c
src/teleport.c
src/wizard.c
src/zap.c

index 10cead1e50870fd5c2f7dbf929f104ce63e62c5c..34885e5c8fbf39c8237156aa23fb8688910eca9a 100644 (file)
@@ -706,4 +706,4 @@ get rid of 3.6.1 workaround needed to retain compatibility with 3.6.0 bones
 add an additional note to mextra.h and obj.h comments that reminds people to
        appropriately init new fields if they need to initialize to something
        other than zero
-
+rework stairs structure into a linked list
index c72ab57bffbdf4f8d4e4dc0d9af5a7d5186c6f11..dc1039c22c785d67f7c20cd3a41e5272421870c8 100644 (file)
@@ -75,16 +75,6 @@ struct dgn_topology { /* special dungeon levels for speed */
 #define sokoend_level           (g.dungeon_topology.d_sokoend_level)
 /* clang-format on */
 
-#define xdnstair (g.dnstair.sx)
-#define ydnstair (g.dnstair.sy)
-#define xupstair (g.upstair.sx)
-#define yupstair (g.upstair.sy)
-
-#define xdnladder (g.dnladder.sx)
-#define ydnladder (g.dnladder.sy)
-#define xupladder (g.upladder.sx)
-#define yupladder (g.upladder.sy)
-
 #define dunlev_reached(x) (g.dungeons[(x)->dnum].dunlev_ureached)
 
 #include "quest.h"
@@ -726,10 +716,7 @@ struct instance_globals {
     int y_maze_max;
     int otg_temp; /* used by object_to_glyph() [otg] */
     int in_doagain;
-    stairway dnstair; /* stairs down */
-    stairway upstair; /* stairs up */
-    stairway dnladder; /* ladder down */
-    stairway upladder; /* ladder up */
+    stairway *stairs;
     int smeq[MAXNROFROOMS + 1];
     int doorindex;
     char *save_cm;
@@ -754,7 +741,6 @@ struct instance_globals {
        number of shots, index of current one, validity check, shoot vs throw */
     struct multishot m_shot;
     dungeon dungeons[MAXDUNGEON]; /* ini'ed by init_dungeon() */
-    stairway sstairs;
     dest_area updest;
     dest_area dndest;
     coord inv_pos;
@@ -765,9 +751,6 @@ struct instance_globals {
     boolean mrg_to_wielded; /* weapon picked is merged with wielded one */
     struct plinemsg_type *plinemsg_types;
     char toplines[TBUFSZ];
-    struct mkroom *upstairs_room;
-    struct mkroom *dnstairs_room;
-    struct mkroom *sstairs_room;
     coord bhitpos; /* place where throw or zap hits or stops */
     boolean in_steed_dismounting;
     coord doors[DOORMAX];
index 2a135c62f69b087c079ca00409bcb00ac811b1b9..7a07e58f449e8319e11991fb1a5f3985df97a90a 100644 (file)
@@ -34,7 +34,9 @@ typedef struct s_level { /* special dungeon level element */
 typedef struct stairway { /* basic stairway identifier */
     xchar sx, sy;         /* x / y location of the stair */
     d_level tolev;        /* where does it go */
-    char up;              /* what type of stairway (up/down) */
+    boolean up;           /* up or down? */
+    boolean isladder;     /* ladder or stairway? */
+    struct stairway *next;
 } stairway;
 
 /* level region types */
index e24e605b6e3f33df5f1c6675edff86bbe887bf16..9280b47b6f494dd2306245d13218876d0915f3f7 100644 (file)
@@ -630,6 +630,15 @@ E void FDECL(next_level, (BOOLEAN_P));
 E void FDECL(prev_level, (BOOLEAN_P));
 E void FDECL(u_on_newpos, (int, int));
 E void FDECL(u_on_rndspot, (int));
+E void FDECL(stairway_add, (int,int, BOOLEAN_P, BOOLEAN_P, d_level *));
+E void NDECL(stairway_print);
+E void NDECL(stairway_free_all);
+E stairway *FDECL(stairway_at, (int, int));
+E stairway *FDECL(stairway_find, (d_level *));
+E stairway *FDECL(stairway_find_from, (d_level *, BOOLEAN_P));
+E stairway *FDECL(stairway_find_dir, (BOOLEAN_P));
+E stairway *FDECL(stairway_find_type_dir, (BOOLEAN_P, BOOLEAN_P));
+E stairway *FDECL(stairway_find_special_dir, (BOOLEAN_P));
 E void FDECL(u_on_sstairs, (int));
 E void NDECL(u_on_upstairs);
 E void NDECL(u_on_dnstairs);
index eaecd34a74a1f9dd1a50620bf6bc6675e3edb7da..6842162ca0181b610e425ca5e7f2ea4a18ea9ee8 100644 (file)
@@ -82,6 +82,7 @@ struct monst {
     xchar mx, my;
     xchar mux, muy;       /* where the monster thinks you are */
 #define MTSZ 4
+    /* mtrack[0..2] is used to keep extra data when migrating the monster */
     coord mtrack[MTSZ];   /* monster track */
     int mhp, mhpmax;
     unsigned mappearance; /* for undetected mimics and the wiz */
index 3504992d6cbefc1c5ea7cf4ec129413e5053ac93..4b6f6d5c8f15cd9deacab65537e720ea36d866a6 100644 (file)
@@ -122,6 +122,8 @@ struct obj {
     long age;               /* creation date */
     long owornmask;
     unsigned lua_ref_cnt;  /* # of lua script references for this object */
+    xchar omigr_from_dnum; /* where obj is migrating from */
+    xchar omigr_from_dlevel; /* where obj is migrating from */
     struct oextra *oextra; /* pointer to oextra struct */
 };
 
index adf03ba79bab0d38a89afae348b9489cdee4dcc9..0ca768ddb1d33522963052492b83b4c28ba46550 100644 (file)
@@ -17,7 +17,7 @@
  * Incrementing EDITLEVEL can be used to force invalidation of old bones
  * and save files.
  */
-#define EDITLEVEL 24
+#define EDITLEVEL 25
 
 /*
  * Development status possibilities.
index fbaa8a388496d9f290e78afcaf311a683bb72cbe..b555124394b93773c2ca931f90b92146cb689fa7 100644 (file)
@@ -243,6 +243,8 @@ enum screen_symbols {
 #define is_cmap_furniture(i) ((i) >= S_upstair && (i) <= S_fountain)
 #define is_cmap_water(i) ((i) == S_pool || (i) == S_water)
 #define is_cmap_lava(i) ((i) == S_lava)
+#define is_cmap_stairs(i) ((i) == S_upstair || (i) == S_dnstair || \
+                           (i) == S_upladder || (i) == S_dnladder)
 
 
 struct symdef {
index a11919b854c40b775752aec20c95cf83037407d4..d015e762144e6baa21f5b9a1266aefefc9fbe7bf 100644 (file)
@@ -712,43 +712,21 @@ do_positionbar()
 {
     static char pbar[COLNO];
     char *p;
+    stairway *stway = g.stairs;
 
     p = pbar;
-    /* up stairway */
-    if (g.upstair.sx
-        && (glyph_to_cmap(g.level.locations[g.upstair.sx][g.upstair.sy].glyph)
-                == S_upstair
-            || glyph_to_cmap(g.level.locations[g.upstair.sx][g.upstair.sy].glyph)
-                   == S_upladder)) {
-        *p++ = '<';
-        *p++ = g.upstair.sx;
-    }
-    if (g.sstairs.sx
-        && (glyph_to_cmap(g.level.locations[g.sstairs.sx][g.sstairs.sy].glyph)
-                == S_upstair
-            || glyph_to_cmap(g.level.locations[g.sstairs.sx][g.sstairs.sy].glyph)
-                   == S_upladder)) {
-        *p++ = '<';
-        *p++ = g.sstairs.sx;
-    }
-
-    /* down stairway */
-    if (g.dnstair.sx
-        && (glyph_to_cmap(g.level.locations[g.dnstair.sx][g.dnstair.sy].glyph)
-                == S_dnstair
-            || glyph_to_cmap(g.level.locations[g.dnstair.sx][g.dnstair.sy].glyph)
-                   == S_dnladder)) {
-        *p++ = '>';
-        *p++ = g.dnstair.sx;
-    }
-    if (g.sstairs.sx
-        && (glyph_to_cmap(g.level.locations[g.sstairs.sx][g.sstairs.sy].glyph)
-                == S_dnstair
-            || glyph_to_cmap(g.level.locations[g.sstairs.sx][g.sstairs.sy].glyph)
-                   == S_dnladder)) {
-        *p++ = '>';
-        *p++ = g.sstairs.sx;
-    }
+    /* TODO: use the same method as getpos() so objects don't cover stairs */
+    while (stway) {
+        int x = stway->sx;
+        int y = stway->sy;
+        int glyph = glyph_to_cmap(g.level.locations[x][y].glyph);
+
+        if (is_cmap_stairs(glyph)) {
+            *p++ = (stway->up ? '<' : '>');
+            *p++ = stway->sx;
+        }
+        stway = stway->next;
+     }
 
     /* hero location */
     if (u.ux) {
index d5f8a17970296294a57debcf2a6bc25fa0e902dd..a7cc2d82cda483b7ef57b2b29c093805ff0974a0 100644 (file)
@@ -2556,10 +2556,10 @@ struct obj *otmp;
         what = "in water";
     else if (is_lava(u.ux, u.uy))
         what = "in lava";
-    else if (On_stairs(u.ux, u.uy))
-        what = (u.ux == xdnladder || u.ux == xupladder) ? "on the ladder"
-                                                        : "on the stairs";
-    else if (IS_FURNITURE(levtyp) || IS_ROCK(levtyp)
+    else if (On_stairs(u.ux, u.uy)) {
+        stairway *stway = stairway_at(u.ux, u.uy);
+        what = stway->isladder ? "on the ladder" : "on the stairs";
+    else if (IS_FURNITURE(levtyp) || IS_ROCK(levtyp)
              || closed_door(u.ux, u.uy) || t_at(u.ux, u.uy))
         what = "here";
     else if (Is_airlevel(&u.uz) || Is_waterlevel(&u.uz))
index b870f0d464d9623a1d2ce97efee50ce0adcaf381..c6561ee198ee364fcc91e81b2b67b8c27867da4a 100644 (file)
--- a/src/cmd.c
+++ b/src/cmd.c
@@ -3958,6 +3958,7 @@ boolean doit;
     schar typ = levl[u.ux][u.uy].typ;
     int npick;
     menu_item *picks = (menu_item *) 0;
+    stairway *stway = stairway_at(u.ux, u.uy);
 
     win = create_nhwindow(NHW_MENU);
     start_menu(win, MENU_BEHAVE_STANDARD);
@@ -3974,16 +3975,14 @@ boolean doit;
         add_herecmd_menuitem(win, dosit,
                              "Sit on the throne");
 
-    if (On_stairs_up(u.ux, u.uy)) {
+    if (stway && stway->up) {
         Sprintf(buf, "Go up the %s",
-                (u.ux == xupladder && u.uy == yupladder)
-                ? "ladder" : "stairs");
+                stway->isladder ? "ladder" : "stairs");
         add_herecmd_menuitem(win, doup, buf);
     }
-    if (On_stairs_dn(u.ux, u.uy)) {
+    if (stway && !stway->up) {
         Sprintf(buf, "Go down the %s",
-                (u.ux == xupladder && u.uy == yupladder)
-                ? "ladder" : "stairs");
+                stway->isladder ? "ladder" : "stairs");
         add_herecmd_menuitem(win, dodown, buf);
     }
     if (u.usteed) { /* another movement choice */
index 5f44d64e15ae869d28a16b8d165d9648aea5e4bf..54d38ac6d7d417f7076bbf9bfbcb6e4d3737f389 100644 (file)
@@ -270,10 +270,7 @@ const struct instance_globals g_init = {
     (ROWNO - 1) & ~1, /* y_maze_max */
     UNDEFINED_VALUE, /* otg_temp */
     0, /* in_doagain */
-    DUMMY, /* dnstair */
-    DUMMY, /* upstair */
-    DUMMY, /* dnladder */
-    DUMMY, /* upladder */
+    NULL, /* stairs */
     DUMMY, /* smeq */
     0, /* doorindex */
     NULL, /* save_cm */
@@ -294,7 +291,6 @@ const struct instance_globals g_init = {
     UNDEFINED_PTR, /* sp_levchn */
     { 0, 0, STRANGE_OBJECT, FALSE }, /* m_shot */
     UNDEFINED_VALUES, /* dungeons */
-    { 0, 0, { 0, 0 }, 0 }, /* sstairs */
     { 0, 0, 0, 0, 0, 0, 0, 0 }, /* updest */
     { 0, 0, 0, 0, 0, 0, 0, 0 }, /* dndest */
     { 0, 0} , /* inv_pos */
@@ -305,9 +301,6 @@ const struct instance_globals g_init = {
     FALSE, /* mrg_to_wielded */
     NULL, /* plinemsg_types */
     UNDEFINED_VALUES, /* toplines */
-    UNDEFINED_PTR, /* upstairs_room */
-    UNDEFINED_PTR, /* dnstairs_room */
-    UNDEFINED_PTR, /* sstairs_room */
     DUMMY, /* bhitpos */
     FALSE, /* in_steed_dismounting */
     DUMMY, /* doors */
index 05369498f26010331953deb5b911a1859717bb8c..7c3e603c269e6d8f1934ce9196f3a6ba6869dba0 100644 (file)
--- a/src/dig.c
+++ b/src/dig.c
@@ -188,7 +188,8 @@ int x, y;
         (madeby == BY_YOU && uwep && is_axe(uwep)) ? "chop" : "dig in";
 
     if (On_stairs(x, y)) {
-        if (x == xdnladder || x == xupladder) {
+        stairway *stway = stairway_at(x, y);
+        if (stway->isladder) {
             if (verbose)
                 pline_The("ladder resists your effort.");
         } else if (verbose)
@@ -1431,12 +1432,12 @@ zap_dig()
         if (!Is_airlevel(&u.uz) && !Is_waterlevel(&u.uz) && !Underwater) {
             if (u.dz < 0 || On_stairs(u.ux, u.uy)) {
                 int dmg;
-                if (On_stairs(u.ux, u.uy))
+                if (On_stairs(u.ux, u.uy)) {
+                    stairway *stway = stairway_at(u.ux, u.uy);
                     pline_The("beam bounces off the %s and hits the %s.",
-                              (u.ux == xdnladder || u.ux == xupladder)
-                                  ? "ladder"
-                                  : "stairs",
+                              stway->isladder ? "ladder" : "stairs",
                               ceiling(u.ux, u.uy));
+                }
                 You("loosen a rock from the %s.", ceiling(u.ux, u.uy));
                 pline("It falls on your %s!", body_part(HEAD));
                 dmg = rnd((uarmh && is_metallic(uarmh)) ? 2 : 6);
index ee2c3c14187254161d3ac37cae94297df95826fc..a98417fa330c4ad4f787ad2de8fa9a65d30eee39 100644 (file)
--- a/src/do.c
+++ b/src/do.c
@@ -950,10 +950,9 @@ int
 dodown()
 {
     struct trap *trap = 0;
-    boolean stairs_down = ((u.ux == xdnstair && u.uy == ydnstair)
-                           || (u.ux == g.sstairs.sx && u.uy == g.sstairs.sy
-                               && !g.sstairs.up)),
-            ladder_down = (u.ux == xdnladder && u.uy == ydnladder);
+    stairway *stway = stairway_at(u.ux, u.uy);
+    boolean stairs_down = (stway && !stway->up && !stway->isladder),
+            ladder_down = (stway && !stway->up &&  stway->isladder);
 
     if (u_rooted())
         return 1;
@@ -1105,6 +1104,8 @@ dodown()
 int
 doup()
 {
+    stairway *stway = stairway_at(u.ux,u.uy);
+
     if (u_rooted())
         return 1;
 
@@ -1114,10 +1115,7 @@ doup()
         return 1;
     }
 
-    if ((u.ux != xupstair || u.uy != yupstair)
-        && (!xupladder || u.ux != xupladder || u.uy != yupladder)
-        && (!g.sstairs.sx || u.ux != g.sstairs.sx || u.uy != g.sstairs.sy
-            || !g.sstairs.up)) {
+    if (!stway || (stway && !stway->up)) {
         You_cant("go up here.");
         return 0;
     }
@@ -1458,6 +1456,7 @@ boolean at_stairs, falling, portal;
             dunlev_reached(&u.uz) = dunlev(&u.uz);
     }
 
+    stairway_free_all();
     /* set default level change destination areas */
     /* the special level code may override these */
     (void) memset((genericptr_t) &g.updest, 0, sizeof g.updest);
@@ -1528,8 +1527,9 @@ boolean at_stairs, falling, portal;
         }
     } else if (at_stairs && !In_endgame(&u.uz)) {
         if (up) {
-            if (g.at_ladder)
-                u_on_newpos(xdnladder, ydnladder);
+            stairway *stway = stairway_find_from(&u.uz0, g.at_ladder);
+            if (stway)
+                u_on_newpos(stway->sx, stway->sy);
             else if (newdungeon)
                 u_on_sstairs(1);
             else
@@ -1544,8 +1544,9 @@ boolean at_stairs, falling, portal;
                       (Flying && g.at_ladder) ? " along" : "",
                       g.at_ladder ? "ladder" : "stairs");
         } else { /* down */
-            if (g.at_ladder)
-                u_on_newpos(xupladder, yupladder);
+            stairway *stway = stairway_find_from(&u.uz0, g.at_ladder);
+            if (stway)
+                u_on_newpos(stway->sx, stway->sy);
             else if (newdungeon)
                 u_on_sstairs(0);
             else
index 14a6b19921247e8be990b1c6b3f77ed00d0d4ac0..450996168fe707707717a7f7c1b9dca53ceecc4d 100644 (file)
--- a/src/dog.c
+++ b/src/dog.c
@@ -306,6 +306,8 @@ boolean with_you;
     xchar xlocale, ylocale, xyloc, xyflags, wander;
     int num_segs;
     boolean failed_to_place = FALSE;
+    stairway *stway;
+    d_level fromdlev;
 
     mtmp->nmon = fmon;
     fmon = mtmp;
@@ -331,6 +333,8 @@ boolean with_you;
     xyflags = mtmp->mtrack[0].y;
     xlocale = mtmp->mtrack[1].x;
     ylocale = mtmp->mtrack[1].y;
+    fromdlev.dnum = mtmp->mtrack[2].x;
+    fromdlev.dlevel = mtmp->mtrack[2].y;
     memset(mtmp->mtrack, 0, sizeof mtmp->mtrack);
 
     if (mtmp == u.usteed)
@@ -376,19 +380,34 @@ boolean with_you;
         xlocale = u.ux, ylocale = u.uy;
         break;
     case MIGR_STAIRS_UP:
-        xlocale = xupstair, ylocale = yupstair;
+        if ((stway = stairway_find_from(&fromdlev, FALSE)) != 0) {
+            xlocale = stway->sx;
+            ylocale = stway->sy;
+        }
         break;
     case MIGR_STAIRS_DOWN:
-        xlocale = xdnstair, ylocale = ydnstair;
+        if ((stway = stairway_find_from(&fromdlev, FALSE)) != 0) {
+            xlocale = stway->sx;
+            ylocale = stway->sy;
+        }
         break;
     case MIGR_LADDER_UP:
-        xlocale = xupladder, ylocale = yupladder;
+        if ((stway = stairway_find_from(&fromdlev, TRUE)) != 0) {
+            xlocale = stway->sx;
+            ylocale = stway->sy;
+        }
         break;
     case MIGR_LADDER_DOWN:
-        xlocale = xdnladder, ylocale = ydnladder;
+        if ((stway = stairway_find_from(&fromdlev, TRUE)) != 0) {
+            xlocale = stway->sx;
+            ylocale = stway->sy;
+        }
         break;
     case MIGR_SSTAIRS:
-        xlocale = g.sstairs.sx, ylocale = g.sstairs.sy;
+        if ((stway = stairway_find(&fromdlev)) != 0) {
+            xlocale = stway->sx;
+            ylocale = stway->sy;
+        }
         break;
     case MIGR_PORTAL:
         if (In_endgame(&u.uz)) {
@@ -719,6 +738,8 @@ coord *cc;   /* optional destination coordinates */
         xyflags |= 2;
     mtmp->wormno = num_segs;
     mtmp->mlstmv = g.monstermoves;
+    mtmp->mtrack[2].x = u.uz.dnum; /* migrating from this dungeon */
+    mtmp->mtrack[2].y = u.uz.dlevel; /* migrating from this dungeon level */
     mtmp->mtrack[1].x = cc ? cc->x : mtmp->mx;
     mtmp->mtrack[1].y = cc ? cc->y : mtmp->my;
     mtmp->mtrack[0].x = xyloc;
index 979f1315cf45a7885a2d733fc58fb6f645403648..81ed6c314a2bad16abbf2a615fc0c9bfd3ff9506 100644 (file)
@@ -19,7 +19,7 @@ static int FDECL(kick_object, (XCHAR_P, XCHAR_P, char *));
 static int FDECL(really_kick_object, (XCHAR_P, XCHAR_P));
 static char *FDECL(kickstr, (char *, const char *));
 static void FDECL(otransit_msg, (struct obj *, BOOLEAN_P, long));
-static void FDECL(drop_to, (coord *, SCHAR_P));
+static void FDECL(drop_to, (coord *, SCHAR_P, XCHAR_P, XCHAR_P));
 
 static const char kick_passes_thru[] = "kick passes harmlessly through";
 
@@ -1322,10 +1322,13 @@ dokick()
 }
 
 static void
-drop_to(cc, loc)
+drop_to(cc, loc, x,y)
 coord *cc;
 schar loc;
+xchar x,y;
 {
+    stairway *stway = stairway_at(x, y);
+
     /* cover all the MIGR_xxx choices generated by down_gate() */
     switch (loc) {
     case MIGR_RANDOM: /* trap door or hole */
@@ -1340,12 +1343,14 @@ schar loc;
         /*FALLTHRU*/
     case MIGR_STAIRS_UP:
     case MIGR_LADDER_UP:
-        cc->x = u.uz.dnum;
-        cc->y = u.uz.dlevel + 1;
-        break;
     case MIGR_SSTAIRS:
-        cc->x = g.sstairs.tolev.dnum;
-        cc->y = g.sstairs.tolev.dlevel;
+        if (stway) {
+            cc->x = stway->tolev.dnum;
+            cc->y = stway->tolev.dlevel;
+        } else {
+            cc->x = u.uz.dnum;
+            cc->y = u.uz.dlevel + 1;
+        }
         break;
     default:
     case MIGR_NOWHERE:
@@ -1373,7 +1378,7 @@ xchar dlev;          /* if !0 send to dlev near player */
         return;
 
     toloc = down_gate(x, y);
-    drop_to(&cc, toloc);
+    drop_to(&cc, toloc, x, y);
     if (!cc.y)
         return;
 
@@ -1502,7 +1507,7 @@ boolean shop_floor_obj;
         return FALSE;
     if ((toloc = down_gate(x, y)) == MIGR_NOWHERE)
         return FALSE;
-    drop_to(&cc, toloc);
+    drop_to(&cc, toloc, x, y);
     if (!cc.y)
         return FALSE;
 
@@ -1586,6 +1591,7 @@ boolean shop_floor_obj;
     otmp->ox = cc.x;
     otmp->oy = cc.y;
     otmp->owornmask = (long) toloc;
+
     /* boulder from rolling boulder trap, no longer part of the trap */
     if (otmp->otyp == BOULDER)
         otmp->otrapped = 0;
@@ -1614,6 +1620,9 @@ boolean near_hero;
     register int nx, ny;
     int where;
     boolean nobreak, noscatter;
+    stairway *stway;
+    d_level fromdlev;
+    boolean isladder;
 
     for (otmp = g.migrating_objs; otmp; otmp = otmp2) {
         otmp2 = otmp->nobj;
@@ -1633,16 +1642,21 @@ boolean near_hero;
 
         obj_extract_self(otmp);
         otmp->owornmask = 0L;
+        fromdlev.dnum = otmp->omigr_from_dnum;
+        fromdlev.dlevel = otmp->omigr_from_dlevel;
+
+        isladder = FALSE;
 
         switch (where) {
-        case MIGR_STAIRS_UP:
-            nx = xupstair, ny = yupstair;
-            break;
         case MIGR_LADDER_UP:
-            nx = xupladder, ny = yupladder;
-            break;
+            isladder = TRUE;
+        case MIGR_STAIRS_UP:
         case MIGR_SSTAIRS:
-            nx = g.sstairs.sx, ny = g.sstairs.sy;
+            if ((stway = stairway_find_from(&fromdlev, isladder)) != 0) {
+                nx = stway->sx;
+                nx = stway->sy;
+            }
+            break;
             break;
         case MIGR_WITH_HERO:
             nx = u.ux, ny = u.uy;
@@ -1652,6 +1666,8 @@ boolean near_hero;
             nx = ny = 0;
             break;
         }
+        otmp->omigr_from_dnum = 0;
+        otmp->omigr_from_dlevel = 0;
         if (nx > 0) {
             place_object(otmp, nx, ny);
             if (!nobreak && !IS_SOFT(levl[nx][ny].typ)) {
@@ -1727,6 +1743,8 @@ unsigned long deliverflags;
                 free_oname(otmp);
             }
             otmp->migr_species = NON_PM;
+            otmp->omigr_from_dnum = 0;
+            otmp->omigr_from_dlevel = 0;
             (void) add_to_minv(mtmp, otmp);
             cnt++;
             if (maxobj && cnt >= maxobj)
@@ -1773,19 +1791,19 @@ down_gate(x, y)
 xchar x, y;
 {
     struct trap *ttmp;
+    stairway *stway = stairway_at(x, y);
 
     g.gate_str = 0;
     /* this matches the player restriction in goto_level() */
     if (on_level(&u.uz, &qstart_level) && !ok_to_quest())
         return MIGR_NOWHERE;
 
-    if ((xdnstair == x && ydnstair == y)
-        || (g.sstairs.sx == x && g.sstairs.sy == y && !g.sstairs.up)) {
+    if (stway && !stway->up && !stway->isladder) {
         g.gate_str = "down the stairs";
-        return (xdnstair == x && ydnstair == y) ? MIGR_STAIRS_UP
-                                                : MIGR_SSTAIRS;
+        return (stway->tolev.dnum == u.uz.dnum) ? MIGR_STAIRS_UP
+                                : MIGR_SSTAIRS;
     }
-    if (xdnladder == x && ydnladder == y) {
+    if (stway && !stway->up && stway->isladder) {
         g.gate_str = "down the ladder";
         return MIGR_LADDER_UP;
     }
index ec60d716820f2a01ddde19356e5036c406e4c787..32e1ea4c6ff77912714c5c906017fc06cd9f6a20 100644 (file)
@@ -1398,13 +1398,14 @@ void
 next_level(at_stairs)
 boolean at_stairs;
 {
-    if (at_stairs && u.ux == g.sstairs.sx && u.uy == g.sstairs.sy) {
-        /* Taking a down dungeon branch. */
-        goto_level(&g.sstairs.tolev, at_stairs, FALSE, FALSE);
-    } else {
-        /* Going down a stairs or jump in a trap door. */
-        d_level newlevel;
+    stairway *stway = stairway_at(u.ux, u.uy);
+    d_level newlevel;
 
+    if (at_stairs && stway) {
+        newlevel.dnum = stway->tolev.dnum;
+        newlevel.dlevel = stway->tolev.dlevel;
+        goto_level(&newlevel, at_stairs, FALSE, FALSE);
+    } else {
         newlevel.dnum = u.uz.dnum;
         newlevel.dlevel = u.uz.dlevel + 1;
         goto_level(&newlevel, at_stairs, !at_stairs, FALSE);
@@ -1416,17 +1417,22 @@ void
 prev_level(at_stairs)
 boolean at_stairs;
 {
-    if (at_stairs && u.ux == g.sstairs.sx && u.uy == g.sstairs.sy) {
+    stairway *stway = stairway_at(u.ux, u.uy);
+    d_level newlevel;
+
+    if (at_stairs && stway && stway->tolev.dnum != u.uz.dnum) {
         /* Taking an up dungeon branch. */
         /* KMH -- Upwards branches are okay if not level 1 */
         /* (Just make sure it doesn't go above depth 1) */
         if (!u.uz.dnum && u.uz.dlevel == 1 && !u.uhave.amulet)
             done(ESCAPED);
-        else
-            goto_level(&g.sstairs.tolev, at_stairs, FALSE, FALSE);
+        else {
+            newlevel.dnum = stway->tolev.dnum;
+            newlevel.dlevel = stway->tolev.dlevel;
+            goto_level(&newlevel, at_stairs, FALSE, FALSE);
+        }
     } else {
         /* Going up a stairs or rising through the ceiling. */
-        d_level newlevel;
         newlevel.dnum = u.uz.dnum;
         newlevel.dlevel = u.uz.dlevel - 1;
         goto_level(&newlevel, at_stairs, FALSE, FALSE);
@@ -1492,13 +1498,141 @@ int upflag;
     switch_terrain();
 }
 
+void
+stairway_add(x,y, up, ladder, dest)
+int x,y;
+boolean up, ladder;
+d_level *dest;
+{
+    stairway *tmp = (stairway *)alloc(sizeof(stairway));
+
+    tmp->sx = x;
+    tmp->sy = y;
+    tmp->up = up;
+    tmp->isladder = ladder;
+    assign_level(&(tmp->tolev), dest);
+    tmp->next = g.stairs;
+    g.stairs = tmp;
+}
+
+void
+stairway_free_all()
+{
+    stairway *tmp = g.stairs;
+
+    while (tmp) {
+        stairway *tmp2 = tmp->next;
+        free(tmp);
+        tmp = tmp2;
+    }
+    g.stairs = NULL;
+}
+
+stairway *
+stairway_at(x,y)
+int x,y;
+{
+    stairway *tmp = g.stairs;
+
+    while (tmp && !(tmp->sx == x && tmp->sy == y))
+        tmp = tmp->next;
+
+    return tmp;
+}
+
+stairway *
+stairway_find(fromdlev)
+d_level *fromdlev;
+{
+    stairway *tmp = g.stairs;
+
+    while (tmp) {
+        if (tmp->tolev.dnum == fromdlev->dnum
+            && tmp->tolev.dlevel == fromdlev->dlevel)
+            return tmp;
+        tmp = tmp->next;
+    }
+
+    return tmp;
+}
+
+stairway *
+stairway_find_from(fromdlev, ladder)
+d_level *fromdlev;
+boolean ladder;
+{
+    stairway *tmp = g.stairs;
+
+    while (tmp) {
+        if (tmp->tolev.dnum == fromdlev->dnum
+            && tmp->tolev.dlevel == fromdlev->dlevel
+            && tmp->isladder == ladder)
+            return tmp;
+        tmp = tmp->next;
+    }
+
+    return tmp;
+}
+
+stairway *
+stairway_find_dir(up)
+boolean up;
+{
+    stairway *tmp = g.stairs;
+
+    while (tmp && !(tmp->up == up))
+        tmp = tmp->next;
+
+    return tmp;
+}
+
+stairway *
+stairway_find_ladder()
+{
+    stairway *tmp = g.stairs;
+
+    while (tmp && !tmp->isladder)
+        tmp = tmp->next;
+
+    return tmp;
+}
+
+stairway *
+stairway_find_type_dir(ladder,up)
+boolean ladder, up;
+{
+    stairway *tmp = g.stairs;
+
+    while (tmp && !(tmp->isladder == ladder && tmp->up == up))
+        tmp = tmp->next;
+
+    return tmp;
+}
+
+stairway *
+stairway_find_special_dir(up)
+boolean up;
+{
+    stairway *tmp = g.stairs;
+
+    while (tmp) {
+        if (tmp->tolev.dnum != u.uz.dnum && tmp->up != up)
+            return tmp;
+        tmp = tmp->next;
+    }
+
+    return tmp;
+}
+
 /* place you on the special staircase */
 void
 u_on_sstairs(upflag)
 int upflag;
 {
-    if (g.sstairs.sx)
-        u_on_newpos(g.sstairs.sx, g.sstairs.sy);
+    stairway *stway = stairway_find_special_dir(upflag);
+
+    if (stway)
+        u_on_newpos(stway->sx, stway->sy);
     else
         u_on_rndspot(upflag);
 }
@@ -1507,8 +1641,10 @@ int upflag;
 void
 u_on_upstairs()
 {
-    if (xupstair)
-        u_on_newpos(xupstair, yupstair);
+    stairway *stway = stairway_find_dir(TRUE);
+
+    if (stway)
+        u_on_newpos(stway->sx, stway->sy);
     else
         u_on_sstairs(0); /* destination upstairs implies moving down */
 }
@@ -1517,8 +1653,10 @@ u_on_upstairs()
 void
 u_on_dnstairs()
 {
-    if (xdnstair)
-        u_on_newpos(xdnstair, ydnstair);
+    stairway *stway = stairway_find_dir(FALSE);
+
+    if (stway)
+        u_on_newpos(stway->sx, stway->sy);
     else
         u_on_sstairs(1); /* destination dnstairs implies moving up */
 }
@@ -1527,37 +1665,34 @@ boolean
 On_stairs(x, y)
 xchar x, y;
 {
-    return (boolean) ((x == xupstair && y == yupstair)
-                      || (x == xdnstair && y == ydnstair)
-                      || (x == xdnladder && y == ydnladder)
-                      || (x == xupladder && y == yupladder)
-                      || (x == g.sstairs.sx && y == g.sstairs.sy));
+    return (stairway_at(x,y) != NULL);
 }
 
 boolean
 On_ladder(x, y)
 xchar x, y;
 {
-    return (boolean) ((x == xdnladder && y == ydnladder)
-                      || (x == xupladder && y == yupladder));
+    stairway *stway = stairway_at(x,y);
+
+    return (boolean) (stway && stway->isladder);
 }
 
 boolean
 On_stairs_up(x, y)
 xchar x, y;
 {
-    return ((x == xupstair && y == yupstair)
-            || (x == g.sstairs.sx && y == g.sstairs.sy && g.sstairs.up)
-            || (x == xupladder && y == yupladder));
+    stairway *stway = stairway_at(x,y);
+
+    return (boolean) (stway && stway->up);
 }
 
 boolean
 On_stairs_dn(x, y)
 xchar x, y;
 {
-    return ((x == xdnstair && y == ydnstair)
-            || (x == g.sstairs.sx && y == g.sstairs.sy && !g.sstairs.up)
-            || (x == xdnladder && y == ydnladder));
+    stairway *stway = stairway_at(x,y);
+
+    return (boolean) (stway && !stway->up);
 }
 
 boolean
@@ -1599,6 +1734,8 @@ Can_rise_up(x, y, lev)
 int x, y;
 d_level *lev;
 {
+    stairway *stway = stairway_find_special_dir(FALSE);
+
     /* can't rise up from inside the top of the Wizard's tower */
     /* KMH -- or in sokoban */
     if (In_endgame(lev) || In_sokoban(lev)
@@ -1607,7 +1744,7 @@ d_level *lev;
     return (boolean) (lev->dlevel > 1
                       || (g.dungeons[lev->dnum].entry_lev == 1
                           && ledger_no(lev) != 1
-                          && g.sstairs.sx && g.sstairs.up));
+                          && stway && stway->up));
 }
 
 boolean
index cfc80f5eb2a87fff131184881a553dacb0be76f5..7c1393d2d4447025c47b08db2972a70a05e0ca07 100644 (file)
@@ -3362,6 +3362,7 @@ char *buf;
     int ltyp = lev->typ, cmap = -1;
     const char *dfeature = 0;
     static char altbuf[BUFSZ];
+    stairway *stway = stairway_at(x,y);
 
     if (IS_DOOR(ltyp)) {
         switch (lev->doormask) {
@@ -3402,15 +3403,13 @@ char *buf;
                 a_gname(),
                 align_str(Amask2align(lev->altarmask & ~AM_SHRINE)));
         dfeature = altbuf;
-    } else if ((x == xupstair && y == yupstair)
-               || (x == g.sstairs.sx && y == g.sstairs.sy && g.sstairs.up))
+    } else if (stway && !stway->isladder && stway->up)
         cmap = S_upstair; /* "staircase up" */
-    else if ((x == xdnstair && y == ydnstair)
-             || (x == g.sstairs.sx && y == g.sstairs.sy && !g.sstairs.up))
+    else if (stway && !stway->isladder && !stway->up)
         cmap = S_dnstair; /* "staircase down" */
-    else if (x == xupladder && y == yupladder)
+    else if (stway && stway->isladder && stway->up)
         cmap = S_upladder; /* "ladder up" */
-    else if (x == xdnladder && y == ydnladder)
+    else if (stway && stway->isladder && !stway->up)
         cmap = S_dnladder; /* "ladder down" */
     else if (ltyp == DRAWBRIDGE_DOWN)
         cmap = S_vodbridge; /* "lowered drawbridge" */
index 42367342013835e177459727e603c18d81fde975..1eeae31183e7a2ce8905f109776fdb5f62ee26ca 100644 (file)
@@ -151,6 +151,7 @@ coord *startp;
     int lax;          /* if TRUE, pick a position in sight. */
     int dd;           /* distance to current point */
     int max_distance; /* max distance found so far */
+    stairway *stway = g.stairs;
 
     /*
      * If blind and not telepathic, then it doesn't matter what we pick ---
@@ -166,15 +167,13 @@ coord *startp;
      * Arrive at an up or down stairwell if it is in line of sight from the
      * hero.
      */
-    if (couldsee(g.upstair.sx, g.upstair.sy)) {
-        startp->x = g.upstair.sx;
-        startp->y = g.upstair.sy;
-        return TRUE;
-    }
-    if (couldsee(g.dnstair.sx, g.dnstair.sy)) {
-        startp->x = g.dnstair.sx;
-        startp->y = g.dnstair.sy;
-        return TRUE;
+    while (stway) {
+        if (stway->tolev.dnum == u.uz.dnum && couldsee(stway->sx, stway->sy)) {
+            startp->x = stway->sx;
+            startp->y = stway->sy;
+            return TRUE;
+        }
+        stway = stway->next;
     }
 
     /*
index 3af9524ba2d31474c55193c34c67f604c5bdc986..abf074fd563dea0c19c8ba2f6a40223e538c1a93 100644 (file)
@@ -1095,20 +1095,16 @@ coord *cc;
                         goto gotgood;
                 }
             if (bl == 0 && (!mon || mon->data->mmove)) {
+                stairway *stway = g.stairs;
                 /* all map positions are visible (or not good),
                    try to pick something logical */
-                if (g.dnstair.sx && !rn2(2)) {
-                    nx = g.dnstair.sx;
-                    ny = g.dnstair.sy;
-                } else if (g.upstair.sx && !rn2(2)) {
-                    nx = g.upstair.sx;
-                    ny = g.upstair.sy;
-                } else if (g.dnladder.sx && !rn2(2)) {
-                    nx = g.dnladder.sx;
-                    ny = g.dnladder.sy;
-                } else if (g.upladder.sx && !rn2(2)) {
-                    nx = g.upladder.sx;
-                    ny = g.upladder.sy;
+                while (stway) {
+                    if (stway->tolev.dnum == u.uz.dnum && !rn2(2)) {
+                        nx = stway->sx;
+                        ny = stway->sy;
+                        break;
+                    }
+                    stway = stway->next;
                 }
                 if (goodpos(nx, ny, mon, gpflags))
                     goto gotgood;
index 07feca909c42222a427b831965bfbb3d26524794..0a030dc4a3527f1d5a60177ef9546bef71ccb4c7 100644 (file)
@@ -719,10 +719,7 @@ clear_level_structures()
     g.doorindex = 0;
     init_rect();
     init_vault();
-    xdnstair = ydnstair = xupstair = yupstair = 0;
-    g.sstairs.sx = g.sstairs.sy = 0;
-    xdnladder = ydnladder = xupladder = yupladder = 0;
-    g.dnstairs_room = g.upstairs_room = g.sstairs_room = (struct mkroom *) 0;
+    stairway_free_all();
     g.made_branch = FALSE;
     clear_regions();
     g.xstart = 1;
@@ -1112,13 +1109,6 @@ mklev()
     for (ridx = 0; ridx < SIZE(g.rooms); ridx++)
         g.rooms[ridx].orig_rtype = g.rooms[ridx].rtype;
 
-    /* something like this usually belongs in clear_level_structures()
-       but these aren't saved and restored so might not retain their
-       values for the life of the current level; reset them to default
-       now so that they never do and no one will be tempted to introduce
-       a new use of them for anything on this level */
-    g.dnstairs_room = g.upstairs_room = g.sstairs_room = (struct mkroom *) 0;
-
     reseed_random(rn2);
     reseed_random(rn2_on_display_rng);
 }
@@ -1262,14 +1252,10 @@ xchar x, y; /* location */
     if (br->type == BR_PORTAL) {
         mkportal(x, y, dest->dnum, dest->dlevel);
     } else if (make_stairs) {
-        g.sstairs.sx = x;
-        g.sstairs.sy = y;
-        g.sstairs.up =
-            (char) on_level(&br->end1, &u.uz) ? br->end1_up : !br->end1_up;
-        assign_level(&g.sstairs.tolev, dest);
-        g.sstairs_room = br_room;
-
-        levl[x][y].ladder = g.sstairs.up ? LA_UP : LA_DOWN;
+        boolean goes_up = on_level(&br->end1, &u.uz) ? br->end1_up : !br->end1_up;
+
+        stairway_add(x,y, goes_up, FALSE, dest);
+        levl[x][y].ladder = goes_up ? LA_UP : LA_DOWN;
         levl[x][y].typ = STAIRS;
     }
     /*
@@ -1633,13 +1619,17 @@ struct mkroom *croom;
         return;
 
     if (up) {
-        xupstair = x;
-        yupstair = y;
-        g.upstairs_room = croom;
+        d_level dest;
+
+        dest.dnum = u.uz.dnum;
+        dest.dlevel = u.uz.dlevel - 1;
+        stairway_add(x,y, TRUE, FALSE, &dest);
     } else {
-        xdnstair = x;
-        ydnstair = y;
-        g.dnstairs_room = croom;
+        d_level dest;
+
+        dest.dnum = u.uz.dnum;
+        dest.dlevel = u.uz.dlevel + 1;
+        stairway_add(x,y, FALSE, FALSE, &dest);
     }
 
     levl[x][y].typ = STAIRS;
@@ -1659,7 +1649,7 @@ struct mkroom *croom;
 int phase;
 {
     return (croom && (croom->needjoining || (phase < 0))
-            && ((croom != g.dnstairs_room && croom != g.upstairs_room)
+            && ((!has_dnstairs(croom) && !has_upstairs(croom))
                 || phase < 1)
             && (croom->rtype == OROOM
                 || ((phase < 2) && croom->rtype == THEMEROOM)));
index 16c92a8e3fd9d4575c0f584398bf6995ef7c043a..4b3e7c5885eac02c1ea27e00cefcc49d30acae35 100644 (file)
@@ -1015,6 +1015,7 @@ const char *s;
         mazexy(&mm);
         mkstairs(mm.x, mm.y, 0, (struct mkroom *) 0); /* down */
     } else { /* choose "vibrating square" location */
+        stairway *stway;
         int trycnt = 0;
 #define x_maze_min 2
 #define y_maze_min 2
@@ -1047,10 +1048,11 @@ const char *s;
                to be on a spot that's already in use (wall|trap) */
             if (++trycnt > 1000)
                 break;
-        } while (x == xupstair || y == yupstair /*(direct line)*/
-                 || abs(x - xupstair) == abs(y - yupstair)
-                 || distmin(x, y, xupstair, yupstair) <= INVPOS_DISTANCE
-                 || !SPACE_POS(levl[x][y].typ) || occupied(x, y));
+        } while (((stway = stairway_find_dir(TRUE)) != 0)
+                 && (x == stway->sx || y == stway->sy /*(direct line)*/
+                 || abs(x - stway->sx) == abs(y - stway->sy)
+                 || distmin(x, y, stway->sx, stway->sy) <= INVPOS_DISTANCE
+                 || !SPACE_POS(levl[x][y].typ) || occupied(x, y)));
         g.inv_pos.x = x;
         g.inv_pos.y = y;
         maketrap(g.inv_pos.x, g.inv_pos.y, VIBRATING_SQUARE);
index 7f97bae9646775601be2fc1fd66260925474a66b..f4960008b3c0b411b0856f54a3eb31a20f3c9f5b 100644 (file)
@@ -2130,6 +2130,8 @@ struct obj *obj;
 
     obj->where = OBJ_MIGRATING;
     obj->nobj = g.migrating_objs;
+    obj->omigr_from_dnum = u.uz.dnum;
+    obj->omigr_from_dlevel = u.uz.dlevel;
     g.migrating_objs = obj;
 }
 
index b8df0ffa5b81ae1a57004ee024b3c37c6338aa15..d1e7c7cbb42a4c2607f50344ec15152add1f8c3c 100644 (file)
@@ -622,10 +622,13 @@ boolean
 has_dnstairs(sroom)
 register struct mkroom *sroom;
 {
-    if (sroom == g.dnstairs_room)
-        return TRUE;
-    if (g.sstairs.sx && !g.sstairs.up)
-        return (boolean) (sroom == g.sstairs_room);
+    stairway *stway = g.stairs;
+
+    while (stway) {
+        if (!stway->up && inside_room(sroom, stway->sx, stway->sy))
+            return TRUE;
+        stway = stway->next;
+    }
     return FALSE;
 }
 
@@ -633,10 +636,13 @@ boolean
 has_upstairs(sroom)
 register struct mkroom *sroom;
 {
-    if (sroom == g.upstairs_room)
-        return TRUE;
-    if (g.sstairs.sx && g.sstairs.up)
-        return (boolean) (sroom == g.sstairs_room);
+    stairway *stway = g.stairs;
+
+    while (stway) {
+        if (stway->up && inside_room(sroom, stway->sx, stway->sy))
+            return TRUE;
+        stway = stway->next;
+    }
     return FALSE;
 }
 
index 44097e22154fbca11d36700b7200e1b5ce534de5..6ae225d44164590b21a53939cecf6dda78ddbdaa 100644 (file)
--- a/src/mon.c
+++ b/src/mon.c
@@ -2327,11 +2327,15 @@ register struct monst *mtmp;
 #endif
 
     if (mtmp->data->mlet == S_KOP) {
+        stairway *stway = stairway_find_type_dir(FALSE, FALSE);
+
         /* Dead Kops may come back. */
         switch (rnd(5)) {
         case 1: /* returns near the stairs */
-            (void) makemon(mtmp->data, xdnstair, ydnstair, NO_MM_FLAGS);
-            break;
+            if (stway) {
+                (void) makemon(mtmp->data, stway->sx, stway->sy, NO_MM_FLAGS);
+                break;
+            }
         case 2: /* randomly */
             (void) makemon(mtmp->data, 0, 0, NO_MM_FLAGS);
             break;
index 360b564826fa93af93128aca4e6baefc567ec4e9..12e2cb6d6052c357d377f164137d274faa68ab91 100644 (file)
@@ -336,6 +336,7 @@ struct monst *mtmp;
     int fraction, x = mtmp->mx, y = mtmp->my;
     boolean stuck = (mtmp == u.ustuck),
             immobile = (mtmp->data->mmove == 0);
+    stairway *stway;
 
     if (is_animal(mtmp->data) || mindless(mtmp->data))
         return FALSE;
@@ -432,23 +433,25 @@ struct monst *mtmp;
     if (stuck || immobile) {
         ; /* fleeing by stairs or traps is not possible */
     } else if (levl[x][y].typ == STAIRS) {
-        if (x == xdnstair && y == ydnstair) {
+        stway = stairway_at(x,y);
+        if (stway && !stway->up && stway->tolev.dnum == u.uz.dnum) {
             if (!is_floater(mtmp->data))
                 g.m.has_defense = MUSE_DOWNSTAIRS;
-        } else if (x == xupstair && y == yupstair) {
+        } else if (stway && stway->up && stway->tolev.dnum == u.uz.dnum) {
             g.m.has_defense = MUSE_UPSTAIRS;
-        } else if (g.sstairs.sx && x == g.sstairs.sx && y == g.sstairs.sy) {
-            if (g.sstairs.up || !is_floater(mtmp->data))
+        } else if (stway &&  stway->tolev.dnum != u.uz.dnum) {
+            if (stway->up || !is_floater(mtmp->data))
                 g.m.has_defense = MUSE_SSTAIRS;
         }
     } else if (levl[x][y].typ == LADDER) {
-        if (x == xupladder && y == yupladder) {
+        stway = stairway_at(x,y);
+        if (stway && stway->up && stway->tolev.dnum == u.uz.dnum) {
             g.m.has_defense = MUSE_UP_LADDER;
-        } else if (x == xdnladder && y == ydnladder) {
+        } else if (stway && !stway->up && stway->tolev.dnum == u.uz.dnum) {
             if (!is_floater(mtmp->data))
                 g.m.has_defense = MUSE_DN_LADDER;
-        } else if (g.sstairs.sx && x == g.sstairs.sx && y == g.sstairs.sy) {
-            if (g.sstairs.up || !is_floater(mtmp->data))
+        } else if (stway && stway->tolev.dnum != u.uz.dnum) {
+            if (stway->up || !is_floater(mtmp->data))
                 g.m.has_defense = MUSE_SSTAIRS;
         }
     } else {
@@ -653,6 +656,7 @@ struct monst *mtmp;
     struct obj *otmp = g.m.defensive;
     boolean vis, vismon, oseen;
     const char *Mnam;
+    stairway *stway;
 
     if ((i = precheck(mtmp, otmp)) != 0)
         return i;
@@ -770,8 +774,7 @@ struct monst *mtmp;
         if (IS_FURNITURE(levl[mtmp->mx][mtmp->my].typ)
             || IS_DRAWBRIDGE(levl[mtmp->mx][mtmp->my].typ)
             || (is_drawbridge_wall(mtmp->mx, mtmp->my) >= 0)
-            || (g.sstairs.sx && g.sstairs.sx == mtmp->mx
-                && g.sstairs.sy == mtmp->my)) {
+            || stairway_at(mtmp->mx, mtmp->my)) {
             pline_The("digging ray is ineffective.");
             return 2;
         }
@@ -893,6 +896,9 @@ struct monst *mtmp;
         return 2;
     case MUSE_UPSTAIRS:
         m_flee(mtmp);
+        stway = stairway_at(mtmp->mx, mtmp->my);
+        if (!stway)
+            return 0;
         if (ledger_no(&u.uz) == 1)
             goto escape; /* impossible; level 1 upstairs are SSTAIRS */
         if (Inhell && mon_has_amulet(mtmp) && !rn2(4)
@@ -910,33 +916,45 @@ struct monst *mtmp;
         } else {
             if (vismon)
                 pline("%s escapes upstairs!", Monnam(mtmp));
-            migrate_to_level(mtmp, ledger_no(&u.uz) - 1, MIGR_STAIRS_DOWN,
+            migrate_to_level(mtmp, ledger_no(&(stway->tolev)), MIGR_STAIRS_DOWN,
                              (coord *) 0);
         }
         return 2;
     case MUSE_DOWNSTAIRS:
         m_flee(mtmp);
+        stway = stairway_at(mtmp->mx, mtmp->my);
+        if (!stway)
+            return 0;
         if (vismon)
             pline("%s escapes downstairs!", Monnam(mtmp));
-        migrate_to_level(mtmp, ledger_no(&u.uz) + 1, MIGR_STAIRS_UP,
+        migrate_to_level(mtmp, ledger_no(&(stway->tolev)), MIGR_STAIRS_UP,
                          (coord *) 0);
         return 2;
     case MUSE_UP_LADDER:
         m_flee(mtmp);
+        stway = stairway_at(mtmp->mx, mtmp->my);
+        if (!stway)
+            return 0;
         if (vismon)
             pline("%s escapes up the ladder!", Monnam(mtmp));
-        migrate_to_level(mtmp, ledger_no(&u.uz) - 1, MIGR_LADDER_DOWN,
+        migrate_to_level(mtmp, ledger_no(&(stway->tolev)), MIGR_LADDER_DOWN,
                          (coord *) 0);
         return 2;
     case MUSE_DN_LADDER:
         m_flee(mtmp);
+        stway = stairway_at(mtmp->mx, mtmp->my);
+        if (!stway)
+            return 0;
         if (vismon)
             pline("%s escapes down the ladder!", Monnam(mtmp));
-        migrate_to_level(mtmp, ledger_no(&u.uz) + 1, MIGR_LADDER_UP,
+        migrate_to_level(mtmp, ledger_no(&(stway->tolev)), MIGR_LADDER_UP,
                          (coord *) 0);
         return 2;
     case MUSE_SSTAIRS:
         m_flee(mtmp);
+        stway = stairway_at(mtmp->mx, mtmp->my);
+        if (!stway)
+            return 0;
         if (ledger_no(&u.uz) == 1) {
  escape:
             /* Monsters without the Amulet escape the dungeon and
@@ -959,12 +977,12 @@ struct monst *mtmp;
         }
         if (vismon)
             pline("%s escapes %sstairs!", Monnam(mtmp),
-                  g.sstairs.up ? "up" : "down");
+                  stway->up ? "up" : "down");
         /* going from the Valley to Castle (Stronghold) has no sstairs
            to target, but having g.sstairs.<sx,sy> == <0,0> will work the
            same as specifying MIGR_RANDOM when mon_arrive() eventually
            places the monster, so we can use MIGR_SSTAIRS unconditionally */
-        migrate_to_level(mtmp, ledger_no(&g.sstairs.tolev), MIGR_SSTAIRS,
+        migrate_to_level(mtmp, ledger_no(&(stway->tolev)), MIGR_SSTAIRS,
                          (coord *) 0);
         return 2;
     case MUSE_TELEPORT_TRAP:
@@ -1223,11 +1241,7 @@ struct monst *mtmp;
             && !Teleport_control
             /* do try to move hero to a more vulnerable spot */
             && (onscary(u.ux, u.uy, mtmp)
-                || (u.ux == xupstair && u.uy == yupstair)
-                || (u.ux == xdnstair && u.uy == ydnstair)
-                || (u.ux == g.sstairs.sx && u.uy == g.sstairs.sy)
-                || (u.ux == xupladder && u.uy == yupladder)
-                || (u.ux == xdnladder && u.uy == ydnladder))) {
+                || (stairway_at(u.ux, u.uy))) {
             g.m.offensive = obj;
             g.m.has_offense = MUSE_WAN_TELEPORTATION;
         }
index 4b12acb3280e2b9e004163b92b7f54e466f8737a..02484db07d582ade0f47ebffbebe0ee9f4f74137 100644 (file)
@@ -1051,9 +1051,7 @@ register struct obj *otmp;
             HLevitation &= ~I_SPECIAL; /* can't descend upon demand */
             if (BLevitation) {
                 ; /* rising via levitation is blocked */
-            } else if ((u.ux == xupstair && u.uy == yupstair)
-                    || (g.sstairs.up && u.ux == g.sstairs.sx && u.uy == g.sstairs.sy)
-                    || (xupladder && u.ux == xupladder && u.uy == yupladder)) {
+            } else if (stairway_find_dir(TRUE)) {
                 (void) doup();
                 /* in case we're already Levitating, which would have
                    resulted in incrementing 'nothing' */
index 8df518b45a7883010627ee2401dd02e7d9a5bb1d..5b933ae191b08d2e1b5b0a7b91bbcb603662c05a 100644 (file)
@@ -905,6 +905,33 @@ NHFILE *nhfp;
     return 1;
 }
 
+void
+rest_stairs(nhfp)
+NHFILE *nhfp;
+{
+    int buflen = 0;
+    stairway stway;
+    int len = 0;
+
+    stairway_free_all();
+    while (1) {
+        if (nhfp->structlevel) {
+            len += sizeof(buflen);
+            mread(nhfp->fd, (genericptr_t) &buflen, sizeof buflen);
+        }
+
+        if (buflen == -1)
+            break;
+
+        if (nhfp->structlevel) {
+            len += sizeof(stairway);
+            mread(nhfp->fd, (genericptr_t) &stway, sizeof(stairway));
+        }
+
+        stairway_add(stway.sx, stway.sy, stway.up, stway.isladder, &(stway.tolev));
+    }
+}
+
 void
 restcemetery(nhfp, cemeteryaddr)
 NHFILE *nhfp;
@@ -1050,11 +1077,7 @@ xchar lev;
     elapsed = g.monstermoves - g.omoves;
 
     if (nhfp->structlevel) {
-        mread(nhfp->fd, (genericptr_t)&g.upstair, sizeof(stairway));
-        mread(nhfp->fd, (genericptr_t)&g.dnstair, sizeof(stairway));
-        mread(nhfp->fd, (genericptr_t)&g.upladder, sizeof(stairway));
-        mread(nhfp->fd, (genericptr_t)&g.dnladder, sizeof(stairway));
-        mread(nhfp->fd, (genericptr_t)&g.sstairs, sizeof(stairway));
+        rest_stairs(nhfp);
         mread(nhfp->fd, (genericptr_t)&g.updest, sizeof(dest_area));
         mread(nhfp->fd, (genericptr_t)&g.dndest, sizeof(dest_area));
         mread(nhfp->fd, (genericptr_t)&g.level.flags, sizeof(g.level.flags));
@@ -1132,22 +1155,33 @@ xchar lev;
     rest_regions(nhfp);
 
     if (ghostly) {
+        stairway *stway = g.stairs;
+        while (stway) {
+            if (!stway->isladder && !stway->up && stway->tolev.dnum == u.uz.dnum)
+                break;
+            stway = stway->next;
+        }
+
         /* Now get rid of all the temp fruits... */
         freefruitchn(g.oldfruit), g.oldfruit = 0;
 
         if (lev > ledger_no(&medusa_level)
-            && lev < ledger_no(&stronghold_level) && xdnstair == 0) {
+            && lev < ledger_no(&stronghold_level) && !stway) {
             coord cc;
+            d_level dest;
+
+            dest.dnum = u.uz.dnum;
+            dest.dlevel = u.uz.dlevel + 1;
 
             mazexy(&cc);
-            xdnstair = cc.x;
-            ydnstair = cc.y;
+            stairway_add(cc.x, cc.y, FALSE, FALSE, &dest);
             levl[cc.x][cc.y].typ = STAIRS;
         }
 
         br = Is_branchlev(&u.uz);
         if (br && u.uz.dlevel == 1) {
             d_level ltmp;
+            stairway *stway;
 
             if (on_level(&u.uz, &br->end1))
                 assign_level(&ltmp, &br->end2);
@@ -1157,8 +1191,15 @@ xchar lev;
             switch (br->type) {
             case BR_STAIR:
             case BR_NO_END1:
-            case BR_NO_END2: /* OK to assign to g.sstairs if it's not used */
-                assign_level(&g.sstairs.tolev, &ltmp);
+            case BR_NO_END2:
+                stway = g.stairs;
+                while (stway) {
+                    if (stway->tolev.dnum != u.uz.dnum)
+                        break;
+                    stway = stway->next;
+                }
+                if (stway)
+                    assign_level(&(stway->tolev), &ltmp);
                 break;
             case BR_PORTAL: /* max of 1 portal per level */
                 for (trap = g.ftrap; trap; trap = trap->ntrap)
index a15ce5860e2553796f4fc85cf565b8e3e4b18a64..586ae177d43b01e2b18a8ad4196cad2b9c72d267 100644 (file)
@@ -28,6 +28,7 @@ static void FDECL(savedamage, (NHFILE *));
 static void FDECL(saveobj, (NHFILE *,struct obj *));
 static void FDECL(savemon, (NHFILE *,struct monst *));
 static void FDECL(savelevl, (NHFILE *,BOOLEAN_P));
+static void FDECL(save_stairs, (NHFILE *));
 static void FDECL(saveobjchn, (NHFILE *,struct obj *));
 static void FDECL(savemonchn, (NHFILE *,struct monst *));
 static void FDECL(savetrapchn, (NHFILE *,struct trap *));
@@ -457,11 +458,7 @@ xchar lev;
     if (nhfp->structlevel) {
         bwrite(nhfp->fd, (genericptr_t) g.lastseentyp, sizeof g.lastseentyp);
         bwrite(nhfp->fd, (genericptr_t) &g.monstermoves, sizeof g.monstermoves);
-        bwrite(nhfp->fd, (genericptr_t) &g.upstair, sizeof (stairway));
-        bwrite(nhfp->fd, (genericptr_t) &g.dnstair, sizeof (stairway));
-        bwrite(nhfp->fd, (genericptr_t) &g.upladder, sizeof (stairway));
-        bwrite(nhfp->fd, (genericptr_t) &g.dnladder, sizeof (stairway));
-        bwrite(nhfp->fd, (genericptr_t) &g.sstairs, sizeof (stairway));
+        save_stairs(nhfp);
         bwrite(nhfp->fd, (genericptr_t) &g.updest, sizeof (dest_area));
         bwrite(nhfp->fd, (genericptr_t) &g.dndest, sizeof (dest_area));
         bwrite(nhfp->fd, (genericptr_t) &g.level.flags, sizeof g.level.flags);
@@ -496,6 +493,7 @@ xchar lev;
         fobj = 0;
         g.level.buriedobjlist = 0;
         g.billobjs = 0;
+        stairway_free_all();
         /* level.bonesinfo = 0; -- handled by savecemetery() */
     }
     save_engravings(nhfp);
@@ -668,6 +666,34 @@ struct obj *otmp;
     }
 }
 
+static void
+save_stairs(nhfp)
+NHFILE *nhfp;
+{
+    stairway *stway = g.stairs;
+    int buflen = (int) sizeof (stairway);
+    int len = 0;
+
+    while (stway) {
+        if (perform_bwrite(nhfp)) {
+            if (nhfp->structlevel) {
+                len += sizeof(buflen);
+                bwrite(nhfp->fd, (genericptr_t) &buflen, sizeof buflen);
+                len += sizeof(stairway);
+                bwrite(nhfp->fd, (genericptr_t) stway, sizeof(stairway));
+            }
+        }
+        stway = stway->next;
+    }
+    if (perform_bwrite(nhfp)) {
+        if (nhfp->structlevel) {
+            buflen = -1;
+            len += sizeof(buflen);
+            bwrite(nhfp->fd, (genericptr_t) &buflen, sizeof buflen);
+        }
+    }
+}
+
 static void
 saveobjchn(nhfp, otmp)
 NHFILE *nhfp;
index 2c7852d1032be6c9fbb11094a4dcca598bc6205e..f393daab916c022ed198dd7465f47b3de9580b0d 100644 (file)
--- a/src/shk.c
+++ b/src/shk.c
@@ -362,6 +362,13 @@ register boolean nearshop;
 
     {
         coord mm;
+        stairway *stway = g.stairs;
+
+        while (stway) {
+            if (!stway->isladder && !stway->up && stway->tolev.dnum == u.uz.dnum)
+                break;
+            stway = stway->next;
+        }
 
         if (nearshop) {
             /* Create swarm around you, if you merely "stepped out" */
@@ -375,8 +382,8 @@ register boolean nearshop;
         if (flags.verbose)
             pline_The("Keystone Kops are after you!");
         /* Create swarm near down staircase (hinders return to level) */
-        mm.x = xdnstair;
-        mm.y = ydnstair;
+        mm.x = stway->sx;
+        mm.y = stway->sy;
         makekops(&mm);
         /* Create swarm near shopkeeper (hinders return to shop) */
         mm.x = shkp->mx;
index f3e2fe1a3aaa0ca630e812b05d924a525bfa05b0..5767744c95c03093b0806d10311d41f3a153fc04 100755 (executable)
@@ -60,7 +60,6 @@ static void FDECL(create_object, (object *, struct mkroom *));
 static void FDECL(create_altar, (altar *, struct mkroom *));
 static boolean FDECL(search_door, (struct mkroom *,
                                        xchar *, xchar *, XCHAR_P, int));
-static void NDECL(fix_stair_rooms);
 static void FDECL(create_corridor, (corridor *));
 static struct mkroom *FDECL(build_room, (room *, struct mkroom *));
 static void FDECL(light_region, (region *));
@@ -500,6 +499,7 @@ boolean extras;
     struct mkroom *sroom;
     timer_element *timer;
     boolean ball_active = FALSE, ball_fliparea = FALSE;
+    stairway *stway;
 
     /* nothing to do unless (flp & 1) or (flp & 2) or both */
     if ((flp & 3) == 0)
@@ -537,29 +537,11 @@ boolean extras;
     }
 
     /* stairs and ladders */
-    if (flp & 1) {
-        if (xupstair)
-            yupstair = FlipY(yupstair);
-        if (xdnstair)
-            ydnstair = FlipY(ydnstair);
-        if (xupladder)
-            yupladder = FlipY(yupladder);
-        if (xdnladder)
-            ydnladder = FlipY(ydnladder);
-        if (g.sstairs.sx)
-            g.sstairs.sy = FlipY(g.sstairs.sy);
-    }
-    if (flp & 2) {
-        if (xupstair)
-            xupstair = FlipX(xupstair);
-        if (xdnstair)
-            xdnstair = FlipX(xdnstair);
-        if (xupladder)
-            xupladder = FlipX(xupladder);
-        if (xdnladder)
-            xdnladder = FlipX(xdnladder);
-        if (g.sstairs.sx)
-            g.sstairs.sx = FlipX(g.sstairs.sx);
+    for (stway = g.stairs; stway; stway = stway->next) {
+        if (flp & 1)
+            stway->sy = FlipY(stway->sy);
+        if (flp & 2)
+            stway->sx = FlipX(stway->sx);
     }
 
     /* traps */
@@ -2564,52 +2546,6 @@ schar ftyp, btyp;
     return TRUE;
 }
 
-/*
- * Disgusting hack: since special levels have their rooms filled before
- * sorting the rooms, we have to re-arrange the speed values g.upstairs_room
- * and g.dnstairs_room after the rooms have been sorted.  On normal levels,
- * stairs don't get created until _after_ sorting takes place.
- */
-static void
-fix_stair_rooms()
-{
-    int i;
-    struct mkroom *croom;
-
-    if (xdnstair
-        && !((g.dnstairs_room->lx <= xdnstair
-              && xdnstair <= g.dnstairs_room->hx)
-             && (g.dnstairs_room->ly <= ydnstair
-                 && ydnstair <= g.dnstairs_room->hy))) {
-        for (i = 0; i < g.nroom; i++) {
-            croom = &g.rooms[i];
-            if ((croom->lx <= xdnstair && xdnstair <= croom->hx)
-                && (croom->ly <= ydnstair && ydnstair <= croom->hy)) {
-                g.dnstairs_room = croom;
-                break;
-            }
-        }
-        if (i == g.nroom)
-            panic("Couldn't find dnstair room in fix_stair_rooms!");
-    }
-    if (xupstair
-        && !((g.upstairs_room->lx <= xupstair
-              && xupstair <= g.upstairs_room->hx)
-             && (g.upstairs_room->ly <= yupstair
-                 && yupstair <= g.upstairs_room->hy))) {
-        for (i = 0; i < g.nroom; i++) {
-            croom = &g.rooms[i];
-            if ((croom->lx <= xupstair && xupstair <= croom->hx)
-                && (croom->ly <= yupstair && yupstair <= croom->hy)) {
-                g.upstairs_room = croom;
-                break;
-            }
-        }
-        if (i == g.nroom)
-            panic("Couldn't find upstair room in fix_stair_rooms!");
-    }
-}
-
 /*
  * Corridors always start from a door. But it can end anywhere...
  * Basically we search for door coordinates or for endpoints coordinates
@@ -2622,7 +2558,6 @@ corridor *c;
     coord org, dest;
 
     if (c->src.room == -1) {
-        fix_stair_rooms();
         makecorridors(); /*makecorridors(c->src.door);*/
         return;
     }
@@ -3933,12 +3868,18 @@ boolean using_ladder;
     if (using_ladder) {
         levl[x][y].typ = LADDER;
         if (up) {
-            xupladder = x;
-            yupladder = y;
+            d_level dest;
+
+            dest.dnum = u.uz.dnum;
+            dest.dlevel = u.uz.dlevel - 1;
+            stairway_add(x, y, TRUE, TRUE, &dest);
             levl[x][y].ladder = LA_UP;
         } else {
-            xdnladder = x;
-            ydnladder = y;
+            d_level dest;
+
+            dest.dnum = u.uz.dnum;
+            dest.dlevel = u.uz.dlevel + 1;
+            stairway_add(x, y, FALSE, TRUE, &dest);
             levl[x][y].ladder = LA_DOWN;
         }
     } else {
@@ -5334,17 +5275,15 @@ ensure_way_out()
     struct trap *ttmp = g.ftrap;
     int x,y;
     boolean ret = TRUE;
+    stairway *stway = g.stairs;
 
     set_selection_floodfillchk(floodfillchk_match_accessible);
 
-    if (xupstair && !selection_getpoint(xupstair, yupstair, ov))
-        selection_floodfill(ov, xupstair, yupstair, TRUE);
-    if (xdnstair && !selection_getpoint(xdnstair, ydnstair, ov))
-        selection_floodfill(ov, xdnstair, ydnstair, TRUE);
-    if (xupladder && !selection_getpoint(xupladder, yupladder, ov))
-        selection_floodfill(ov, xupladder, yupladder, TRUE);
-    if (xdnladder && !selection_getpoint(xdnladder, ydnladder, ov))
-        selection_floodfill(ov, xdnladder, ydnladder, TRUE);
+    while (stway) {
+        if (stway->tolev.dnum == u.uz.dnum)
+            selection_floodfill(ov, stway->sx, stway->sy, TRUE);
+        stway = stway->next;
+    }
 
     while (ttmp) {
         if ((ttmp->ttyp == MAGIC_PORTAL || ttmp->ttyp == VIBRATING_SQUARE
index cb8bb4f40e5c665dc27124c1f00c73f6ff3274cc..396ae3b56945ad4688e2e1cf966b430dc70d088a 100644 (file)
@@ -1260,6 +1260,17 @@ register int x, y;
         make_angry_shk(mtmp, oldx, oldy);
 }
 
+static stairway *
+stairway_find_forwiz(ladder, up)
+boolean ladder, up;
+{
+    stairway *stway = g.stairs;
+
+    while (stway && !(stway->isladder == ladder && stway->up == up && stway->tolev.dnum == u.uz.dnum))
+        stway = stway->next;
+    return stway;
+}
+
 /* place a monster at a random location, typically due to teleport */
 /* return TRUE if successful, FALSE if not */
 boolean
@@ -1268,6 +1279,7 @@ struct monst *mtmp; /* mx==0 implies migrating monster arrival */
 boolean suppress_impossible;
 {
     register int x, y, trycount;
+    stairway *stway;
 
     if (mtmp == u.usteed) {
         tele();
@@ -1275,12 +1287,19 @@ boolean suppress_impossible;
     }
 
     if (mtmp->iswiz && mtmp->mx) { /* Wizard, not just arriving */
-        if (!In_W_tower(u.ux, u.uy, &u.uz))
-            x = xupstair, y = yupstair;
-        else if (!xdnladder) /* bottom level of tower */
-            x = xupladder, y = yupladder;
-        else
-            x = xdnladder, y = ydnladder;
+        if (!In_W_tower(u.ux, u.uy, &u.uz)) {
+            stway = stairway_find_forwiz(FALSE, TRUE);
+            x = stway->sx;
+            y = stway->sy;
+        } else if (!stairway_find_forwiz(TRUE, FALSE)) { /* bottom level of tower */
+            stway = stairway_find_forwiz(TRUE, TRUE);
+            x = stway->sx;
+            y = stway->sy;
+        } else {
+            stway = stairway_find_forwiz(TRUE, FALSE);
+            x = stway->sx;
+            y = stway->sy;
+        }
         /* if the wiz teleports away to heal, try the up staircase,
            to block the player's escaping before he's healed
            (deliberately use `goodpos' rather than `rloc_pos_ok' here) */
index a0b267682e513f2b3f990be2bbdb80b65cc84f6b..0c6462888affe14492069d4d9859b3509b295cf0 100644 (file)
@@ -329,30 +329,26 @@ xchar *sx;
 xchar *sy;
 {
     xchar x = 0, y = 0;
-
-    if (builds_up(&u.uz)) {
-        if (xdnstair) {
-            x = xdnstair;
-            y = ydnstair;
-        } else if (xdnladder) {
-            x = xdnladder;
-            y = ydnladder;
-        }
+    stairway *stway = g.stairs;
+    boolean stdir = !builds_up(&u.uz);
+
+    if ((stway = stairway_find_type_dir(FALSE, stdir)) != 0) {
+        x = stway->sx;
+        y = stway->sy;
+    } else if ((stway = stairway_find_type_dir(TRUE, stdir)) != 0) {
+        x = stway->sx;
+        y = stway->sy;
     } else {
-        if (xupstair) {
-            x = xupstair;
-            y = yupstair;
-        } else if (xupladder) {
-            x = xupladder;
-            y = yupladder;
+        while (stway) {
+            if (stway->tolev.dnum != u.uz.dnum) {
+                x = stway->sx;
+                y = stway->sy;
+                break;
+            }
+            stway = stway->next;
         }
     }
 
-    if (!x && g.sstairs.sx) {
-        x = g.sstairs.sx;
-        y = g.sstairs.sy;
-    }
-
     if (x && y) {
         *sx = x;
         *sy = y;
index 3910b909a97a9225ca591bd1a5f8346e2f8f9f62..0494a9fb0c1a7be853ba20982a2b11c9318a3b0c 100644 (file)
--- a/src/zap.c
+++ b/src/zap.c
@@ -2891,6 +2891,7 @@ struct obj *obj; /* wand or spell */
     struct engr *e;
     struct trap *ttmp;
     char buf[BUFSZ];
+    stairway *stway = g.stairs;
 
     /* some wands have special effects other than normal bhitpile */
     /* drawbridge might change <u.ux,u.uy> */
@@ -2913,11 +2914,16 @@ struct obj *obj; /* wand or spell */
         return TRUE; /* we've done our own bhitpile */
     case WAN_OPENING:
     case SPE_KNOCK:
+        while (stway) {
+            if (!stway->isladder && !stway->up && stway->tolev.dnum == u.uz.dnum)
+                break;
+            stway = stway->next;
+        }
         /* up or down, but at closed portcullis only */
         if (is_db_wall(x, y) && find_drawbridge(&xx, &yy)) {
             open_drawbridge(xx, yy);
             disclose = TRUE;
-        } else if (u.dz > 0 && (x == xdnstair && y == ydnstair)
+        } else if (u.dz > 0 && stway && stway->sx == x && stway->sy == y
                    /* can't use the stairs down to quest level 2 until
                       leader "unlocks" them; give feedback if you try */
                    && on_level(&u.uz, &qstart_level) && !ok_to_quest()) {