Crossfire Server, Branch 1.12  R12190
rogue_layout.c
Go to the documentation of this file.
00001 
00006 #include <global.h>
00007 #include <random_map.h>
00008 #include <math.h>
00009 
00010 typedef struct {
00011     int x;
00012     int y;  /* coordinates of room centers */
00013 
00014     int sx;
00015     int sy;  /* sizes */
00016     int ax, ay, zx, zy;  /* coordinates of extrema of the rectangle */
00017 
00018     int rtype;  /* circle or rectangular */
00019 } Room;
00020 
00021 static int roguelike_place_room(Room *Rooms, int xsize, int ysize, int nrooms);
00022 static void roguelike_make_rooms(Room *Rooms, char **maze, int options);
00023 static void roguelike_link_rooms(Room *Rooms, char **maze, int xsize, int ysize);
00024 
00044 int surround_check(char **layout, int i, int j, int Xsize, int Ysize) {
00045     int surround_index = 0;
00046 
00047     if ((i > 0) && (layout[i-1][j] != 0 && layout[i-1][j] != '.'))
00048         surround_index += 1;
00049     if ((i < Xsize-1) && (layout[i+1][j] != 0 && layout[i+1][j] != '.'))
00050         surround_index += 2;
00051     if ((j > 0) && (layout[i][j-1] != 0 && layout[i][j-1] != '.'))
00052         surround_index += 4;
00053     if ((j < Ysize-1) && (layout[i][j+1] != 0 && layout[i][j+1] != '.'))
00054         surround_index += 8;
00055     return surround_index;
00056 }
00057 
00069 char **roguelike_layout_gen(int xsize, int ysize, int options) {
00070     int i, j;
00071     Room *Rooms = NULL;
00072     Room *walk;
00073     int nrooms = 0;
00074     int tries = 0;
00075 
00076     /* allocate that array, write walls everywhere up */
00077     char **maze = (char **)malloc(sizeof(char *)*xsize);
00078     for (i = 0; i < xsize; i++) {
00079         maze[i] = (char *)malloc(sizeof(char)*ysize);
00080         for (j = 0; j < ysize; j++)
00081             maze[i][j] = '#';
00082     }
00083 
00084     /* minimum room size is basically 5x5:  if xsize/ysize is
00085      less than 3x that then hollow things out, stick in
00086      a stairsup and stairs down, and exit */
00087 
00088     if (xsize < 11 || ysize < 11) {
00089         for (i = 1; i < xsize-1; i++)
00090             for (j = 1; j < ysize-1; j++)
00091                 maze[i][j] = 0;
00092         maze[(xsize-1)/2][(ysize-1)/2] = '>';
00093         maze[(xsize-1)/2][(ysize-1)/2+1] = '<';
00094         return maze;
00095     }
00096 
00097     /* decide on the number of rooms */
00098     nrooms = RANDOM()%10+6;
00099     Rooms = (Room *)calloc(nrooms+1, sizeof(Room));
00100 
00101     /* actually place the rooms */
00102     i = 0;
00103     while (tries < 450 && i < nrooms) {
00104         /* try to place the room */
00105         if (!roguelike_place_room(Rooms, xsize, ysize, nrooms))
00106             tries++;
00107         else
00108             i++;
00109     }
00110 
00111     if (i == 0) { /* no can do! */
00112         for (i = 1; i < xsize-1; i++)
00113             for (j = 1; j < ysize-1; j++)
00114                 maze[i][j] = 0;
00115         maze[(xsize-1)/2][(ysize-1)/2] = '>';
00116         maze[(xsize-1)/2][(ysize-1)/2+1] = '<';
00117         free(Rooms);
00118         return maze;
00119     }
00120 
00121     /* erase the areas occupied by the rooms */
00122     roguelike_make_rooms(Rooms, maze, options);
00123 
00124     roguelike_link_rooms(Rooms, maze, xsize, ysize);
00125 
00126     /* put in the stairs */
00127 
00128     maze[Rooms->x][Rooms->y] = '<';
00129     /* get the last one */
00130     for (walk = Rooms; walk->x != 0; walk++)
00131         ;
00132     /* back up one */
00133     walk--;
00134     if (walk == Rooms) {
00135         /* In this case, there is only a single room.  We don't want to
00136          * clobber are up exit (above) with a down exit, so put the
00137          * other exit one space up/down, depending which is a space
00138          * and not a wall.
00139          */
00140         if (maze[walk->x][walk->y+1] == '.')
00141             maze[walk->x][walk->y+1] = '>';
00142         else
00143             maze[walk->x][walk->y-1] = '>';
00144     } else
00145         maze[walk->x][walk->y] = '>';
00146 
00147     /* convert all the '.' to 0, we're through with the '.' */
00148     for (i = 0; i < xsize; i++)
00149         for (j = 0; j < ysize; j++) {
00150             if (maze[i][j] == '.')
00151                 maze[i][j] = 0;
00152             if (maze[i][j] == 'D') { /* remove bad door. */
00153                 int si = surround_check(maze, i, j, xsize, ysize);
00154 
00155                 if (si != 3 && si != 12) {
00156                     maze[i][j] = 0;
00157                     /* back up and recheck any nearby doors */
00158                     i = 0;
00159                     j = 0;
00160                 }
00161             }
00162         }
00163 
00164     free(Rooms);
00165     return maze;
00166 }
00167 
00180 static int roguelike_place_room(Room *Rooms, int xsize, int ysize, int nrooms) {
00181     int tx, ty;  /* trial center locations */
00182     int sx, sy;  /* trial sizes */
00183     int ax, ay;  /* min coords of rect */
00184     int zx, zy;  /* max coords of rect */
00185     int x_basesize;
00186     int y_basesize;
00187     Room *walk;
00188 
00189     /* decide on the base x and y sizes */
00190 
00191     x_basesize = xsize/isqrt(nrooms);
00192     y_basesize = ysize/isqrt(nrooms);
00193 
00194 
00195     tx = RANDOM()%xsize;
00196     ty = RANDOM()%ysize;
00197 
00198     /* generate a distribution of sizes centered about basesize */
00199     sx = (RANDOM()%x_basesize)+(RANDOM()%x_basesize)+(RANDOM()%x_basesize);
00200     sy = (RANDOM()%y_basesize)+(RANDOM()%y_basesize)+(RANDOM()%y_basesize);
00201     sy = (int)(sy*.5);   /* renormalize */
00202 
00203     /* find the corners */
00204     ax = tx-sx/2;
00205     zx = tx+sx/2+sx%2;
00206 
00207     ay = ty-sy/2;
00208     zy = ty+sy/2+sy%2;
00209 
00210     /* check to see if it's in the map */
00211     if (zx > xsize-1 || ax < 1)
00212         return 0;
00213     if (zy > ysize-1 || ay < 1)
00214         return 0;
00215 
00216     /* no small fish */
00217     if (sx < 3 || sy < 3)
00218         return 0;
00219 
00220     /* check overlap with existing rooms */
00221     for (walk = Rooms; walk->x != 0; walk++) {
00222         int dx = abs(tx-walk->x);
00223         int dy = abs(ty-walk->y);
00224 
00225         if ((dx < (walk->sx+sx)/2+2) && (dy < (walk->sy+sy)/2+2))
00226             return 0;
00227     }
00228 
00229     /* if we've got here, presumably the room is OK. */
00230 
00231     /* get a pointer to the first free room */
00232     for (walk = Rooms; walk->x != 0; walk++)
00233         ;
00234     walk->x = tx;
00235     walk->y = ty;
00236     walk->sx = sx;
00237     walk->sy = sy;
00238     walk->ax = ax;
00239     walk->ay = ay;
00240     walk->zx = zx;
00241     walk->zy = zy;
00242     return 1;  /* success */
00243 }
00244 
00254 static void roguelike_make_rooms(Room *Rooms, char **maze, int options) {
00255     int making_circle = 0;
00256     int i, j;
00257     int R;
00258     Room *walk;
00259 
00260     for (walk = Rooms; walk->x != 0; walk++) {
00261         /* first decide what shape to make */
00262         switch (options) {
00263         case 1:
00264             making_circle = 0;
00265             break;
00266 
00267         case 2:
00268             making_circle = 1;
00269             break;
00270 
00271         default:
00272             making_circle = ((RANDOM()%3 == 0) ? 1 : 0);
00273             break;
00274         }
00275 
00276         if (walk->sx < walk->sy)
00277             R = walk->sx/2;
00278         else
00279             R = walk->sy/2;
00280 
00281         /* enscribe a rectangle or a circle */
00282         for (i = walk->ax; i < walk->zx; i++)
00283             for (j = walk->ay; j < walk->zy; j++) {
00284                 if (!making_circle || ((int)(0.5+hypot(walk->x-i, walk->y-j))) <= R)
00285                     maze[i][j] = '.';
00286             }
00287     }
00288 }
00289 
00300 static void roguelike_link_rooms(Room *Rooms, char **maze, int xsize, int ysize) {
00301     Room *walk;
00302     int i, j;
00303 
00304     /* link each room to the previous room */
00305     if (Rooms[1].x == 0)
00306         return; /* only 1 room */
00307 
00308     for (walk = Rooms+1; walk->x != 0; walk++) {
00309         int x1 = walk->x;
00310         int y1 = walk->y;
00311         int x2 = (walk-1)->x;
00312         int y2 = (walk-1)->y;
00313         int in_wall = 0;
00314 
00315         if (RANDOM()%2) {     /* connect in x direction first */
00316             /* horizontal connect */
00317             /* swap (x1,y1) (x2,y2) if necessary */
00318 
00319             if (x2 < x1) {
00320                 int tx = x2, ty = y2;
00321                 x2 = x1;
00322                 y2 = y1;
00323                 x1 = tx;
00324                 y1 = ty;
00325             }
00326 
00327             j = y1;
00328             for (i = x1; i < x2; i++) {
00329                 if (in_wall == 0 && maze[i][j] == '#') {
00330                     in_wall = 1;
00331                     maze[i][j] = 'D';
00332                 } else if (in_wall && maze[i][j] == '.') {
00333                     in_wall = 0;
00334                     maze[i-1][j] = 'D';
00335                 } else if (maze[i][j] != 'D' && maze[i][j] != '.')
00336                     maze[i][j] = 0;
00337             }
00338             j = MIN(y1, y2);
00339             if (maze[i][j] == '.')
00340                 in_wall = 0;
00341             if (maze[i][j] == 0 || maze[i][j] == '#')
00342                 in_wall = 1;
00343             for (/* j set already */; j < MAX(y1, y2); j++) {
00344                 if (in_wall == 0 && maze[i][j] == '#') {
00345                     in_wall = 1;
00346                     maze[i][j] = 'D';
00347                 } else if (in_wall && maze[i][j] == '.') {
00348                     in_wall = 0;
00349                     maze[i][j-1] = 'D';
00350                 } else if (maze[i][j] != 'D' && maze[i][j] != '.')
00351                     maze[i][j] = 0;
00352             }
00353         } else { /* connect in y direction first */
00354             in_wall = 0;
00355             /* swap if necessary */
00356             if (y2 < y1) {
00357                 int tx = x2, ty = y2;
00358                 x2 = x1;
00359                 y2 = y1;
00360                 x1 = tx;
00361                 y1 = ty;
00362             }
00363             i = x1;
00364             /* vertical connect */
00365             for (j = y1; j < y2; j++) {
00366                 if (in_wall == 0 && maze[i][j] == '#') {
00367                     in_wall = 1;
00368                     maze[i][j] = 'D';
00369                 } else if (in_wall && maze[i][j] == '.') {
00370                     in_wall = 0;
00371                     maze[i][j-1] = 'D';
00372                 } else if (maze[i][j] != 'D' && maze[i][j] != '.')
00373                     maze[i][j] = 0;
00374             }
00375 
00376             i = MIN(x1, x2);
00377             if (maze[i][j] == '.')
00378                 in_wall = 0;
00379             if (maze[i][j] == 0 || maze[i][j] == '#')
00380                 in_wall = 1;
00381             for (/* i set already */; i < MAX(x1, x2); i++) {
00382                 if (in_wall == 0 && maze[i][j] == '#') {
00383                     in_wall = 1;
00384                     maze[i][j] = 'D';
00385                 } else if (in_wall && maze[i][j] == '.') {
00386                     in_wall = 0;
00387                     maze[i-1][j] = 'D';
00388                 } else
00389                     if (maze[i][j] != 'D' && maze[i][j] != '.')
00390                         maze[i][j] = 0;
00391 
00392             }
00393         }
00394     }
00395 }