/* * static char *room_gen_onion_c = * "$Id: room_gen_onion.c 11578 2009-02-23 22:02:27Z lalo $"; */ /* CrossFire, A Multiplayer game for X-windows Copyright (C) 2001 Mark Wedel & Crossfire Development Team Copyright (C) 1992 Frank Tore Johansen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. The authors can be reached via e-mail at crossfire-devel@real-time.com */ /** * @file * The onion room generator: Onion rooms are like this: char **map_gen_onion(int xsize, int ysize, int option, int layers); like this: regular random centered, linear onion bottom/right centered, nonlinear ######################### ######################### # # # # # ######## ########## # # ##################### # # # # # # # # # ###### ######## # # # # # # # # # # # # # ######## ######## # # # #### ###### # # # # # # # # # # # # # # # # # # # # # # ############ # # # # # # ########### ## # # # # # # # # # # # # # ################ # # # # # # ######### # # # # # # # # # # #################### # # # # # # # # # # # # # # ######################### ######################### */ #include #include #include #ifndef MIN #define MIN(x, y) (((x) < (y)) ? (x) : (y)) #endif void centered_onion(char **maze, int xsize, int ysize, int option, int layers); void bottom_centered_onion(char **maze, int xsize, int ysize, int option, int layers); void bottom_right_centered_onion(char **maze, int xsize, int ysize, int option, int layers); void draw_onion(char **maze, float *xlocations, float *ylocations, int layers); void make_doors(char **maze, float *xlocations, float *ylocations, int layers, int options); /** * Generates an onion layout. * @param xsize * @param ysize * layout size. * @param option * combination of @ref OPT_xxx "OPT_xxx" values. * @param layers * number of layers the onion should have. * @return * layout. */ char **map_gen_onion(int xsize, int ysize, int option, int layers) { int i, j; /* allocate that array, set it up */ char **maze = (char **)calloc(sizeof(char *), xsize); for (i = 0; i < xsize; i++) { maze[i] = (char *)calloc(sizeof(char), ysize); } /* pick some random options if option = 0 */ if (option == 0) { switch (RANDOM()%3) { case 0: option |= OPT_CENTERED; break; case 1: option |= OPT_BOTTOM_C; break; case 2: option |= OPT_BOTTOM_R; break; } if (RANDOM()%2) option |= OPT_LINEAR; if (RANDOM()%2) option |= OPT_IRR_SPACE; } /* write the outer walls, if appropriate. */ if (!(option&OPT_WALL_OFF)) { for (i = 0; i < xsize; i++) maze[i][0] = maze[i][ysize-1] = '#'; for (j = 0; j < ysize; j++) maze[0][j] = maze[xsize-1][j] = '#'; }; if (option&OPT_WALLS_ONLY) return maze; /* pick off the mutually exclusive options */ if (option&OPT_BOTTOM_R) bottom_right_centered_onion(maze, xsize, ysize, option, layers); else if (option&OPT_BOTTOM_C) bottom_centered_onion(maze, xsize, ysize, option, layers); else if (option&OPT_CENTERED) centered_onion(maze, xsize, ysize, option, layers); return maze; } /** * Creates a centered onion layout. * @param maze * layout. * @param xsize * @param ysize * layout size. * @param option * combination of @ref OPT_xxx "OPT_xxx" values. * @param layers * number of layers to create. */ void centered_onion(char **maze, int xsize, int ysize, int option, int layers) { int i, maxlayers; float *xlocations; float *ylocations; maxlayers = (MIN(xsize, ysize)-2)/5; if (!maxlayers) return; /* map too small to onionize */ if (layers > maxlayers) layers = maxlayers; if (layers == 0) layers = (RANDOM()%maxlayers)+1; xlocations = (float *)calloc(sizeof(float), 2*layers); ylocations = (float *)calloc(sizeof(float), 2*layers); /* place all the walls */ if (option&OPT_IRR_SPACE) { /* randomly spaced */ int x_spaces_available, y_spaces_available; /* the "extra" spaces available for spacing between layers */ x_spaces_available = (xsize-2)-6*layers+1; y_spaces_available = (ysize-2)-6*layers+1; /* pick an initial random pitch */ for (i = 0; i < 2*layers; i++) { float xpitch = 2, ypitch = 2; if (x_spaces_available > 0) xpitch = 2 +(RANDOM()%x_spaces_available +RANDOM()%x_spaces_available +RANDOM()%x_spaces_available)/3; if (y_spaces_available > 0) ypitch = 2 +(RANDOM()%y_spaces_available +RANDOM()%y_spaces_available +RANDOM()%y_spaces_available)/3; xlocations[i] = ((i > 0) ? xlocations[i-1] : 0)+xpitch; ylocations[i] = ((i > 0) ? ylocations[i-1] : 0)+ypitch; x_spaces_available -= xpitch-2; y_spaces_available -= ypitch-2; } } if (!(option&OPT_IRR_SPACE)) { /* evenly spaced */ float xpitch, ypitch; /* pitch of the onion layers */ xpitch = (xsize-2.0)/(2.0*layers+1); ypitch = (ysize-2.0)/(2.0*layers+1); xlocations[0] = xpitch; ylocations[0] = ypitch; for (i = 1; i < 2*layers; i++) { xlocations[i] = xlocations[i-1]+xpitch; ylocations[i] = ylocations[i-1]+ypitch; } } /* draw all the onion boxes. */ draw_onion(maze, xlocations, ylocations, layers); make_doors(maze, xlocations, ylocations, layers, option); } /** * Create a bottom-centered layout. * @param maze * layout. * @param xsize * @param ysize * layout size. * @param option * combination of @ref OPT_xxx "OPT_xxx" values. * @param layers * number of layers to create. */ void bottom_centered_onion(char **maze, int xsize, int ysize, int option, int layers) { int i, maxlayers; float *xlocations; float *ylocations; maxlayers = (MIN(xsize, ysize)-2)/5; if (!maxlayers) return; /* map too small to onionize */ if (layers > maxlayers) layers = maxlayers; if (layers == 0) layers = (RANDOM()%maxlayers)+1; xlocations = (float *)calloc(sizeof(float), 2*layers); ylocations = (float *)calloc(sizeof(float), 2*layers); /* place all the walls */ if (option&OPT_IRR_SPACE) { /* randomly spaced */ int x_spaces_available, y_spaces_available; /* the "extra" spaces available for spacing between layers */ x_spaces_available = (xsize-2)-6*layers+1; y_spaces_available = (ysize-2)-3*layers+1; /* pick an initial random pitch */ for (i = 0; i < 2*layers; i++) { float xpitch = 2, ypitch = 2; if (x_spaces_available > 0) xpitch = 2 +(RANDOM()%x_spaces_available +RANDOM()%x_spaces_available +RANDOM()%x_spaces_available)/3; if (y_spaces_available > 0) ypitch = 2 +(RANDOM()%y_spaces_available +RANDOM()%y_spaces_available +RANDOM()%y_spaces_available)/3; xlocations[i] = ((i > 0) ? xlocations[i-1] : 0)+xpitch; if (i < layers) ylocations[i] = ((i > 0) ? ylocations[i-1] : 0)+ypitch; else ylocations[i] = ysize-1; x_spaces_available -= xpitch-2; y_spaces_available -= ypitch-2; } } if (!(option&OPT_IRR_SPACE)) { /* evenly spaced */ float xpitch, ypitch; /* pitch of the onion layers */ xpitch = (xsize-2.0)/(2.0*layers+1); ypitch = (ysize-2.0)/(layers+1); xlocations[0] = xpitch; ylocations[0] = ypitch; for (i = 1; i < 2*layers; i++) { xlocations[i] = xlocations[i-1]+xpitch; if (i < layers) ylocations[i] = ylocations[i-1]+ypitch; else ylocations[i] = ysize-1; } } /* draw all the onion boxes. */ draw_onion(maze, xlocations, ylocations, layers); make_doors(maze, xlocations, ylocations, layers, option); } /** * Draws the lines in the maze defining the onion layers. * @param maze * map to draw to. * @param xlocations * @param ylocations * array of locations. * @param layers * number of layers. * @todo * explain what locations arrays should be, and the meaning of layers. */ void draw_onion(char **maze, float *xlocations, float *ylocations, int layers) { int i, j, l; for (l = 0; l < layers; l++) { int x1, x2, y1, y2; /* horizontal segments */ y1 = (int)ylocations[l]; y2 = (int)ylocations[2*layers-l-1]; for (i = (int)xlocations[l]; i <= (int)xlocations[2*layers-l-1]; i++) { maze[i][y1] = '#'; maze[i][y2] = '#'; } /* vertical segments */ x1 = (int)xlocations[l]; x2 = (int)xlocations[2*layers-l-1]; for (j = (int)ylocations[l]; j <= (int)ylocations[2*layers-l-1]; j++) { maze[x1][j] = '#'; maze[x2][j] = '#'; } } } /** * Add doors to the layout. * @param maze * map to draw to. * @param xlocations * @param ylocations * array of locations. Will be free()d. * @param layers * number of layers. * @param options * combination of @ref OPT_xxx "OPT_xxx" values. * @todo * explain what locations arrays should be, and the meaning of layers. */ void make_doors(char **maze, float *xlocations, float *ylocations, int layers, int options) { int freedoms; /* number of different walls on which we could place a door */ int which_wall; /* left, 1, top, 2, right, 3, bottom 4 */ int l, x1 = 0, x2, y1 = 0, y2; freedoms = 4; /* centered */ if (options&OPT_BOTTOM_C) freedoms = 3; if (options&OPT_BOTTOM_R) freedoms = 2; if (layers <= 0) return; /* pick which wall will have a door. */ which_wall = RANDOM()%freedoms+1; for (l = 0; l < layers; l++) { if (options&OPT_LINEAR) { /* linear door placement. */ switch (which_wall) { case 1: { /* left hand wall */ x1 = (int)xlocations[l]; y1 = (int)((ylocations[l]+ylocations[2*layers-l-1])/2); break; } case 2: { /* top wall placement */ x1 = (int)((xlocations[l]+xlocations[2*layers-l-1])/2); y1 = (int)ylocations[l]; break; } case 3: { /* right wall placement */ x1 = (int)xlocations[2*layers-l-1]; y1 = (int)((ylocations[l]+ylocations[2*layers-l-1])/2); break; } case 4: { /* bottom wall placement */ x1 = (int)((xlocations[l]+xlocations[2*layers-l-1])/2); y1 = (int)ylocations[2*layers-l-1]; break; } } } else { /* random door placement. */ which_wall = RANDOM()%freedoms+1; switch (which_wall) { case 1: { /* left hand wall */ x1 = (int)xlocations[l]; y2 = ylocations[2*layers-l-1]-ylocations[l]-1; if (y2 > 0) y1 = ylocations[l]+RANDOM()%y2+1; else y1 = ylocations[l]+1; break; } case 2: { /* top wall placement */ x2 = (int)((-xlocations[l]+xlocations[2*layers-l-1]))-1; if (x2 > 0) x1 = xlocations[l]+RANDOM()%x2+1; else x1 = xlocations[l]+1; y1 = (int)ylocations[l]; break; } case 3: { /* right wall placement */ x1 = (int)xlocations[2*layers-l-1]; y2 = (int)((-ylocations[l]+ylocations[2*layers-l-1]))-1; if (y2 > 0) y1 = ylocations[l]+RANDOM()%y2+1; else y1 = ylocations[l]+1; break; } case 4: { /* bottom wall placement */ x2 = (int)((-xlocations[l]+xlocations[2*layers-l-1]))-1; if (x2 > 0) x1 = xlocations[l]+RANDOM()%x2+1; else x1 = xlocations[l]+1; y1 = (int)ylocations[2*layers-l-1]; break; } } } if (options&OPT_NO_DOORS) maze[x1][y1] = '#'; /* no door. */ else maze[x1][y1] = 'D'; /* write the door */ } /* mark the center of the maze with a C */ l = layers-1; x1 = (int)(xlocations[l]+xlocations[2*layers-l-1])/2; y1 = (int)(ylocations[l]+ylocations[2*layers-l-1])/2; maze[x1][y1] = 'C'; /* not needed anymore */ free(xlocations); free(ylocations); } /** * Create a bottom-right-centered layout. * @param maze * layout. * @param xsize * @param ysize * layout size. * @param option * combination of @ref OPT_xxx "OPT_xxx" values. * @param layers * number of layers to create. */ void bottom_right_centered_onion(char **maze, int xsize, int ysize, int option, int layers) { int i, maxlayers; float *xlocations; float *ylocations; maxlayers = (MIN(xsize, ysize)-2)/5; if (!maxlayers) return; /* map too small to onionize */ if (layers > maxlayers) layers = maxlayers; if (layers == 0) layers = (RANDOM()%maxlayers)+1; xlocations = (float *)calloc(sizeof(float), 2*layers); ylocations = (float *)calloc(sizeof(float), 2*layers); /* place all the walls */ if (option&OPT_IRR_SPACE) { /* randomly spaced */ int x_spaces_available, y_spaces_available; /* the "extra" spaces available for spacing between layers */ x_spaces_available = (xsize-2)-3*layers+1; y_spaces_available = (ysize-2)-3*layers+1; /* pick an initial random pitch */ for (i = 0; i < 2*layers; i++) { float xpitch = 2, ypitch = 2; if (x_spaces_available > 0) xpitch = 2 +(RANDOM()%x_spaces_available +RANDOM()%x_spaces_available +RANDOM()%x_spaces_available)/3; if (y_spaces_available > 0) ypitch = 2 +(RANDOM()%y_spaces_available +RANDOM()%y_spaces_available +RANDOM()%y_spaces_available)/3; if (i < layers) xlocations[i] = ((i > 0) ? xlocations[i-1] : 0)+xpitch; else xlocations[i] = xsize-1; if (i < layers) ylocations[i] = ((i > 0) ? ylocations[i-1] : 0)+ypitch; else ylocations[i] = ysize-1; x_spaces_available -= xpitch-2; y_spaces_available -= ypitch-2; } } if (!(option&OPT_IRR_SPACE)) { /* evenly spaced */ float xpitch, ypitch; /* pitch of the onion layers */ xpitch = (xsize-2.0)/(2.0*layers+1); ypitch = (ysize-2.0)/(layers+1); xlocations[0] = xpitch; ylocations[0] = ypitch; for (i = 1; i < 2*layers; i++) { if (i < layers) xlocations[i] = xlocations[i-1]+xpitch; else xlocations[i] = xsize-1; if (i < layers) ylocations[i] = ylocations[i-1]+ypitch; else ylocations[i] = ysize-1; } } /* draw all the onion boxes. */ draw_onion(maze, xlocations, ylocations, layers); make_doors(maze, xlocations, ylocations, layers, option); }