From: Pasi Kallinen Date: Mon, 9 Nov 2020 16:50:02 +0000 (+0200) Subject: Rework stairs structure X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=6ec55a36249cf04493c49a87df70ab0b3cceb68b;p=nethack Rework stairs structure 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. --- diff --git a/doc/fixes37.0 b/doc/fixes37.0 index 10cead1e5..34885e5c8 100644 --- a/doc/fixes37.0 +++ b/doc/fixes37.0 @@ -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 diff --git a/include/decl.h b/include/decl.h index c72ab57bf..dc1039c22 100644 --- a/include/decl.h +++ b/include/decl.h @@ -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]; diff --git a/include/dungeon.h b/include/dungeon.h index 2a135c62f..7a07e58f4 100644 --- a/include/dungeon.h +++ b/include/dungeon.h @@ -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 */ diff --git a/include/extern.h b/include/extern.h index e24e605b6..9280b47b6 100644 --- a/include/extern.h +++ b/include/extern.h @@ -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); diff --git a/include/monst.h b/include/monst.h index eaecd34a7..6842162ca 100644 --- a/include/monst.h +++ b/include/monst.h @@ -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 */ diff --git a/include/obj.h b/include/obj.h index 3504992d6..4b6f6d5c8 100644 --- a/include/obj.h +++ b/include/obj.h @@ -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 */ }; diff --git a/include/patchlevel.h b/include/patchlevel.h index adf03ba79..0ca768ddb 100644 --- a/include/patchlevel.h +++ b/include/patchlevel.h @@ -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. diff --git a/include/rm.h b/include/rm.h index fbaa8a388..b55512439 100644 --- a/include/rm.h +++ b/include/rm.h @@ -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 { diff --git a/src/allmain.c b/src/allmain.c index a11919b85..d015e7621 100644 --- a/src/allmain.c +++ b/src/allmain.c @@ -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) { diff --git a/src/apply.c b/src/apply.c index d5f8a1797..a7cc2d82c 100644 --- a/src/apply.c +++ b/src/apply.c @@ -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)) diff --git a/src/cmd.c b/src/cmd.c index b870f0d46..c6561ee19 100644 --- 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 */ diff --git a/src/decl.c b/src/decl.c index 5f44d64e1..54d38ac6d 100644 --- a/src/decl.c +++ b/src/decl.c @@ -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 */ diff --git a/src/dig.c b/src/dig.c index 05369498f..7c3e603c2 100644 --- 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); diff --git a/src/do.c b/src/do.c index ee2c3c141..a98417fa3 100644 --- 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 diff --git a/src/dog.c b/src/dog.c index 14a6b1992..450996168 100644 --- 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; diff --git a/src/dokick.c b/src/dokick.c index 979f1315c..81ed6c314 100644 --- a/src/dokick.c +++ b/src/dokick.c @@ -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; } diff --git a/src/dungeon.c b/src/dungeon.c index ec60d7168..32e1ea4c6 100644 --- a/src/dungeon.c +++ b/src/dungeon.c @@ -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 diff --git a/src/invent.c b/src/invent.c index cfc80f5eb..7c1393d2d 100644 --- a/src/invent.c +++ b/src/invent.c @@ -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" */ diff --git a/src/mail.c b/src/mail.c index 423673420..1eeae3118 100644 --- a/src/mail.c +++ b/src/mail.c @@ -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; } /* diff --git a/src/makemon.c b/src/makemon.c index 3af9524ba..abf074fd5 100644 --- a/src/makemon.c +++ b/src/makemon.c @@ -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; diff --git a/src/mklev.c b/src/mklev.c index 07feca909..0a030dc4a 100644 --- a/src/mklev.c +++ b/src/mklev.c @@ -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))); diff --git a/src/mkmaze.c b/src/mkmaze.c index 16c92a8e3..4b3e7c588 100644 --- a/src/mkmaze.c +++ b/src/mkmaze.c @@ -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); diff --git a/src/mkobj.c b/src/mkobj.c index 7f97bae96..f4960008b 100644 --- a/src/mkobj.c +++ b/src/mkobj.c @@ -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; } diff --git a/src/mkroom.c b/src/mkroom.c index b8df0ffa5..d1e7c7cbb 100644 --- a/src/mkroom.c +++ b/src/mkroom.c @@ -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; } diff --git a/src/mon.c b/src/mon.c index 44097e221..6ae225d44 100644 --- 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; diff --git a/src/muse.c b/src/muse.c index 360b56482..12e2cb6d6 100644 --- a/src/muse.c +++ b/src/muse.c @@ -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. == <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; } diff --git a/src/potion.c b/src/potion.c index 4b12acb32..02484db07 100644 --- a/src/potion.c +++ b/src/potion.c @@ -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' */ diff --git a/src/restore.c b/src/restore.c index 8df518b45..5b933ae19 100644 --- a/src/restore.c +++ b/src/restore.c @@ -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(<mp, &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, <mp); + 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), <mp); break; case BR_PORTAL: /* max of 1 portal per level */ for (trap = g.ftrap; trap; trap = trap->ntrap) diff --git a/src/save.c b/src/save.c index a15ce5860..586ae177d 100644 --- a/src/save.c +++ b/src/save.c @@ -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; diff --git a/src/shk.c b/src/shk.c index 2c7852d10..f393daab9 100644 --- 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; diff --git a/src/sp_lev.c b/src/sp_lev.c index f3e2fe1a3..5767744c9 100755 --- a/src/sp_lev.c +++ b/src/sp_lev.c @@ -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 diff --git a/src/teleport.c b/src/teleport.c index cb8bb4f40..396ae3b56 100644 --- a/src/teleport.c +++ b/src/teleport.c @@ -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) */ diff --git a/src/wizard.c b/src/wizard.c index a0b267682..0c6462888 100644 --- a/src/wizard.c +++ b/src/wizard.c @@ -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; diff --git a/src/zap.c b/src/zap.c index 3910b909a..0494a9fb0 100644 --- 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 */ @@ -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()) {