ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/random_maps/rogue_layout.C
Revision: 1.24
Committed: Sat Nov 17 23:40:02 2018 UTC (5 years, 7 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.23: +1 -0 lines
Log Message:
copyright update 2018

File Contents

# User Rev Content
1 root 1.12 /*
2     * This file is part of Deliantra, the Roguelike Realtime MMORPG.
3 root 1.21 *
4 root 1.24 * Copyright (©) 2017,2018 Marc Alexander Lehmann / the Deliantra team
5 root 1.22 * Copyright (©) 2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
6 root 1.19 * Copyright (©) 1994-2004 Crossfire Development Team (restored, original file without copyright notice)
7 root 1.21 *
8 root 1.12 * Deliantra is free software: you can redistribute it and/or modify it under
9     * the terms of the Affero GNU General Public License as published by the
10     * Free Software Foundation, either version 3 of the License, or (at your
11     * option) any later version.
12 root 1.21 *
13 root 1.12 * This program is distributed in the hope that it will be useful,
14     * but WITHOUT ANY WARRANTY; without even the implied warranty of
15     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16     * GNU General Public License for more details.
17 root 1.21 *
18 root 1.12 * You should have received a copy of the Affero GNU General Public License
19     * and the GNU General Public License along with this program. If not, see
20     * <http://www.gnu.org/licenses/>.
21 root 1.21 *
22 root 1.12 * The authors can be reached via e-mail to <support@deliantra.net>
23     */
24    
25 root 1.15 /* generate a rogue/nethack-like maze */
26 elmex 1.1 #include <global.h>
27 root 1.18 #include <rmg.h>
28 root 1.11 #include <rproto.h>
29 elmex 1.1
30 root 1.3 typedef struct
31     {
32 elmex 1.1 int x;
33 root 1.3 int y; /* coordinates of room centers */
34 elmex 1.1
35     int sx;
36 root 1.3 int sy; /* sizes */
37     int ax, ay, zx, zy; /* coordinates of extrema of the rectangle */
38 elmex 1.1
39 root 1.3 int rtype; /* circle or rectangular */
40 elmex 1.1 } Room;
41    
42 root 1.7 static int roguelike_place_room (Room *rooms, int xsize, int ysize, int nrooms);
43     static void roguelike_make_rooms (Room *rooms, char **maze, int options);
44 root 1.16 static void roguelike_link_rooms (Room *rooms, layout &maze);
45 elmex 1.1
46 root 1.3 int
47 root 1.16 surround_check (layout &maze, int i, int j)
48 root 1.3 {
49 elmex 1.1 /* 1 = wall to left,
50 root 1.3 2 = wall to right,
51     4 = wall above
52     8 = wall below */
53 elmex 1.1 int surround_index = 0;
54 root 1.3
55 root 1.16 if ((i > 0) && (maze[i - 1][j] != 0 && maze[i - 1][j] != '.')) surround_index |= 1;
56     if ((i < maze.w - 1) && (maze[i + 1][j] != 0 && maze[i + 1][j] != '.')) surround_index |= 2;
57     if ((j > 0) && (maze[i][j - 1] != 0 && maze[i][j - 1] != '.')) surround_index |= 4;
58     if ((j < maze.h - 1) && (maze[i][j + 1] != 0 && maze[i][j + 1] != '.')) surround_index |= 8;
59 root 1.9
60 elmex 1.1 return surround_index;
61     }
62    
63 root 1.15 /* actually make the maze: we work by a reduction process:
64 root 1.23 * first we make everything a wall, then we remove areas to make rooms
65 elmex 1.1 */
66 root 1.8 void
67 root 1.15 roguelike_layout_gen (layout &maze, int options)
68 root 1.3 {
69     int i, j;
70 elmex 1.1 Room *walk;
71 root 1.3 int nrooms = 0;
72     int tries = 0;
73 elmex 1.1
74 root 1.14 int xsize = maze.w;
75     int ysize = maze.h;
76 elmex 1.1
77     /* minimum room size is basically 5x5: if xsize/ysize is
78     less than 3x that then hollow things out, stick in
79     a stairsup and stairs down, and exit */
80 root 1.3 if (xsize < 11 || ysize < 11)
81     {
82 root 1.14 maze.clear ();
83     maze.border ();
84 root 1.7
85     maze[(xsize - 1) / 2][(ysize - 1) / 2 ] = '>';
86 root 1.3 maze[(xsize - 1) / 2][(ysize - 1) / 2 + 1] = '<';
87 root 1.7
88 root 1.8 return;
89 root 1.3 }
90 elmex 1.1
91 root 1.14 maze.fill ('#');
92 root 1.8
93 elmex 1.1 /* decide on the number of rooms */
94 root 1.10 nrooms = rmg_rndm (10) + 6;
95 root 1.7 Room *rooms = salloc0<Room> (nrooms + 1);
96 elmex 1.1
97     /* actually place the rooms */
98 root 1.3 i = 0;
99     while (tries < 450 && i < nrooms)
100     {
101     /* try to place the room */
102 root 1.7 if (!roguelike_place_room (rooms, xsize, ysize, nrooms))
103 root 1.3 tries++;
104     else
105     i++;
106     }
107    
108 root 1.9 if (i == 0) /* no can do! */
109     {
110 root 1.14 maze.clear ();
111     maze.border ();
112 root 1.7
113     maze [(xsize - 1) / 2][(ysize - 1) / 2 ] = '>';
114     maze [(xsize - 1) / 2][(ysize - 1) / 2 + 1] = '<';
115    
116     sfree (rooms, nrooms + 1);
117 root 1.8 return;
118 root 1.3 }
119    
120 elmex 1.1 /* erase the areas occupied by the rooms */
121 root 1.7 roguelike_make_rooms (rooms, maze, options);
122 elmex 1.1
123 root 1.16 roguelike_link_rooms (rooms, maze);
124 elmex 1.1
125     /* put in the stairs */
126 root 1.3
127 root 1.7 maze[rooms->x][rooms->y] = '<';
128    
129 elmex 1.1 /* get the last one */
130 root 1.7 for (walk = rooms; walk->x != 0; walk++)
131     ;
132    
133 elmex 1.1 /* back up one */
134     walk--;
135 root 1.7
136     if (walk == rooms)
137 root 1.3 {
138     /* In this case, there is only a single room. We don't want to
139     * clobber are up exit (above) with a down exit, so put the
140     * other exit one space up/down, depending which is a space
141     * and not a wall.
142     */
143     if (maze[walk->x][walk->y + 1] == '.')
144     maze[walk->x][walk->y + 1] = '>';
145     else
146     maze[walk->x][walk->y - 1] = '>';
147     }
148 elmex 1.1 else
149     maze[walk->x][walk->y] = '>';
150    
151     /* convert all the '.' to 0, we're through with the '.' */
152 root 1.3 for (i = 0; i < xsize; i++)
153     for (j = 0; j < ysize; j++)
154     {
155     if (maze[i][j] == '.')
156     maze[i][j] = 0;
157 root 1.7
158 root 1.3 if (maze[i][j] == 'D')
159     { /* remove bad door. */
160 root 1.16 int si = surround_check (maze, i, j);
161 root 1.3
162     if (si != 3 && si != 12)
163     {
164     maze[i][j] = 0;
165     /* back up and recheck any nearby doors */
166     i = 0;
167     j = 0;
168     }
169     }
170 elmex 1.1 }
171 root 1.3
172 root 1.7 sfree (rooms, nrooms + 1);
173 elmex 1.1 }
174    
175 root 1.3 static int
176 root 1.7 roguelike_place_room (Room *rooms, int xsize, int ysize, int nrooms)
177 root 1.3 {
178     int tx, ty; /* trial center locations */
179     int sx, sy; /* trial sizes */
180     int ax, ay; /* min coords of rect */
181     int zx, zy; /* max coords of rect */
182 elmex 1.1 int x_basesize;
183     int y_basesize;
184     Room *walk;
185    
186     /* decide on the base x and y sizes */
187 root 1.3 x_basesize = xsize / isqrt (nrooms);
188     y_basesize = ysize / isqrt (nrooms);
189 elmex 1.1
190 root 1.10 tx = rmg_rndm (xsize);
191     ty = rmg_rndm (ysize);
192 root 1.3
193 elmex 1.1 /* generate a distribution of sizes centered about basesize */
194 root 1.10 sx = rmg_rndm (x_basesize) + rmg_rndm (x_basesize) + rmg_rndm (x_basesize);
195     sy = rmg_rndm (y_basesize) + rmg_rndm (y_basesize) + rmg_rndm (y_basesize);
196 root 1.17 sy >>= 1; /* renormalize */
197 elmex 1.1
198     /* find the corners */
199     ax = tx - sx / 2;
200 root 1.3 zx = tx + sx / 2 + sx % 2;
201 elmex 1.1
202     ay = ty - sy / 2;
203 root 1.3 zy = ty + sy / 2 + sy % 2;
204 elmex 1.1
205     /* check to see if it's in the map */
206 root 1.7 if (zx > xsize - 1 || ax < 1) return 0;
207     if (zy > ysize - 1 || ay < 1) return 0;
208 root 1.3
209 elmex 1.1 /* no small fish */
210 root 1.3 if (sx < 3 || sy < 3)
211     return 0;
212 elmex 1.1
213     /* check overlap with existing rooms */
214 root 1.7 for (walk = rooms; walk->x != 0; walk++)
215 root 1.3 {
216     int dx = abs (tx - walk->x);
217     int dy = abs (ty - walk->y);
218    
219     if ((dx < (walk->sx + sx) / 2 + 2) && (dy < (walk->sy + sy) / 2 + 2))
220     return 0;
221     }
222 elmex 1.1
223     /* if we've got here, presumably the room is OK. */
224    
225     /* get a pointer to the first free room */
226 root 1.17 for (walk = rooms; walk->x; walk++)
227 root 1.7 ;
228    
229 root 1.17 walk->x = tx;
230     walk->y = ty;
231 elmex 1.1 walk->sx = sx;
232     walk->sy = sy;
233     walk->ax = ax;
234     walk->ay = ay;
235     walk->zx = zx;
236     walk->zy = zy;
237 root 1.7
238 root 1.3 return 1; /* success */
239 elmex 1.1 }
240 root 1.3
241 elmex 1.1 /* write all the rooms into the maze */
242 root 1.3 static void
243 root 1.7 roguelike_make_rooms (Room *rooms, char **maze, int options)
244 root 1.3 {
245     int making_circle = 0;
246 elmex 1.1 int R;
247     Room *walk;
248    
249 root 1.17 for (walk = rooms; walk->x; walk++)
250 root 1.3 {
251     /* first decide what shape to make */
252     switch (options)
253     {
254 root 1.4 case 1:
255     making_circle = 0;
256     break;
257     case 2:
258     making_circle = 1;
259     break;
260     default:
261 root 1.10 making_circle = ((rmg_rndm (3) == 0) ? 1 : 0);
262 root 1.4 break;
263 root 1.3 }
264    
265     if (walk->sx < walk->sy)
266     R = walk->sx / 2;
267     else
268     R = walk->sy / 2;
269    
270     /* enscribe a rectangle */
271 root 1.17 for (int i = walk->ax; i < walk->zx; i++)
272     for (int j = walk->ay; j < walk->zy; j++)
273 root 1.7 if (!making_circle || ((int) (0.5 + hypot (walk->x - i, walk->y - j))) <= R)
274     maze[i][j] = '.';
275 elmex 1.1 }
276     }
277    
278 root 1.3 static void
279 root 1.16 roguelike_link_rooms (Room *rooms, layout &maze)
280 root 1.3 {
281 elmex 1.1 Room *walk;
282 root 1.3 int i, j;
283    
284 elmex 1.1 /* link each room to the previous room */
285 root 1.7 if (rooms[1].x == 0)
286 root 1.3 return; /* only 1 room */
287 elmex 1.1
288 root 1.7 for (walk = rooms + 1; walk->x != 0; walk++)
289 root 1.3 {
290     int x1 = walk->x;
291     int y1 = walk->y;
292     int x2 = (walk - 1)->x;
293     int y2 = (walk - 1)->y;
294     int in_wall = 0;
295    
296 root 1.10 if (rmg_rndm (2))
297 root 1.3 { /* connect in x direction first */
298     /* horizontal connect */
299     /* swap (x1,y1) (x2,y2) if necessary */
300    
301     if (x2 < x1)
302     {
303     int tx = x2, ty = y2;
304    
305     x2 = x1;
306     y2 = y1;
307     x1 = tx;
308     y1 = ty;
309     }
310    
311    
312     j = y1;
313     for (i = x1; i < x2; i++)
314     {
315     if (in_wall == 0 && maze[i][j] == '#')
316     {
317     in_wall = 1;
318     maze[i][j] = 'D';
319     }
320     else if (in_wall && maze[i][j] == '.')
321     {
322     in_wall = 0;
323     maze[i - 1][j] = 'D';
324     }
325     else if (maze[i][j] != 'D' && maze[i][j] != '.')
326     maze[i][j] = 0;
327     }
328 root 1.11
329     j = min (y1, y2);
330    
331 root 1.3 if (maze[i][j] == '.')
332     in_wall = 0;
333 root 1.11
334 root 1.3 if (maze[i][j] == 0 || maze[i][j] == '#')
335     in_wall = 1;
336 root 1.11
337     for ( /* j set already */ ; j < max (y1, y2); j++)
338 root 1.3 {
339     if (in_wall == 0 && maze[i][j] == '#')
340     {
341     in_wall = 1;
342     maze[i][j] = 'D';
343     }
344     else if (in_wall && maze[i][j] == '.')
345     {
346     in_wall = 0;
347     maze[i][j - 1] = 'D';
348     }
349     else if (maze[i][j] != 'D' && maze[i][j] != '.')
350     maze[i][j] = 0;
351     }
352 elmex 1.1
353     }
354 root 1.3 else
355     { /* connect in y direction first */
356     in_wall = 0;
357     /* swap if necessary */
358     if (y2 < y1)
359     {
360     int tx = x2, ty = y2;
361    
362     x2 = x1;
363     y2 = y1;
364     x1 = tx;
365     y1 = ty;
366     }
367     i = x1;
368     /* vertical connect */
369     for (j = y1; j < y2; j++)
370     {
371     if (in_wall == 0 && maze[i][j] == '#')
372     {
373     in_wall = 1;
374     maze[i][j] = 'D';
375     }
376     else if (in_wall && maze[i][j] == '.')
377     {
378     in_wall = 0;
379     maze[i][j - 1] = 'D';
380     }
381     else if (maze[i][j] != 'D' && maze[i][j] != '.')
382     maze[i][j] = 0;
383     }
384    
385 root 1.11 i = min (x1, x2);
386    
387 root 1.3 if (maze[i][j] == '.')
388     in_wall = 0;
389 root 1.11
390 root 1.3 if (maze[i][j] == 0 || maze[i][j] == '#')
391     in_wall = 1;
392 root 1.11
393     for ( /* i set already */ ; i < max (x1, x2); i++)
394 root 1.3 {
395     if (in_wall == 0 && maze[i][j] == '#')
396     {
397     in_wall = 1;
398     maze[i][j] = 'D';
399     }
400     else if (in_wall && maze[i][j] == '.')
401     {
402     in_wall = 0;
403     maze[i - 1][j] = 'D';
404     }
405     else if (maze[i][j] != 'D' && maze[i][j] != '.')
406     maze[i][j] = 0;
407    
408     }
409 elmex 1.1
410     }
411    
412     }
413     }
414 root 1.7