/* * This file contains support for digging the "special" levels: the * elemental planes, underworld, and hell, including the gates * connecting them to the rest of the dungeon. */ #include #include "vars.h" #include "dice.h" #include "pline.h" #include "gates.h" #include "digdungeon.h" #include "digspecial.h" /* * These arrays come from elsewhere (specialdata.c, at this writing). * See the comment there for more. */ extern const char *underworld_quarters[]; extern const char *hell_quarters[]; /* * Return true if lc is an acceptable place to put a gate. * * For a place to be acceptable, it must be CAVE/JUSTCAVE. If the * level is wraparound (ie, the elemental planes), that is the only * test. Otherwise, the eight surrounding cells are considered as a * loop and we count the number of transitions between cave and * non-cave in this loop; this count must equal two. (Arguably we * should also accept zero, so that the gate doesn't have to be * against a wall.) This is designed to ensure that the gate never * blocks off access to anything, because any cell adjacent to it can * be reached from any other cell adjacent to it without passing * through the gate cell. */ static int ckgateok(LOC *lc) { int iscave[8]; int i; int j; if ((lc->type != LOC_CAVE) || (lc->cavetype != LOC_JUSTCAVE)) { return(0); } if (lc->on->flags & LVF_WRAPAROUND) { return(1); } for (i=0;i<8;i++) { iscave[i] = (lc->on->cells[lc->x+delta[i][0]][lc->y+delta[i][1]].type == LOC_CAVE); } j = 0; for (i=0;i<8;i++) { if (iscave[i] != iscave[(i+1)%8]) { j ++; } } return(j==2); } /* * This is a location test function used when creating gates on the * underworld and hell levels. This returns true for any * CAVE/JUSTCAVE cell which has LF_FLAG set. level_from_quarters() * sets LF_FLAG on cells to indicate that the level data marks them as * being suitable for gates. */ static int ckflag(LOC *lc) { return( (lc->type == LOC_CAVE) && (lc->cavetype == LOC_JUSTCAVE) && (lc->flags & LF_FLAG) ); } /* * This is ckavoid crossed with ckflag. */ static int ckavflag(LOC *lc) { return(ckflag(lc)&&ckavoid(lc)); } /* * Initial setup of the elemental plane of Earth. */ static void make_earth(void) { LEVEL *lp; lp = &levels[L_EARTH]; lp->levelno = L_EARTH; lp->index = L_EARTH; lp->shortname = "Earth"; lp->longname = "Plane of Earth"; lp->flags = LVF_WRAPAROUND; lp->visibility = 2; clearlevel(lp,LOC_CAVE); } /* * Initial setup of the elemental plane of Air. */ static void make_air(void) { LEVEL *lp; lp = &levels[L_AIR]; lp->levelno = L_AIR; lp->index = L_AIR; lp->shortname = "Air"; lp->longname = "Plane of Air"; lp->flags = LVF_WRAPAROUND | LVF_DRIFT; lp->visibility = 10; clearlevel(lp,LOC_CAVE); } /* * Initial setup of the elemental plane of Water. */ static void make_water(void) { LEVEL *lp; lp = &levels[L_WATER]; lp->levelno = L_WATER; lp->index = L_WATER; lp->shortname = "Water"; lp->longname = "Plane of Water"; lp->flags = LVF_WRAPAROUND | LVF_DRIFT; lp->visibility = 3; clearlevel(lp,LOC_CAVE); } /* * Initial setup of the elemental plane of Fire. */ static void make_fire(void) { LEVEL *lp; lp = &levels[L_FIRE]; lp->levelno = L_FIRE; lp->index = L_FIRE; lp->shortname = "Fire"; lp->longname = "Plane of Fire"; lp->flags = LVF_WRAPAROUND; lp->visibility = 6; clearlevel(lp,LOC_CAVE); } /* * Initial setup of the Underworld level. */ static void make_underworld(void) { LEVEL *lp; lp = &levels[L_UNDERWORLD]; lp->levelno = L_UNDERWORLD; lp->index = L_UNDERWORLD; lp->shortname = "Underworld"; lp->longname = "Underworld"; lp->visibility = 7; } /* * Initial setup of the Hell level. */ static void make_hell(void) { LEVEL *lp; lp = &levels[L_HELL]; lp->levelno = L_HELL; lp->index = L_HELL; lp->shortname = "Hell"; lp->longname = "Hell"; lp->visibility = 7; } /* * Create a level from quarter-level data. The Underworld and Hell * levels are created this way. * * For each such level, there is a hand-created list of level quarters. * Four quarters are picked randomly from this list. One is used * directly, one is rotated 180°, one is flipped about X, and one is * flipped about Y; they are then simply placed next to one another to * create the level layout. The quarters are carefully designed to * ensure that (a) each quarter is connected and (b) any two quarters * connect when placed next to one another along either axis. This * ensures that the level as a whole is usable. * * After the quarters have been picked and the basic level assembled, * doors may be placed, either blocking otherwise open passages or in * otherwise solid walls; the data may mark walls as not being * acceptable locations for doors, in which case they have LF_FLAGB * set. The data also marks some cells as being suitable places to * put a gate; those locations have LF_FLAG set. These flags are used * elsewhere in dungeon generation. */ static void level_from_quarters(LEVEL *lv, const char **qs) { static int xreset[4] = { 0, 0, LEV_X-1, LEV_X-1 }; static int yreset[4] = { 0, LEV_Y-1, 0, LEV_Y-1 }; static int xinc[4] = { 1, 1, -1, -1 }; static int yinc[4] = { 1, -1, 1, -1 }; int qnos[4]; int nq; int i; int j; int x; int y; int dx; int dy; int xc; int yc; const char *qp; clearlevel(lv,LOC_ROCK); for (nq=0;qs[nq];nq++) ; if (nq < 4) { panic("Not enough quarters in level_from_quarters"); } for (i=0;i<4;i++) { qnos[i] = rnd(nq-i); } for (i=3;i>0;i--) { for (j=i-1;j>=0;j--) { if (qnos[i] >= qnos[j]) { qnos[i] ++; } } } for (i=3;i>0;i--) { for (j=i-1;j>=0;j--) { if (qnos[i] == qnos[j]) { panic("identical quarters chosen: %d %d %d %d", qnos[0],qnos[1],qnos[2],qnos[3]); } } } for (i=0;i<4;i++) { qp = qs[qnos[i]]; dx = xinc[i]; dy = yinc[i]; y = yreset[i]; for (yc=LEV_Y/2;yc>0;yc--) { x = xreset[i]; for (xc=LEV_X/2;xc>0;xc--) { switch (*qp++) { case 'O': lv->cells[x][y].flags &= ~LF_FLAGB; break; case 'N': lv->cells[x][y].flags |= LF_FLAGB; break; case '#': digcell(lv,x,y); lv->cells[x][y].flags &= ~LF_FLAG; lv->cells[x][y].flags |= LF_FLAGB; lv->cells[x][y].type = LOC_SDOOR; lv->cells[x][y].walldoor = LOC_UWALL; break; case '.': digcell(lv,x,y); lv->cells[x][y].flags &= ~(LF_FLAG|LF_FLAGB); break; case '_': digcell(lv,x,y); lv->cells[x][y].flags |= LF_FLAG; lv->cells[x][y].flags &= ~LF_FLAGB; break; default: panic("Bad character 0%o in quarter",0xff&(int)qp[-1]); break; } x += dx; } y += dy; } } } /* * Replace most of the Plane of Earth with LOC_ROCK. */ static void solidify_earth(void) { LEVEL *lv; int x; int y; LOC *l; int i; void dig_one(LOC *lc) { if (lc->type != LOC_GATE) digcell(lc->on,lc->x,lc->y); } void maybe_dig_one(LOC *lc) { if ((lc->type != LOC_GATE) && rnd(2)) digcell(lc->on,lc->x,lc->y); } lv = &levels[L_EARTH]; for (x=LEV_X-1;x>=0;x--) for (y=LEV_Y-1;y>=0;y--) { l = &lv->cells[x][y]; if (l->type == LOC_CAVE) l->type = LOC_ROCK; } all_neighbours(gate_elem_dungeon[L_EARTH],&dig_one); all_neighbours(gate_elem_uworld[L_EARTH],&dig_one); for (i=roll("d4+2");i>0;i--) { x = rnd(LEV_X); y = rnd(LEV_Y); l = &lv->cells[x][y]; dig_one(l); all_neighbours(l,&maybe_dig_one); } } /* * Main routine, called during dungeon generation, for creating the * special levels. We create the four elemental planes, then link * them up with gates in a random order (we use elem_order both to * shuffle the order in and to store it for use elsewhere). We also * create the first gate, in the main dungeon, and the Underworld and * Hell levels, including their gates. */ void makespeciallevels(void) { int i; LEVEL *llv; LEVEL *lv; LOC *g; make_earth(); make_air(); make_water(); make_fire(); // This initialization assumes 4 elemental planes.... #if L__NELEM != 4 #error "makespeciallevels assumes 4 elemental planes" #endif elem_order[0] = L_EARTH; elem_order[1] = L_AIR; elem_order[2] = L_FIRE; elem_order[3] = L_WATER; for (i=0;i