/* * This file is part of Deliantra, the Roguelike Realtime MMORPG. * * Copyright (©) 2017,2018 Marc Alexander Lehmann / the Deliantra team * Copyright (©) 2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016 Marc Alexander Lehmann / Robin Redeker / the Deliantra team * Copyright (©) 2001 Mark Wedel & Crossfire Development Team * Copyright (©) 1992 Frank Tore Johansen * * Deliantra is free software: you can redistribute it and/or modify it under * the terms of the Affero GNU General Public License as published by the * Free Software Foundation, either version 3 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 Affero GNU General Public License * and the GNU General Public License along with this program. If not, see * . * * The authors can be reached via e-mail to */ /* 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 static void centered_onion (char **maze, int xsize, int ysize, int option, int layers); static void bottom_centered_onion (char **maze, int xsize, int ysize, int option, int layers); static void bottom_right_centered_onion (char **maze, int xsize, int ysize, int option, int layers); static void draw_onion (char **maze, float *xlocations, float *ylocations, int layers); static void make_doors (char **maze, float *xlocations, float *ylocations, int layers, int options); void map_gen_onion (layout &maze, int option, int layers) { int xsize = maze.w; int ysize = maze.h; maze.clear (); /* pick some random options if option = 0 */ if (option == 0) { switch (rmg_rndm (3)) { case 0: option |= RMOPT_CENTERED; break; case 1: option |= RMOPT_BOTTOM_C; break; case 2: option |= RMOPT_BOTTOM_R; break; } if (rmg_rndm (2)) option |= RMOPT_LINEAR; if (rmg_rndm (2)) option |= RMOPT_IRR_SPACE; } /* write the outer walls, if appropriate. */ if (!(option & RMOPT_WALL_OFF)) maze.border (); if (option & RMOPT_WALLS_ONLY) return; /* pick off the mutually exclusive options */ if (option & RMOPT_BOTTOM_R) bottom_right_centered_onion (maze, xsize, ysize, option, layers); else if (option & RMOPT_BOTTOM_C) bottom_centered_onion (maze, xsize, ysize, option, layers); else if (option & RMOPT_CENTERED) centered_onion (maze, xsize, ysize, option, layers); } static void centered_onion (char **maze, int xsize, int ysize, int option, int layers) { int i, maxlayers; maxlayers = (min (xsize, ysize) - 2) / 5; if (!maxlayers) return; /* map too small to onionize */ if (layers > maxlayers) layers = maxlayers; if (layers == 0) layers = rmg_rndm (maxlayers) + 1; float *xlocations = salloc0 (2 * layers); float *ylocations = salloc0 (2 * layers); /* place all the walls */ if (option & RMOPT_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 + (rmg_rndm (x_spaces_available) + rmg_rndm (x_spaces_available) + rmg_rndm (x_spaces_available)) / 3; if (y_spaces_available > 0) ypitch = 2 + (rmg_rndm (y_spaces_available) + rmg_rndm (y_spaces_available) + rmg_rndm (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 -= (int) (xpitch - 2); y_spaces_available -= (int) (ypitch - 2); } } if (!(option & RMOPT_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); sfree (xlocations, 2 * layers); sfree (ylocations, 2 * layers); } static void bottom_centered_onion (char **maze, int xsize, int ysize, int option, int layers) { int i, maxlayers; maxlayers = (min (xsize, ysize) - 2) / 5; if (!maxlayers) return; /* map too small to onionize */ if (layers > maxlayers) layers = maxlayers; if (layers == 0) layers = rmg_rndm (maxlayers) + 1; float *xlocations = salloc0 (2 * layers); float *ylocations = salloc0 (2 * layers); /* place all the walls */ if (option & RMOPT_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 + (rmg_rndm (x_spaces_available) + rmg_rndm (x_spaces_available) + rmg_rndm (x_spaces_available)) / 3; if (y_spaces_available > 0) ypitch = 2 + (rmg_rndm (y_spaces_available) + rmg_rndm (y_spaces_available) + rmg_rndm (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 -= (int) (xpitch - 2); y_spaces_available -= (int) (ypitch - 2); } } if (!(option & RMOPT_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); sfree (xlocations, 2 * layers); sfree (ylocations, 2 * layers); } /* draw_boxes: draws the lines in the maze defining the onion layers */ static 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] = '#'; } } } static 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 & RMOPT_BOTTOM_C) freedoms = 3; if (options & RMOPT_BOTTOM_R) freedoms = 2; if (layers <= 0) return; /* pick which wall will have a door. */ which_wall = rmg_rndm (freedoms) + 1; for (l = 0; l < layers; l++) { if (options & RMOPT_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 = rmg_rndm (freedoms) + 1; switch (which_wall) { case 1: { /* left hand wall */ x1 = (int) xlocations[l]; y2 = (int) (ylocations[2 * layers - l - 1] - ylocations[l] - 1); if (y2 > 0) y1 = (int) (ylocations[l] + rmg_rndm (y2) + 1); else y1 = (int) (ylocations[l] + 1); break; } case 2: { /* top wall placement */ x2 = (int) ((-xlocations[l] + xlocations[2 * layers - l - 1])) - 1; if (x2 > 0) x1 = (int) (xlocations[l] + rmg_rndm (x2) + 1); else x1 = (int) (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 = (int) (ylocations[l] + rmg_rndm (y2) + 1); else y1 = (int) (ylocations[l] + 1); break; } case 4: { /* bottom wall placement */ x2 = (int) ((-xlocations[l] + xlocations[2 * layers - l - 1])) - 1; if (x2 > 0) x1 = (int) (xlocations[l] + rmg_rndm (x2) + 1); else x1 = (int) (xlocations[l] + 1); y1 = (int) ylocations[2 * layers - l - 1]; break; } } } if (options & RMOPT_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'; } static void bottom_right_centered_onion (char **maze, int xsize, int ysize, int option, int layers) { int i, maxlayers; maxlayers = (min (xsize, ysize) - 2) / 5; if (!maxlayers) return; /* map too small to onionize */ if (layers > maxlayers) layers = maxlayers; if (layers == 0) layers = rmg_rndm (maxlayers) + 1; float *xlocations = salloc0 (2 * layers); float *ylocations = salloc0 (2 * layers); /* place all the walls */ if (option & RMOPT_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 + (rmg_rndm (x_spaces_available) + rmg_rndm (x_spaces_available) + rmg_rndm (x_spaces_available)) / 3; if (y_spaces_available > 0) ypitch = 2 + (rmg_rndm (y_spaces_available) + rmg_rndm (y_spaces_available) + rmg_rndm (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 -= (int) (xpitch - 2); y_spaces_available -= (int) (ypitch - 2); } } if (!(option & RMOPT_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); sfree (xlocations, 2 * layers); sfree (ylocations, 2 * layers); }