ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/random_maps/rogue_layout.C
Revision: 1.6
Committed: Sat Jan 27 02:19:37 2007 UTC (17 years, 4 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: rel-2_4, rel-2_2, rel-2_3, rel-2_0, rel-2_1, rel-2_32, rel-2_43, rel-2_42, rel-2_41
Changes since 1.5: +4 -6 lines
Log Message:
- experimentall.y determine minimum random map size (12)
- harden random map generator by stresstesting
- reduced codesize while increasing readability (RANDOM => rndm)
- fixed a likely unimportant bug in random_roll64
- possibly broke lots of code

File Contents

# User Rev Content
1 root 1.3
2 elmex 1.1 /* generate a rogue/nethack-like layout */
3     #include <global.h>
4     #include <random_map.h>
5     #include <math.h>
6    
7 root 1.3 typedef struct
8     {
9 elmex 1.1 int x;
10 root 1.3 int y; /* coordinates of room centers */
11 elmex 1.1
12     int sx;
13 root 1.3 int sy; /* sizes */
14     int ax, ay, zx, zy; /* coordinates of extrema of the rectangle */
15 elmex 1.1
16 root 1.3 int rtype; /* circle or rectangular */
17 elmex 1.1 } Room;
18    
19 root 1.3 static int roguelike_place_room (Room * Rooms, int xsize, int ysize, int nrooms);
20     static void roguelike_make_rooms (Room * Rooms, char **maze, int options);
21     static void roguelike_link_rooms (Room * Rooms, char **maze, int xsize, int ysize);
22 elmex 1.1
23 root 1.3 int
24     surround_check (char **layout, int i, int j, int Xsize, int Ysize)
25     {
26 elmex 1.1 /* 1 = wall to left,
27 root 1.3 2 = wall to right,
28     4 = wall above
29     8 = wall below */
30 elmex 1.1 int surround_index = 0;
31 root 1.3
32     if ((i > 0) && (layout[i - 1][j] != 0 && layout[i - 1][j] != '.'))
33     surround_index += 1;
34     if ((i < Xsize - 1) && (layout[i + 1][j] != 0 && layout[i + 1][j] != '.'))
35     surround_index += 2;
36     if ((j > 0) && (layout[i][j - 1] != 0 && layout[i][j - 1] != '.'))
37     surround_index += 4;
38     if ((j < Ysize - 1) && (layout[i][j + 1] != 0 && layout[i][j + 1] != '.'))
39     surround_index += 8;
40 elmex 1.1 return surround_index;
41     }
42    
43    
44     /* actually make the layout: we work by a reduction process:
45     * first we make everything a wall, then we remove areas to make rooms
46     */
47    
48 root 1.3 char **
49     roguelike_layout_gen (int xsize, int ysize, int options)
50     {
51     int i, j;
52     Room *Rooms = 0;
53 elmex 1.1 Room *walk;
54 root 1.3 int nrooms = 0;
55     int tries = 0;
56 elmex 1.1
57     /* allocate that array, write walls everywhere up */
58 root 1.3 char **maze = (char **) malloc (sizeof (char *) * xsize);
59    
60     for (i = 0; i < xsize; i++)
61     {
62     maze[i] = (char *) malloc (sizeof (char) * ysize);
63     for (j = 0; j < ysize; j++)
64     maze[i][j] = '#';
65     }
66 elmex 1.1
67     /* minimum room size is basically 5x5: if xsize/ysize is
68     less than 3x that then hollow things out, stick in
69     a stairsup and stairs down, and exit */
70    
71 root 1.3 if (xsize < 11 || ysize < 11)
72     {
73     for (i = 1; i < xsize - 1; i++)
74     for (j = 1; j < ysize - 1; j++)
75     maze[i][j] = 0;
76     maze[(xsize - 1) / 2][(ysize - 1) / 2] = '>';
77     maze[(xsize - 1) / 2][(ysize - 1) / 2 + 1] = '<';
78     return maze;
79     }
80 elmex 1.1
81     /* decide on the number of rooms */
82 root 1.5 nrooms = rndm (10) + 6;
83 root 1.3 Rooms = (Room *) calloc (nrooms + 1, sizeof (Room));
84 elmex 1.1
85     /* actually place the rooms */
86 root 1.3 i = 0;
87     while (tries < 450 && i < nrooms)
88     {
89     /* try to place the room */
90     if (!roguelike_place_room (Rooms, xsize, ysize, nrooms))
91     tries++;
92     else
93     i++;
94     }
95    
96     if (i == 0)
97     { /* no can do! */
98     for (i = 1; i < xsize - 1; i++)
99     for (j = 1; j < ysize - 1; j++)
100     maze[i][j] = 0;
101     maze[(xsize - 1) / 2][(ysize - 1) / 2] = '>';
102     maze[(xsize - 1) / 2][(ysize - 1) / 2 + 1] = '<';
103     free (Rooms);
104     return maze;
105     }
106    
107    
108 elmex 1.1 /* erase the areas occupied by the rooms */
109 root 1.3 roguelike_make_rooms (Rooms, maze, options);
110 elmex 1.1
111 root 1.3 roguelike_link_rooms (Rooms, maze, xsize, ysize);
112 elmex 1.1
113     /* put in the stairs */
114 root 1.3
115 elmex 1.1 maze[Rooms->x][Rooms->y] = '<';
116     /* get the last one */
117 root 1.3 for (walk = Rooms; walk->x != 0; walk++);
118 elmex 1.1 /* back up one */
119     walk--;
120 root 1.3 if (walk == Rooms)
121     {
122     /* In this case, there is only a single room. We don't want to
123     * clobber are up exit (above) with a down exit, so put the
124     * other exit one space up/down, depending which is a space
125     * and not a wall.
126     */
127     if (maze[walk->x][walk->y + 1] == '.')
128     maze[walk->x][walk->y + 1] = '>';
129     else
130     maze[walk->x][walk->y - 1] = '>';
131     }
132 elmex 1.1 else
133     maze[walk->x][walk->y] = '>';
134    
135     /* convert all the '.' to 0, we're through with the '.' */
136 root 1.3 for (i = 0; i < xsize; i++)
137     for (j = 0; j < ysize; j++)
138     {
139     if (maze[i][j] == '.')
140     maze[i][j] = 0;
141     if (maze[i][j] == 'D')
142     { /* remove bad door. */
143     int si = surround_check (maze, i, j, xsize, ysize);
144    
145     if (si != 3 && si != 12)
146     {
147     maze[i][j] = 0;
148     /* back up and recheck any nearby doors */
149     i = 0;
150     j = 0;
151     }
152     }
153 elmex 1.1 }
154 root 1.3
155     free (Rooms);
156 elmex 1.1 return maze;
157     }
158    
159    
160    
161 root 1.3 static int
162     roguelike_place_room (Room * Rooms, int xsize, int ysize, int nrooms)
163     {
164    
165     int tx, ty; /* trial center locations */
166     int sx, sy; /* trial sizes */
167     int ax, ay; /* min coords of rect */
168     int zx, zy; /* max coords of rect */
169 elmex 1.1 int x_basesize;
170     int y_basesize;
171     Room *walk;
172    
173     /* decide on the base x and y sizes */
174    
175 root 1.3 x_basesize = xsize / isqrt (nrooms);
176     y_basesize = ysize / isqrt (nrooms);
177 elmex 1.1
178 root 1.6 tx = rndm (xsize);
179     ty = rndm (ysize);
180 root 1.3
181 elmex 1.1 /* generate a distribution of sizes centered about basesize */
182 root 1.6 sx = rndm (x_basesize) + rndm (x_basesize) + rndm (x_basesize);
183     sy = rndm (y_basesize) + rndm (y_basesize) + rndm (y_basesize);
184 root 1.3 sy = (int) (sy * .5); /* renormalize */
185 elmex 1.1
186     /* find the corners */
187     ax = tx - sx / 2;
188 root 1.3 zx = tx + sx / 2 + sx % 2;
189 elmex 1.1
190     ay = ty - sy / 2;
191 root 1.3 zy = ty + sy / 2 + sy % 2;
192 elmex 1.1
193     /* check to see if it's in the map */
194 root 1.3 if (zx > xsize - 1 || ax < 1)
195     return 0;
196     if (zy > ysize - 1 || ay < 1)
197     return 0;
198    
199 elmex 1.1 /* no small fish */
200 root 1.3 if (sx < 3 || sy < 3)
201     return 0;
202 elmex 1.1
203     /* check overlap with existing rooms */
204 root 1.3 for (walk = Rooms; walk->x != 0; walk++)
205     {
206     int dx = abs (tx - walk->x);
207     int dy = abs (ty - walk->y);
208    
209     if ((dx < (walk->sx + sx) / 2 + 2) && (dy < (walk->sy + sy) / 2 + 2))
210     return 0;
211     }
212 elmex 1.1
213     /* if we've got here, presumably the room is OK. */
214    
215     /* get a pointer to the first free room */
216 root 1.3 for (walk = Rooms; walk->x != 0; walk++);
217 elmex 1.1 walk->x = tx;
218     walk->y = ty;
219     walk->sx = sx;
220     walk->sy = sy;
221     walk->ax = ax;
222     walk->ay = ay;
223     walk->zx = zx;
224     walk->zy = zy;
225 root 1.3 return 1; /* success */
226 elmex 1.1
227     }
228 root 1.3
229 elmex 1.1
230     /* write all the rooms into the maze */
231 root 1.3 static void
232     roguelike_make_rooms (Room * Rooms, char **maze, int options)
233     {
234     int making_circle = 0;
235     int i, j;
236 elmex 1.1 int R;
237     Room *walk;
238    
239 root 1.3 for (walk = Rooms; walk->x != 0; walk++)
240     {
241     /* first decide what shape to make */
242     switch (options)
243     {
244 root 1.4 case 1:
245     making_circle = 0;
246     break;
247     case 2:
248     making_circle = 1;
249     break;
250     default:
251 root 1.5 making_circle = ((rndm (3) == 0) ? 1 : 0);
252 root 1.4 break;
253 root 1.3 }
254    
255     if (walk->sx < walk->sy)
256     R = walk->sx / 2;
257     else
258     R = walk->sy / 2;
259    
260     /* enscribe a rectangle */
261     for (i = walk->ax; i < walk->zx; i++)
262     for (j = walk->ay; j < walk->zy; j++)
263     {
264     if (!making_circle || ((int) (0.5 + hypot (walk->x - i, walk->y - j))) <= R)
265     maze[i][j] = '.';
266     }
267 elmex 1.1 }
268     }
269    
270    
271    
272 root 1.3 static void
273     roguelike_link_rooms (Room * Rooms, char **maze, int xsize, int ysize)
274     {
275 elmex 1.1 Room *walk;
276 root 1.3 int i, j;
277    
278 elmex 1.1 /* link each room to the previous room */
279 root 1.3 if (Rooms[1].x == 0)
280     return; /* only 1 room */
281 elmex 1.1
282 root 1.3 for (walk = Rooms + 1; walk->x != 0; walk++)
283     {
284     int x1 = walk->x;
285     int y1 = walk->y;
286     int x2 = (walk - 1)->x;
287     int y2 = (walk - 1)->y;
288     int in_wall = 0;
289    
290 root 1.5 if (rndm (2))
291 root 1.3 { /* connect in x direction first */
292     /* horizontal connect */
293     /* swap (x1,y1) (x2,y2) if necessary */
294    
295     if (x2 < x1)
296     {
297     int tx = x2, ty = y2;
298    
299     x2 = x1;
300     y2 = y1;
301     x1 = tx;
302     y1 = ty;
303     }
304    
305    
306     j = y1;
307     for (i = x1; i < x2; i++)
308     {
309     if (in_wall == 0 && maze[i][j] == '#')
310     {
311     in_wall = 1;
312     maze[i][j] = 'D';
313     }
314     else if (in_wall && maze[i][j] == '.')
315     {
316     in_wall = 0;
317     maze[i - 1][j] = 'D';
318     }
319     else if (maze[i][j] != 'D' && maze[i][j] != '.')
320     maze[i][j] = 0;
321     }
322     j = MIN (y1, y2);
323     if (maze[i][j] == '.')
324     in_wall = 0;
325     if (maze[i][j] == 0 || maze[i][j] == '#')
326     in_wall = 1;
327     for ( /* j set already */ ; j < MAX (y1, y2); j++)
328     {
329     if (in_wall == 0 && maze[i][j] == '#')
330     {
331     in_wall = 1;
332     maze[i][j] = 'D';
333     }
334     else if (in_wall && maze[i][j] == '.')
335     {
336     in_wall = 0;
337     maze[i][j - 1] = 'D';
338     }
339     else if (maze[i][j] != 'D' && maze[i][j] != '.')
340     maze[i][j] = 0;
341     }
342 elmex 1.1
343     }
344 root 1.3 else
345     { /* connect in y direction first */
346     in_wall = 0;
347     /* swap if necessary */
348     if (y2 < y1)
349     {
350     int tx = x2, ty = y2;
351    
352     x2 = x1;
353     y2 = y1;
354     x1 = tx;
355     y1 = ty;
356     }
357     i = x1;
358     /* vertical connect */
359     for (j = y1; j < y2; j++)
360     {
361     if (in_wall == 0 && maze[i][j] == '#')
362     {
363     in_wall = 1;
364     maze[i][j] = 'D';
365     }
366     else if (in_wall && maze[i][j] == '.')
367     {
368     in_wall = 0;
369     maze[i][j - 1] = 'D';
370     }
371     else if (maze[i][j] != 'D' && maze[i][j] != '.')
372     maze[i][j] = 0;
373     }
374    
375     i = MIN (x1, x2);
376     if (maze[i][j] == '.')
377     in_wall = 0;
378     if (maze[i][j] == 0 || maze[i][j] == '#')
379     in_wall = 1;
380     for ( /* i set already */ ; i < MAX (x1, x2); i++)
381     {
382     if (in_wall == 0 && maze[i][j] == '#')
383     {
384     in_wall = 1;
385     maze[i][j] = 'D';
386     }
387     else if (in_wall && maze[i][j] == '.')
388     {
389     in_wall = 0;
390     maze[i - 1][j] = 'D';
391     }
392     else if (maze[i][j] != 'D' && maze[i][j] != '.')
393     maze[i][j] = 0;
394    
395     }
396 elmex 1.1
397     }
398    
399     }
400     }