Crossfire Server, Trunk  R20513
room_gen_onion.c
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2013 Mark Wedel and the Crossfire Development Team
5  * Copyright (c) 1992 Frank Tore Johansen
6  *
7  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
8  * welcome to redistribute it under certain conditions. For details, please
9  * see COPYING and LICENSE.
10  *
11  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
12  */
13 
43 #include <stdlib.h>
44 #include <global.h>
45 #include <random_map.h>
46 
47 #ifndef MIN
48 #define MIN(x, y) (((x) < (y)) ? (x) : (y))
49 #endif
50 
51 void centered_onion(char **maze, int xsize, int ysize, int option, int layers);
52 void bottom_centered_onion(char **maze, int xsize, int ysize, int option, int layers);
53 void bottom_right_centered_onion(char **maze, int xsize, int ysize, int option, int layers);
54 
55 void draw_onion(char **maze, float *xlocations, float *ylocations, int layers);
56 void make_doors(char **maze, float *xlocations, float *ylocations, int layers, int options);
57 
70 char **map_gen_onion(int xsize, int ysize, int option, int layers)
71 {
72  int i, j;
73 
74  /* allocate that array, set it up */
75  char **maze = (char **)calloc(xsize, sizeof(char *));
76  for (i = 0; i < xsize; i++) {
77  maze[i] = (char *)calloc(ysize, sizeof(char));
78  }
79 
80  /* pick some random options if option = 0 */
81  if (option == 0) {
82  switch (RANDOM()%3) {
83  case 0:
84  option |= OPT_CENTERED;
85  break;
86 
87  case 1:
88  option |= OPT_BOTTOM_C;
89  break;
90 
91  case 2:
92  option |= OPT_BOTTOM_R;
93  break;
94  }
95  if (RANDOM()%2) {
96  option |= OPT_LINEAR;
97  }
98  if (RANDOM()%2) {
99  option |= OPT_IRR_SPACE;
100  }
101  }
102 
103  /* write the outer walls, if appropriate. */
104  if (!(option&OPT_WALL_OFF)) {
105  for (i = 0; i < xsize; i++) {
106  maze[i][0] = maze[i][ysize-1] = '#';
107  }
108  for (j = 0; j < ysize; j++) {
109  maze[0][j] = maze[xsize-1][j] = '#';
110  }
111  };
112 
113  if (option&OPT_WALLS_ONLY) {
114  return maze;
115  }
116 
117  /* pick off the mutually exclusive options */
118  if (option&OPT_BOTTOM_R) {
119  bottom_right_centered_onion(maze, xsize, ysize, option, layers);
120  } else if (option&OPT_BOTTOM_C) {
121  bottom_centered_onion(maze, xsize, ysize, option, layers);
122  } else if (option&OPT_CENTERED) {
123  centered_onion(maze, xsize, ysize, option, layers);
124  }
125 
126  return maze;
127 }
128 
141 void centered_onion(char **maze, int xsize, int ysize, int option, int layers)
142 {
143  int i, maxlayers;
144  float *xlocations;
145  float *ylocations;
146 
147  maxlayers = (MIN(xsize, ysize)-2)/5;
148  if (!maxlayers) {
149  return; /* map too small to onionize */
150  }
151  if (layers > maxlayers) {
152  layers = maxlayers;
153  }
154  if (layers == 0) {
155  layers = (RANDOM()%maxlayers)+1;
156  }
157  xlocations = (float *)calloc(2 * layers, sizeof(float));
158  ylocations = (float *)calloc(2 * layers, sizeof(float));
159 
160  /* place all the walls */
161  if (option&OPT_IRR_SPACE) { /* randomly spaced */
162  int x_spaces_available, y_spaces_available;
163  /* the "extra" spaces available for spacing between layers */
164  x_spaces_available = (xsize-2)-6*layers+1;
165  y_spaces_available = (ysize-2)-6*layers+1;
166 
167  /* pick an initial random pitch */
168  for (i = 0; i < 2*layers; i++) {
169  float xpitch = 2, ypitch = 2;
170 
171  if (x_spaces_available > 0)
172  xpitch = 2
173  +(RANDOM()%x_spaces_available
174  +RANDOM()%x_spaces_available
175  +RANDOM()%x_spaces_available)/3;
176 
177  if (y_spaces_available > 0)
178  ypitch = 2
179  +(RANDOM()%y_spaces_available
180  +RANDOM()%y_spaces_available
181  +RANDOM()%y_spaces_available)/3;
182  xlocations[i] = ((i > 0) ? xlocations[i-1] : 0)+xpitch;
183  ylocations[i] = ((i > 0) ? ylocations[i-1] : 0)+ypitch;
184  x_spaces_available -= xpitch-2;
185  y_spaces_available -= ypitch-2;
186  }
187  }
188  if (!(option&OPT_IRR_SPACE)) {
189  /* evenly spaced */
190  float xpitch, ypitch; /* pitch of the onion layers */
191 
192  xpitch = (xsize-2.0)/(2.0*layers+1);
193  ypitch = (ysize-2.0)/(2.0*layers+1);
194  xlocations[0] = xpitch;
195  ylocations[0] = ypitch;
196  for (i = 1; i < 2*layers; i++) {
197  xlocations[i] = xlocations[i-1]+xpitch;
198  ylocations[i] = ylocations[i-1]+ypitch;
199  }
200  }
201 
202  /* draw all the onion boxes. */
203  draw_onion(maze, xlocations, ylocations, layers);
204  make_doors(maze, xlocations, ylocations, layers, option);
205 }
206 
219 void bottom_centered_onion(char **maze, int xsize, int ysize, int option, int layers)
220 {
221  int i, maxlayers;
222  float *xlocations;
223  float *ylocations;
224 
225  maxlayers = (MIN(xsize, ysize)-2)/5;
226  if (!maxlayers) {
227  return; /* map too small to onionize */
228  }
229  if (layers > maxlayers) {
230  layers = maxlayers;
231  }
232  if (layers == 0) {
233  layers = (RANDOM()%maxlayers)+1;
234  }
235  xlocations = (float *)calloc(2 * layers, sizeof(float));
236  ylocations = (float *)calloc(2 * layers, sizeof(float));
237 
238  /* place all the walls */
239  if (option&OPT_IRR_SPACE) { /* randomly spaced */
240  int x_spaces_available, y_spaces_available;
241  /* the "extra" spaces available for spacing between layers */
242  x_spaces_available = (xsize-2)-6*layers+1;
243  y_spaces_available = (ysize-2)-3*layers+1;
244 
245  /* pick an initial random pitch */
246  for (i = 0; i < 2*layers; i++) {
247  float xpitch = 2, ypitch = 2;
248 
249  if (x_spaces_available > 0)
250  xpitch = 2
251  +(RANDOM()%x_spaces_available
252  +RANDOM()%x_spaces_available
253  +RANDOM()%x_spaces_available)/3;
254 
255  if (y_spaces_available > 0)
256  ypitch = 2
257  +(RANDOM()%y_spaces_available
258  +RANDOM()%y_spaces_available
259  +RANDOM()%y_spaces_available)/3;
260  xlocations[i] = ((i > 0) ? xlocations[i-1] : 0)+xpitch;
261  if (i < layers) {
262  ylocations[i] = ((i > 0) ? ylocations[i-1] : 0)+ypitch;
263  } else {
264  ylocations[i] = ysize-1;
265  }
266  x_spaces_available -= xpitch-2;
267  y_spaces_available -= ypitch-2;
268  }
269  }
270 
271  if (!(option&OPT_IRR_SPACE)) {
272  /* evenly spaced */
273  float xpitch, ypitch; /* pitch of the onion layers */
274 
275  xpitch = (xsize-2.0)/(2.0*layers+1);
276  ypitch = (ysize-2.0)/(layers+1);
277  xlocations[0] = xpitch;
278  ylocations[0] = ypitch;
279  for (i = 1; i < 2*layers; i++) {
280  xlocations[i] = xlocations[i-1]+xpitch;
281  if (i < layers) {
282  ylocations[i] = ylocations[i-1]+ypitch;
283  } else {
284  ylocations[i] = ysize-1;
285  }
286  }
287  }
288 
289  /* draw all the onion boxes. */
290  draw_onion(maze, xlocations, ylocations, layers);
291  make_doors(maze, xlocations, ylocations, layers, option);
292 }
293 
306 void draw_onion(char **maze, float *xlocations, float *ylocations, int layers)
307 {
308  int i, j, l;
309 
310  for (l = 0; l < layers; l++) {
311  int x1, x2, y1, y2;
312 
313  /* horizontal segments */
314  y1 = (int)ylocations[l];
315  y2 = (int)ylocations[2*layers-l-1];
316  for (i = (int)xlocations[l]; i <= (int)xlocations[2*layers-l-1]; i++) {
317  maze[i][y1] = '#';
318  maze[i][y2] = '#';
319  }
320 
321  /* vertical segments */
322  x1 = (int)xlocations[l];
323  x2 = (int)xlocations[2*layers-l-1];
324  for (j = (int)ylocations[l]; j <= (int)ylocations[2*layers-l-1]; j++) {
325  maze[x1][j] = '#';
326  maze[x2][j] = '#';
327  }
328  }
329 }
330 
345 void make_doors(char **maze, float *xlocations, float *ylocations, int layers, int options)
346 {
347  int freedoms; /* number of different walls on which we could place a door */
348  int which_wall; /* left, 1, top, 2, right, 3, bottom 4 */
349  int l, x1 = 0, x2, y1 = 0, y2;
350 
351  freedoms = 4; /* centered */
352  if (options&OPT_BOTTOM_C) {
353  freedoms = 3;
354  }
355  if (options&OPT_BOTTOM_R) {
356  freedoms = 2;
357  }
358  if (layers <= 0) {
359  goto cleanup;
360  }
361 
362  /* pick which wall will have a door. */
363  which_wall = RANDOM()%freedoms+1;
364  for (l = 0; l < layers; l++) {
365  if (options&OPT_LINEAR) { /* linear door placement. */
366  switch (which_wall) {
367  case 1: { /* left hand wall */
368  x1 = (int)xlocations[l];
369  y1 = (int)((ylocations[l]+ylocations[2*layers-l-1])/2);
370  break;
371  }
372 
373  case 2: { /* top wall placement */
374  x1 = (int)((xlocations[l]+xlocations[2*layers-l-1])/2);
375  y1 = (int)ylocations[l];
376  break;
377  }
378 
379  case 3: { /* right wall placement */
380  x1 = (int)xlocations[2*layers-l-1];
381  y1 = (int)((ylocations[l]+ylocations[2*layers-l-1])/2);
382  break;
383  }
384 
385  case 4: { /* bottom wall placement */
386  x1 = (int)((xlocations[l]+xlocations[2*layers-l-1])/2);
387  y1 = (int)ylocations[2*layers-l-1];
388  break;
389  }
390  }
391  } else { /* random door placement. */
392  which_wall = RANDOM()%freedoms+1;
393  switch (which_wall) {
394  case 1: { /* left hand wall */
395  x1 = (int)xlocations[l];
396  y2 = ylocations[2*layers-l-1]-ylocations[l]-1;
397  if (y2 > 0) {
398  y1 = ylocations[l]+RANDOM()%y2+1;
399  } else {
400  y1 = ylocations[l]+1;
401  }
402  break;
403  }
404 
405  case 2: { /* top wall placement */
406  x2 = (int)((-xlocations[l]+xlocations[2*layers-l-1]))-1;
407  if (x2 > 0) {
408  x1 = xlocations[l]+RANDOM()%x2+1;
409  } else {
410  x1 = xlocations[l]+1;
411  }
412  y1 = (int)ylocations[l];
413  break;
414  }
415 
416  case 3: { /* right wall placement */
417  x1 = (int)xlocations[2*layers-l-1];
418  y2 = (int)((-ylocations[l]+ylocations[2*layers-l-1]))-1;
419  if (y2 > 0) {
420  y1 = ylocations[l]+RANDOM()%y2+1;
421  } else {
422  y1 = ylocations[l]+1;
423  }
424  break;
425  }
426 
427  case 4: { /* bottom wall placement */
428  x2 = (int)((-xlocations[l]+xlocations[2*layers-l-1]))-1;
429  if (x2 > 0) {
430  x1 = xlocations[l]+RANDOM()%x2+1;
431  } else {
432  x1 = xlocations[l]+1;
433  }
434  y1 = (int)ylocations[2*layers-l-1];
435  break;
436  }
437  }
438  }
439 
440  if (options&OPT_NO_DOORS) {
441  maze[x1][y1] = '#'; /* no door. */
442  } else {
443  maze[x1][y1] = 'D'; /* write the door */
444  }
445  }
446 
447  /* mark the center of the maze with a C */
448  l = layers-1;
449  x1 = (int)(xlocations[l]+xlocations[2*layers-l-1])/2;
450  y1 = (int)(ylocations[l]+ylocations[2*layers-l-1])/2;
451  maze[x1][y1] = 'C';
452 
453 cleanup:
454  /* not needed anymore */
455  free(xlocations);
456  free(ylocations);
457 }
458 
471 void bottom_right_centered_onion(char **maze, int xsize, int ysize, int option, int layers)
472 {
473  int i, maxlayers;
474  float *xlocations;
475  float *ylocations;
476 
477  maxlayers = (MIN(xsize, ysize)-2)/5;
478  if (!maxlayers) {
479  return; /* map too small to onionize */
480  }
481  if (layers > maxlayers) {
482  layers = maxlayers;
483  }
484  if (layers == 0) {
485  layers = (RANDOM()%maxlayers)+1;
486  }
487  xlocations = (float *)calloc(2 * layers, sizeof(float));
488  ylocations = (float *)calloc(2 * layers, sizeof(float));
489 
490  /* place all the walls */
491  if (option&OPT_IRR_SPACE) { /* randomly spaced */
492  int x_spaces_available, y_spaces_available;
493  /* the "extra" spaces available for spacing between layers */
494  x_spaces_available = (xsize-2)-3*layers+1;
495  y_spaces_available = (ysize-2)-3*layers+1;
496 
497  /* pick an initial random pitch */
498  for (i = 0; i < 2*layers; i++) {
499  float xpitch = 2, ypitch = 2;
500 
501  if (x_spaces_available > 0)
502  xpitch = 2
503  +(RANDOM()%x_spaces_available
504  +RANDOM()%x_spaces_available
505  +RANDOM()%x_spaces_available)/3;
506 
507  if (y_spaces_available > 0)
508  ypitch = 2
509  +(RANDOM()%y_spaces_available
510  +RANDOM()%y_spaces_available
511  +RANDOM()%y_spaces_available)/3;
512  if (i < layers) {
513  xlocations[i] = ((i > 0) ? xlocations[i-1] : 0)+xpitch;
514  } else {
515  xlocations[i] = xsize-1;
516  }
517 
518  if (i < layers) {
519  ylocations[i] = ((i > 0) ? ylocations[i-1] : 0)+ypitch;
520  } else {
521  ylocations[i] = ysize-1;
522  }
523  x_spaces_available -= xpitch-2;
524  y_spaces_available -= ypitch-2;
525  }
526  }
527 
528  if (!(option&OPT_IRR_SPACE)) { /* evenly spaced */
529  float xpitch, ypitch; /* pitch of the onion layers */
530 
531  xpitch = (xsize-2.0)/(2.0*layers+1);
532  ypitch = (ysize-2.0)/(layers+1);
533  xlocations[0] = xpitch;
534  ylocations[0] = ypitch;
535  for (i = 1; i < 2*layers; i++) {
536  if (i < layers) {
537  xlocations[i] = xlocations[i-1]+xpitch;
538  } else {
539  xlocations[i] = xsize-1;
540  }
541  if (i < layers) {
542  ylocations[i] = ylocations[i-1]+ypitch;
543  } else {
544  ylocations[i] = ysize-1;
545  }
546  }
547  }
548 
549  /* draw all the onion boxes. */
550  draw_onion(maze, xlocations, ylocations, layers);
551  make_doors(maze, xlocations, ylocations, layers, option);
552 }
static struct Command_Line_Options options[]
Actual valid command line options.
Definition: init.c:284
Random map related variables.
#define MIN(x, y)
Global type definitions and header inclusions.
#define OPT_IRR_SPACE
Irregularly/randomly spaced layers (default: regular).
Definition: random_map.h:126
void cleanup(void)
Clean up everything and exit.
Definition: server.c:1137
#define OPT_CENTERED
Centered.
Definition: random_map.h:122
#define OPT_WALL_OFF
No outer wall.
Definition: random_map.h:127
#define OPT_LINEAR
Linear doors (default is nonlinear).
Definition: random_map.h:123
void make_doors(char **maze, float *xlocations, float *ylocations, int layers, int options)
Add doors to the layout.
#define OPT_BOTTOM_R
Bottom-right centered.
Definition: random_map.h:125
void centered_onion(char **maze, int xsize, int ysize, int option, int layers)
Creates a centered onion layout.
void draw_onion(char **maze, float *xlocations, float *ylocations, int layers)
Draws the lines in the maze defining the onion layers.
#define RANDOM()
Definition: define.h:679
#define OPT_BOTTOM_C
Bottom-centered.
Definition: random_map.h:124
#define OPT_WALLS_ONLY
Only walls.
Definition: random_map.h:128
void bottom_centered_onion(char **maze, int xsize, int ysize, int option, int layers)
Create a bottom-centered layout.
void bottom_right_centered_onion(char **maze, int xsize, int ysize, int option, int layers)
Create a bottom-right-centered layout.
char ** map_gen_onion(int xsize, int ysize, int option, int layers)
Generates an onion layout.
#define OPT_NO_DOORS
Place walls insead of doors.
Definition: random_map.h:129