ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/random_maps/layout.C
Revision: 1.2
Committed: Wed Jun 30 23:03:40 2010 UTC (13 years, 11 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.1: +151 -57 lines
Log Message:
add cave maze type

File Contents

# User Rev Content
1 root 1.1 /*
2     * This file is part of Deliantra, the Roguelike Realtime MMORPG.
3     *
4     * Copyright (©) 2010 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
5     *
6     * Deliantra is free software: you can redistribute it and/or modify it under
7     * the terms of the Affero GNU General Public License as published by the
8     * Free Software Foundation, either version 3 of the License, or (at your
9     * option) any later version.
10     *
11     * This program is distributed in the hope that it will be useful,
12     * but WITHOUT ANY WARRANTY; without even the implied warranty of
13     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14     * GNU General Public License for more details.
15     *
16     * You should have received a copy of the Affero GNU General Public License
17     * and the GNU General Public License along with this program. If not, see
18     * <http://www.gnu.org/licenses/>.
19     *
20     * The authors can be reached via e-mail to <support@deliantra.net>
21     */
22    
23     #include <global.h>
24     #include <random_map.h>
25    
26     LayoutData::LayoutData (int w, int h)
27     : w(w), h(h)
28     {
29     int size = (sizeof (char *) + sizeof (char) * h) * w;
30    
31     col = (char **)salloc<char> (size);
32    
33     char *data = (char *)(col + w);
34    
35     for (int x = w; x--; )
36     col [x] = data + x * h;
37     }
38    
39     LayoutData::~LayoutData ()
40     {
41     int size = (sizeof (char *) + sizeof (char) * h) * w;
42    
43     sfree ((char *)col, size);
44     }
45    
46     void
47     LayoutData::fill (char fill)
48     {
49     memset (col [0], fill, w * h);
50     }
51    
52     void
53     LayoutData::rect (int x1, int y1, int x2, int y2, char fill)
54     {
55     for (; x1 < x2; ++x1)
56     memset (col [x1] + y1, fill, y2 - y1);
57     }
58    
59     void LayoutData::border (char fill)
60     {
61     for (int i = 0; i < w; i++) col [i][0] = col [i][h - 1] = fill;
62     for (int j = 0; j < h; j++) col [0][j] = col [w - 1][j] = fill;
63     }
64    
65 root 1.2 void
66     LayoutData::fill_rand (int percent)
67     {
68     percent = lerp (percent, 0, 100, 0, 256);
69    
70     for (int x = w - 1; --x > 0; )
71     for (int y = h - 1; --y > 0; )
72     col [x][y] = rmg_rndm (256) > percent ? 0 : '#';
73     }
74    
75 root 1.1 /////////////////////////////////////////////////////////////////////////////
76    
77 root 1.2 // erode by cellular automata
78 root 1.1 void
79 root 1.2 LayoutData::erode_1_2 (int c1, int c2, int repeat)
80 root 1.1 {
81 root 1.2 LayoutData neu (w, h);
82    
83     while (repeat--)
84 root 1.1 {
85 root 1.2 for (int x = 0; x < w; ++x)
86 root 1.1 {
87 root 1.2 coroapi::cede_to_tick ();
88    
89     for (int y = 0; y < h; ++y)
90     {
91     int n1 = 0, n2 = 0;
92    
93     // a 5x5 area, dx, dy, distance (1 == <= 1, 0 <= 2)
94     static I8 dds[][3] = {
95     { -2, -1, 0 },
96     { -2, 0, 0 },
97     { -2, 1, 0 },
98    
99     { -1, -2, 0 },
100     { -1, -1, 1 },
101     { -1, 0, 1 },
102     { -1, 1, 1 },
103     { -1, 2, 0 },
104    
105     { 0, -2, 0 },
106     { 0, -1, 1 },
107     { 0, 0, 1 },
108     { 0, 1, 1 },
109     { 0, 2, 0 },
110    
111     { 1, -2, 0 },
112     { 1, -1, 1 },
113     { 1, 0, 1 },
114     { 1, 1, 1 },
115     { 1, 2, 0 },
116    
117     { 2, -1, 0 },
118     { 2, 0, 0 },
119     { 2, 1, 0 },
120     };
121    
122     for (int i = array_length (dds); i--; )
123     {
124     int nx = x + dds [i][0];
125     int ny = y + dds [i][1];
126    
127     if (!IN_RANGE_EXC (nx, 0, w) || !IN_RANGE_EXC (ny, 0, h) || !col [nx][ny])
128     {
129     n1 += dds [i][2];
130     n2++;
131     }
132     }
133    
134     neu [x][y] = n1 >= c1 || n2 <= c2 ? '#' : 0;
135     }
136     }
137    
138     swap (neu);
139     }
140     }
141    
142     /////////////////////////////////////////////////////////////////////////////
143    
144     void
145     LayoutData::print ()
146     {
147     for (int y = 0; y < h; y++)
148     {
149     for (int x = 0; x < w; x++)
150     {
151     U8 c = (U8)col [x][y];
152 root 1.1
153     if (!c)
154     c = ' ';
155     else if (c < 10)
156     c += '0';
157     else if (c < 32)
158     c += 'a' - 10;
159    
160     putc ((char)c, stdout);
161     }
162    
163     putc ('\n', stdout);
164     }
165    
166     putc ('\n', stdout);
167     }
168    
169     /////////////////////////////////////////////////////////////////////////////
170     // isolation remover - ensures single connected area
171    
172     typedef fixed_stack<point> pointlist;
173    
174     static noinline void
175 root 1.2 push_flood_fill (LayoutData &maze, LayoutData &dist, pointlist &seeds, int x, int y)
176 root 1.1 {
177     if (maze [x][y])
178     return;
179    
180     while (y > 0 && !maze [x][y - 1])
181     --y;
182    
183     int y0 = y;
184    
185 root 1.2 while (y < maze.h && !maze [x][y])
186 root 1.1 {
187     seeds.push (point (x, y));
188    
189     maze [x][y] = 1;
190     dist [x][y] = 1;
191     ++y;
192     }
193    
194     while (--y >= y0)
195     {
196 root 1.2 if (x > 0) push_flood_fill (maze, dist, seeds, x - 1, y);
197     if (x < maze.w - 1) push_flood_fill (maze, dist, seeds, x + 1, y);
198 root 1.1 }
199     }
200    
201     static inline void
202 root 1.2 make_tunnel (LayoutData &maze, LayoutData &dist, pointlist &seeds, int x, int y, U8 d)
203 root 1.1 {
204     for (;;)
205     {
206     seeds.push (point (x, y));
207    
208     maze [x][y] = 1;
209     dist [x][y] = 1;
210    
211 root 1.2 if (x > 1 && U8 (dist [x - 1][y]) < d && dist [x - 1][y] > 1)
212 root 1.1 --x;
213 root 1.2 else if (x < maze.w - 2 && U8 (dist [x + 1][y]) < d && dist [x + 1][y] > 1)
214 root 1.1 ++x;
215 root 1.2 else if (y > 1 && U8 (dist [x][y - 1]) < d && dist [x][y - 1] > 1)
216 root 1.1 --y;
217 root 1.2 else if (y < maze.h - 2 && U8 (dist [x][y + 1]) < d && dist [x][y + 1] > 1)
218 root 1.1 ++y;
219     else
220     break;
221    
222     d = dist [x][y];
223     }
224     }
225    
226     static void
227 root 1.2 isolation_remover (LayoutData &maze, bool dirty)
228 root 1.1 {
229 root 1.2 LayoutData dist (maze.w, maze.h);
230 root 1.1
231 root 1.2 dist.fill (255);
232 root 1.1
233 root 1.2 fixed_stack<point> seeds (maze.w * maze.h * 4);
234 root 1.1
235     // phase 1, find seed
236 root 1.2 for (int x = 1; x < maze.w; ++x)
237     for (int y = 1; y < maze.h; ++y)
238 root 1.1 if (!maze [x][y])
239     {
240 root 1.2 // found first free space - picking the first one gives
241     // us a slight bias for tunnels, but usually you won't
242     // notice that in-game
243 root 1.1 seeds.push (point (x, y));
244    
245     // phase 2, while we have seeds, if
246     // seed is empty, floodfill, else grow
247    
248     while (seeds.size)
249     {
250 root 1.2 coroapi::cede_to_tick ();
251    
252 root 1.1 point p = seeds.remove (rmg_rndm (seeds.size));
253    
254     x = p.x;
255     y = p.y;
256    
257     if (!maze [x][y])
258     {
259     // found new isolated area, make tunnel?
260     if (!dirty)
261     push_flood_fill (maze, dist, seeds, x, y);
262    
263     make_tunnel (maze, dist, seeds, x, y, 255);
264    
265     if (dirty)
266     push_flood_fill (maze, dist, seeds, x, y);
267     }
268     else
269     {
270     U8 d = U8 (dist [x][y]) + 1;
271    
272 root 1.2 if (x < maze.w - 1 && U8 (dist [x + 1][y]) > d)
273 root 1.1 {
274     dist [x + 1][y] = d;
275     seeds.push (point (x + 1, y));
276     }
277    
278 root 1.2 if (x > 0 && U8 (dist [x - 1][y]) > d)
279 root 1.1 {
280     dist [x - 1][y] = d;
281     seeds.push (point (x - 1, y));
282     }
283    
284 root 1.2 if (y < maze.h - 1 && U8 (dist [x][y + 1]) > d)
285 root 1.1 {
286     dist [x][y + 1] = d;
287     seeds.push (point (x, y + 1));
288     }
289    
290 root 1.2 if (y > 0 && U8 (dist [x][y - 1]) > d)
291 root 1.1 {
292     dist [x][y - 1] = d;
293     seeds.push (point (x, y - 1));
294     }
295     }
296     }
297    
298     goto success;
299     }
300    
301     success:
302    
303     // we mark free but visited floors as 1, undo this here
304 root 1.2 for (int x = 0; x < maze.w; ++x)
305     for (int y = 0; y < maze.h; ++y)
306 root 1.1 if (maze [x][y] == 1)
307     maze [x][y] = 0;
308     }
309    
310     void
311     LayoutData::isolation_remover (bool dirty)
312     {
313 root 1.2 ::isolation_remover (*this, dirty);
314     }
315    
316     /////////////////////////////////////////////////////////////////////////////
317    
318     // inspired mostly by http://www.jimrandomh.org/misc/caves.txt
319     void
320     LayoutData::gen_cave (int subtype)
321     {
322     switch (subtype)
323     {
324     // a rough cave
325     case 0:
326     fill_rand (rmg_rndm (80, 95));
327     break;
328    
329     // corridors
330     case 1:
331     fill_rand (rmg_rndm (5, 40));
332     erode_1_2 (5, 2, 10);
333     erode_1_2 (5, -1, 10);
334     erode_1_2 (5, 2, 1);
335     break;
336    
337     // somewhat open, roundish
338     case 2:
339     fill_rand (45);
340     erode_1_2 (5, 0, 5);
341     erode_1_2 (5, 1, 1);
342     break;
343    
344     // wide open, some room-like structures
345     case 3:
346     fill_rand (45);
347     erode_1_2 (5, 2, 4);
348     erode_1_2 (5, -1, 3);
349     break;
350     }
351    
352     border ();
353     isolation_remover ();
354 root 1.1 }
355    
356     #if 0
357     static struct demo
358     {
359     demo ()
360     {
361     Layout maze (40, 25);
362     rmg_rndm.seed (time (0));
363    
364     for(int i=1;i<10;i)
365     {
366 root 1.2 maze->gen_cave (3);
367     maze->print ();
368 root 1.1 }
369 root 1.2
370 root 1.1 exit (1);
371     }
372     } demo;
373     #endif