535 lines
17 KiB
C
535 lines
17 KiB
C
/*
|
|
* 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 <stdlib.h>
|
|
#include <global.h>
|
|
#include <random_map.h>
|
|
|
|
#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);
|
|
}
|