Atrinik Server 2.5
random_maps/rogue_layout.c
Go to the documentation of this file.
00001 /************************************************************************
00002 *            Atrinik, a Multiplayer Online Role Playing Game            *
00003 *                                                                       *
00004 *    Copyright (C) 2009-2011 Alex Tokar and Atrinik Development Team    *
00005 *                                                                       *
00006 * Fork from Daimonin (Massive Multiplayer Online Role Playing Game)     *
00007 * and Crossfire (Multiplayer game for X-windows).                       *
00008 *                                                                       *
00009 * This program is free software; you can redistribute it and/or modify  *
00010 * it under the terms of the GNU General Public License as published by  *
00011 * the Free Software Foundation; either version 2 of the License, or     *
00012 * (at your option) any later version.                                   *
00013 *                                                                       *
00014 * This program is distributed in the hope that it will be useful,       *
00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00017 * GNU General Public License for more details.                          *
00018 *                                                                       *
00019 * You should have received a copy of the GNU General Public License     *
00020 * along with this program; if not, write to the Free Software           *
00021 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.             *
00022 *                                                                       *
00023 * The author can be reached at admin@atrinik.org                        *
00024 ************************************************************************/
00025 
00030 #include <global.h>
00031 #include <math.h>
00032 
00033 typedef struct
00034 {
00035     /* coordinates of room centers */
00036     int x;
00037     int y;
00038 
00039     /* sizes */
00040     int sx;
00041     int sy;
00042 
00043     /* coordinates of extrema of the rectangle */
00044     int ax,ay,zx,zy;
00045 
00046     /* Circle or rectangular */
00047     int rtype;
00048 } Room;
00049 
00050 static int roguelike_place_room(Room *Rooms, int xsize, int ysize, int nrooms);
00051 static void roguelike_make_rooms(Room *Rooms, char **maze, int options);
00052 static void roguelike_link_rooms(Room *Rooms, char **maze);
00053 
00066 int surround_check(char **layout, int i, int j, int Xsize, int Ysize)
00067 {
00068     int surround_index = 0;
00069 
00070     if ((i > 0) && (layout[i - 1][j] != 0 && layout[i - 1][j] != '.'))
00071     {
00072         surround_index += 1;
00073     }
00074 
00075     if ((i < Xsize - 1) && (layout[i + 1][j] != 0 && layout[i + 1][j] != '.'))
00076     {
00077         surround_index += 2;
00078     }
00079 
00080     if ((j > 0) && (layout[i][j - 1] != 0 && layout[i][j - 1] != '.'))
00081     {
00082         surround_index += 4;
00083     }
00084 
00085     if ((j < Ysize - 1) && (layout[i][j + 1] != 0 && layout[i][j + 1] != '.'))
00086     {
00087         surround_index += 8;
00088     }
00089 
00090     return surround_index;
00091 }
00092 
00102 char **roguelike_layout_gen(int xsize, int ysize, int options)
00103 {
00104     int i, j = 0;
00105     Room *Rooms = NULL, *walk;
00106     int nrooms = 0;
00107     int tries = 0;
00108 
00109     /* Allocate that array, write walls everywhere up */
00110     char **maze = (char **) malloc(sizeof(char *) * xsize);
00111 
00112     for (i = 0; i < xsize; i++)
00113     {
00114         maze[i] = (char *) malloc(sizeof(char) * ysize);
00115 
00116         for (j = 0; j < ysize; j++)
00117         {
00118             maze[i][j] = '#';
00119         }
00120     }
00121 
00122     /* minimum room size is basically 5x5:  if xsize/ysize is
00123        less than 3x that then hollow things out, stick in
00124        a stairsup and stairs down, and exit */
00125     if (xsize < 11 || ysize < 11)
00126     {
00127         for (i = 1; i < xsize - 1; i++)
00128         {
00129             for (j = 1; j < ysize - 1; j++)
00130             {
00131                 maze[i][j] = 0;
00132             }
00133         }
00134 
00135         maze[i / 2][j / 2]= '>';
00136         maze[i / 2][j / 2 + 1] = '<';
00137 
00138         return maze;
00139     }
00140 
00141     /* decide on the number of rooms */
00142     nrooms = RANDOM() % 10 + 6;
00143     Rooms = (Room *) calloc(nrooms + 1 , sizeof(Room));
00144 
00145     /* Actually place the rooms */
00146     i = 0;
00147 
00148     while (tries < 450 && i < nrooms)
00149     {
00150         /* Try to place the room */
00151         if (!roguelike_place_room(Rooms, xsize, ysize, nrooms))
00152         {
00153             tries++;
00154         }
00155         else
00156         {
00157             i++;
00158         }
00159     }
00160 
00161     /* no can do! */
00162     if (i == 0)
00163     {
00164         for (i = 1; i < xsize - 1; i++)
00165         {
00166             for (j = 1; j < ysize - 1; j++)
00167             {
00168                 maze[i][j] = 0;
00169             }
00170         }
00171 
00172         maze[i / 2][j / 2] = '>';
00173         maze[i / 2][j / 2 + 1] = '<';
00174         free(Rooms);
00175 
00176         return maze;
00177     }
00178 
00179     /* Erase the areas occupied by the rooms */
00180     roguelike_make_rooms(Rooms, maze, options);
00181 
00182     roguelike_link_rooms(Rooms, maze);
00183 
00184     /* Put in the stairs */
00185     maze[Rooms->x][Rooms->y] = '<';
00186 
00187     /* Get the last one */
00188     for (walk = Rooms; walk->x != 0; walk++)
00189     {
00190     }
00191 
00192     /* Back up one */
00193     walk--;
00194 
00195     maze[walk->x][walk->y] = '>';
00196 
00197     /* convert all the '.' to 0, we're through with the '.' */
00198     for (i = 0; i < xsize; i++)
00199     {
00200         for (j = 0; j < ysize; j++)
00201         {
00202             if (maze[i][j] == '.')
00203             {
00204                 maze[i][j] = 0;
00205             }
00206 
00207             /* Remove bad door. */
00208             if (maze[i][j] == 'D')
00209             {
00210                 int si = surround_check(maze, i, j, xsize, ysize);
00211 
00212                 if (si != 3 && si != 12)
00213                 {
00214                     maze[i][j] = 0;
00215 
00216                     /* back up and recheck any nearby doors */
00217                     i = 0;
00218                     j = 0;
00219                 }
00220             }
00221         }
00222     }
00223 
00224     free(Rooms);
00225 
00226     return maze;
00227 }
00228 
00236 static int roguelike_place_room(Room *Rooms, int xsize, int ysize, int nrooms)
00237 {
00238     /* trial center locations */
00239     int tx, ty;
00240     /* trial sizes */
00241     int sx, sy;
00242     /* min coords of rect */
00243     int ax, ay;
00244     /* max coords of rect */
00245     int zx, zy;
00246     int x_basesize, y_basesize;
00247     Room *walk;
00248 
00249     /* Decide on the base x and y sizes */
00250     x_basesize = (int) (xsize / sqrt(nrooms));
00251     y_basesize = (int) (ysize / sqrt(nrooms));
00252 
00253     tx = RANDOM() % xsize;
00254     ty = RANDOM() % ysize;
00255 
00256     /* Generate a distribution of sizes centered about basesize */
00257     sx = (RANDOM() % x_basesize) + (RANDOM() % x_basesize)+ (RANDOM() % x_basesize);
00258     sy = (RANDOM() % y_basesize) + (RANDOM() % y_basesize)+ (RANDOM() % y_basesize);
00259 
00260     /* Renormalize */
00261     sy = (int) (sy * 0.5);
00262 
00263     /* Find the corners */
00264     ax = tx - sx / 2;
00265     zx = tx + sx / 2 + sx % 2;
00266 
00267     ay = ty - sy / 2;
00268     zy = ty + sy / 2 + sy % 2;
00269 
00270     /* Check to see if it's in the map */
00271     if (zx > xsize - 1 || ax < 1)
00272     {
00273         return 0;
00274     }
00275 
00276     if (zy > ysize - 1 || ay < 1)
00277     {
00278         return 0;
00279     }
00280 
00281     /* No small fish */
00282     if (sx < 3 || sy < 3)
00283     {
00284         return 0;
00285     }
00286 
00287     /* Check overlap with existing rooms */
00288     for (walk = Rooms; walk->x != 0; walk++)
00289     {
00290         int dx = abs(tx - walk->x), dy = abs(ty - walk->y);
00291 
00292         if ((dx < (walk->sx + sx) / 2 + 2) && (dy < (walk->sy + sy) / 2 + 2))
00293         {
00294             return 0;
00295         }
00296     }
00297 
00298     /* If we've got here, presumably the room is OK. */
00299 
00300     /* Get a pointer to the first free room */
00301     for (walk = Rooms; walk->x != 0; walk++)
00302     {
00303     }
00304 
00305     walk->x = tx;
00306     walk->y = ty;
00307     walk->sx = sx;
00308     walk->sy = sy;
00309     walk->ax = ax;
00310     walk->ay = ay;
00311     walk->zx = zx;
00312     walk->zy = zy;
00313 
00314     /* success */
00315     return 1;
00316 }
00317 
00324 static void roguelike_make_rooms(Room *Rooms, char **maze, int options)
00325 {
00326     int making_circle = 0, i, j, R = 0;
00327     Room *walk;
00328 
00329     for (walk = Rooms; walk->x != 0; walk++)
00330     {
00331         /* First decide what shape to make */
00332         switch (options)
00333         {
00334             case 1:
00335                 making_circle = 0;
00336                 break;
00337 
00338             case 2:
00339                 making_circle = 1;
00340                 break;
00341 
00342             default:
00343                 making_circle = ((RANDOM() % 3 == 0) ? 1 : 0);
00344 
00345                 if (walk->sx < walk->sy)
00346                 {
00347                     R = walk->sx / 2;
00348                 }
00349                 else
00350                 {
00351                     R = walk->sy / 2;
00352                 }
00353         }
00354 
00355         /* Enscribe a rectangle */
00356         for (i = walk->ax; i < walk->zx; i++)
00357         {
00358             for (j = walk->ay; j < walk->zy; j++)
00359             {
00360                 if (!making_circle || ((int) (0.5 + hypot(walk->x - i, walk->y - j))) <= R)
00361                 {
00362                     maze[i][j] = '.';
00363                 }
00364             }
00365         }
00366     }
00367 }
00368 
00375 static void roguelike_link_rooms(Room *Rooms, char **maze)
00376 {
00377     Room *walk;
00378     int i, j;
00379 
00380     /* Link each room to the previous room */
00381     if (Rooms[1].x == 0)
00382     {
00383         /* only 1 room */
00384         return;
00385     }
00386 
00387     for (walk = Rooms + 1; walk->x != 0; walk++)
00388     {
00389         int x = walk->x, y = walk->y, x2 = (walk - 1)->x, y2 = (walk - 1)->y, in_wall = 0;
00390 
00391         /* Connect in x direction first */
00392         if (RANDOM() % 2)
00393         {
00394             /* horizontal connect */
00395             /* swap (x1, y1) (x2, y2) if necessary */
00396             if (x2 < x)
00397             {
00398                 int tx = x2, ty = y2;
00399 
00400                 x2 = x;
00401                 y2 = y;
00402                 x = tx;
00403                 y = ty;
00404             }
00405 
00406             j = y;
00407 
00408             for (i = x; i < x2; i++)
00409             {
00410                 if (in_wall == 0 && maze[i][j] == '#')
00411                 {
00412                     in_wall = 1;
00413                     maze[i][j] = 'D';
00414                 }
00415                 else if (in_wall && maze[i][j] == '.')
00416                 {
00417                     in_wall = 0;
00418                     maze[i - 1][j] = 'D';
00419                 }
00420                 else if (maze[i][j] != 'D' && maze[i][j] != '.')
00421                 {
00422                     maze[i][j] = 0;
00423                 }
00424             }
00425 
00426             j = MIN(y, y2);
00427 
00428             if (maze[i][j] == '.')
00429             {
00430                 in_wall = 0;
00431             }
00432 
00433             if (maze[i][j] == 0 || maze[i][j] == '#')
00434             {
00435                 in_wall = 1;
00436             }
00437 
00438             for ( ; j < MAX(y, y2); j++)
00439             {
00440                 if (in_wall == 0 && maze[i][j] == '#')
00441                 {
00442                     in_wall = 1;
00443                     maze[i][j] = 'D';
00444                 }
00445                 else if (in_wall && maze[i][j] == '.')
00446                 {
00447                     in_wall = 0;
00448                     maze[i][j - 1] = 'D';
00449                 }
00450                 else if (maze[i][j] != 'D' && maze[i][j] != '.')
00451                 {
00452                     maze[i][j] = 0;
00453                 }
00454             }
00455         }
00456         /* Connect in y direction first */
00457         else
00458         {
00459             in_wall = 0;
00460 
00461             /* Swap if necessary */
00462             if (y2 < y)
00463             {
00464                 int tx = x2, ty = y2;
00465 
00466                 x2 = x;
00467                 y2 = y;
00468                 x = tx;
00469                 y = ty;
00470             }
00471 
00472             i = x;
00473 
00474             /* vertical connect */
00475             for (j = y; j < y2; j++)
00476             {
00477                 if (in_wall == 0 && maze[i][j] == '#')
00478                 {
00479                     in_wall = 1;
00480                     maze[i][j] = 'D';
00481                 }
00482                 else if (in_wall && maze[i][j] == '.')
00483                 {
00484                     in_wall = 0;
00485                     maze[i][j - 1] = 'D';
00486                 }
00487                 else if (maze[i][j] != 'D' && maze[i][j] != '.')
00488                 {
00489                     maze[i][j] = 0;
00490                 }
00491             }
00492 
00493             i = MIN(x, x2);
00494 
00495             if (maze[i][j] == '.')
00496             {
00497                 in_wall = 0;
00498             }
00499 
00500             if (maze[i][j] == 0 || maze[i][j] == '#')
00501             {
00502                 in_wall = 1;
00503             }
00504 
00505             for ( ; i < MAX(x, x2); i++)
00506             {
00507                 if (in_wall == 0 && maze[i][j] == '#')
00508                 {
00509                     in_wall = 1;
00510                     maze[i][j] = 'D';
00511                 }
00512                 else if (in_wall && maze[i][j] == '.')
00513                 {
00514                     in_wall = 0;
00515                     maze[i - 1][j] = 'D';
00516                 }
00517                 else if (maze[i][j] != 'D' && maze[i][j] != '.')
00518                 {
00519                     maze[i][j] = 0;
00520                 }
00521             }
00522         }
00523     }
00524 }