ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/random_maps/layout.C
Revision: 1.4
Committed: Thu Jul 1 01:27:14 2010 UTC (13 years, 10 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.3: +5 -25 lines
Log Message:
*** empty log message ***

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 root 1.3 Layout::Layout (int w, int h)
27 root 1.1 : 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 root 1.3 Layout::~Layout ()
40 root 1.1 {
41     int size = (sizeof (char *) + sizeof (char) * h) * w;
42    
43     sfree ((char *)col, size);
44     }
45    
46     void
47 root 1.3 Layout::fill (char fill)
48 root 1.1 {
49     memset (col [0], fill, w * h);
50     }
51    
52     void
53 root 1.3 Layout::rect (int x1, int y1, int x2, int y2, char fill)
54 root 1.1 {
55     for (; x1 < x2; ++x1)
56     memset (col [x1] + y1, fill, y2 - y1);
57     }
58    
59 root 1.3 void Layout::border (char fill)
60 root 1.1 {
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 root 1.3 Layout::fill_rand (int percent)
67 root 1.2 {
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.3 Layout::erode_1_2 (int c1, int c2, int repeat)
80 root 1.1 {
81 root 1.3 Layout neu (w, h);
82 root 1.2
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 root 1.4 { -2, -1, 0 }, { -2, 0, 0 }, { -2, 1, 0 },
96     { -1, -2, 0 }, { -1, -1, 1 }, { -1, 0, 1 }, { -1, 1, 1 }, { -1, 2, 0 },
97     { 0, -2, 0 }, { 0, -1, 1 }, { 0, 0, 1 }, { 0, 1, 1 }, { 0, 2, 0 },
98     { 1, -2, 0 }, { 1, -1, 1 }, { 1, 0, 1 }, { 1, 1, 1 }, { 1, 2, 0 },
99     { 2, -1, 0 }, { 2, 0, 0 }, { 2, 1, 0 },
100 root 1.2 };
101    
102     for (int i = array_length (dds); i--; )
103     {
104     int nx = x + dds [i][0];
105     int ny = y + dds [i][1];
106    
107     if (!IN_RANGE_EXC (nx, 0, w) || !IN_RANGE_EXC (ny, 0, h) || !col [nx][ny])
108     {
109     n1 += dds [i][2];
110     n2++;
111     }
112     }
113    
114     neu [x][y] = n1 >= c1 || n2 <= c2 ? '#' : 0;
115     }
116     }
117    
118     swap (neu);
119     }
120     }
121    
122     /////////////////////////////////////////////////////////////////////////////
123    
124     void
125 root 1.3 Layout::print ()
126 root 1.2 {
127     for (int y = 0; y < h; y++)
128     {
129     for (int x = 0; x < w; x++)
130     {
131     U8 c = (U8)col [x][y];
132 root 1.1
133     if (!c)
134     c = ' ';
135     else if (c < 10)
136     c += '0';
137     else if (c < 32)
138     c += 'a' - 10;
139    
140     putc ((char)c, stdout);
141     }
142    
143     putc ('\n', stdout);
144     }
145    
146     putc ('\n', stdout);
147     }
148    
149     /////////////////////////////////////////////////////////////////////////////
150     // isolation remover - ensures single connected area
151    
152     typedef fixed_stack<point> pointlist;
153    
154     static noinline void
155 root 1.3 push_flood_fill (Layout &dist, pointlist &seeds, int x, int y)
156 root 1.1 {
157 root 1.3 if (dist [x][y])
158 root 1.1 return;
159    
160 root 1.3 while (y > 0 && !dist [x][y - 1])
161 root 1.1 --y;
162    
163     int y0 = y;
164    
165 root 1.3 while (y < dist.h && !dist [x][y])
166 root 1.1 {
167     seeds.push (point (x, y));
168    
169     dist [x][y] = 1;
170     ++y;
171     }
172    
173     while (--y >= y0)
174     {
175 root 1.3 if (x > 0) push_flood_fill (dist, seeds, x - 1, y);
176     if (x < dist.w - 1) push_flood_fill (dist, seeds, x + 1, y);
177 root 1.1 }
178     }
179    
180     static inline void
181 root 1.3 make_tunnel (Layout &dist, pointlist &seeds, int x, int y, U8 d)
182 root 1.1 {
183     for (;;)
184     {
185 root 1.2 if (x > 1 && U8 (dist [x - 1][y]) < d && dist [x - 1][y] > 1)
186 root 1.1 --x;
187 root 1.3 else if (x < dist.w - 2 && U8 (dist [x + 1][y]) < d && dist [x + 1][y] > 1)
188 root 1.1 ++x;
189 root 1.2 else if (y > 1 && U8 (dist [x][y - 1]) < d && dist [x][y - 1] > 1)
190 root 1.1 --y;
191 root 1.3 else if (y < dist.h - 2 && U8 (dist [x][y + 1]) < d && dist [x][y + 1] > 1)
192 root 1.1 ++y;
193     else
194     break;
195    
196     d = dist [x][y];
197 root 1.3 dist [x][y] = 1;
198     seeds.push (point (x, y));
199 root 1.1 }
200     }
201    
202 root 1.3 static void inline
203     maybe_push (Layout &dist, pointlist &seeds, int x, int y, U8 d)
204 root 1.1 {
205 root 1.3 char &D = dist [x][y];
206    
207     if (U8 (D) > d) // if wall and higher distance, lower distance
208     D = d;
209     else if (D) // otherwise, if it's no room, this space is uninteresting
210     return;
211 root 1.1
212 root 1.3 seeds.push (point (x, y));
213     }
214 root 1.1
215 root 1.3 static void
216     isolation_remover (Layout &maze, bool dirty)
217     {
218     Layout dist (maze.w, maze.h);
219 root 1.1
220 root 1.3 // dist contains
221     // 0 == invisited rooms
222     // 1 == visited rooms
223     // 2+ shortest distance to random near room
224 root 1.1
225 root 1.3 // phase 1, initialise dist array, find seed
226     int cnt = 0;
227     int x, y;
228 root 1.1
229 root 1.3 for (int i = 0; i < maze.w; ++i)
230     for (int j = 0; j < maze.h; ++j)
231     {
232     if (maze [i][j] == '#')
233     dist [i][j] = U8 (255);
234     else
235     {
236     dist [i][j] = 0;
237     if (!rmg_rndm (++cnt))
238     x = i, y = j;
239     }
240     }
241 root 1.2
242 root 1.3 if (!cnt)
243     return;
244 root 1.1
245 root 1.3 fixed_stack<point> seeds (maze.w * maze.h * 5);
246 root 1.1
247 root 1.3 // found first free space - picking the first one gives
248     // us a slight bias for tunnels, but usually you won't
249     // notice that in-game
250     seeds.push (point (x, y));
251 root 1.1
252 root 1.3 // phase 2, while we have seeds, if
253     // seed is empty, floodfill, else grow
254 root 1.1
255 root 1.3 while (seeds.size)
256     {
257     coroapi::cede_to_tick ();
258 root 1.1
259 root 1.3 point p = seeds.remove (rmg_rndm (seeds.size));
260 root 1.1
261 root 1.3 x = p.x;
262     y = p.y;
263 root 1.1
264 root 1.3 if (!dist [x][y])
265     {
266     // found new isolated area, make tunnel
267     if (!dirty)
268     push_flood_fill (dist, seeds, x, y);
269 root 1.1
270 root 1.3 make_tunnel (dist, seeds, x, y, 255);
271 root 1.1
272 root 1.3 if (dirty)
273     push_flood_fill (dist, seeds, x, y);
274 root 1.1 }
275 root 1.3 else
276     {
277     // nothing here, continue to expand
278     U8 d = U8 (dist [x][y]) + 1;
279 root 1.1
280 root 1.3 if (x < dist.w - 1) maybe_push (dist, seeds, x + 1, y, d);
281     if (x > 0) maybe_push (dist, seeds, x - 1, y, d);
282     if (y < dist.h - 1) maybe_push (dist, seeds, x, y + 1, d);
283     if (y > 0) maybe_push (dist, seeds, x, y - 1, d);
284     }
285     }
286 root 1.1
287 root 1.3 // now copy the tunnels over
288 root 1.2 for (int x = 0; x < maze.w; ++x)
289     for (int y = 0; y < maze.h; ++y)
290 root 1.3 if (maze [x][y] == '#' && dist [x][y] == 1)
291 root 1.1 maze [x][y] = 0;
292     }
293    
294     void
295 root 1.3 Layout::isolation_remover (bool dirty)
296 root 1.1 {
297 root 1.2 ::isolation_remover (*this, dirty);
298     }
299    
300     /////////////////////////////////////////////////////////////////////////////
301    
302     // inspired mostly by http://www.jimrandomh.org/misc/caves.txt
303     void
304 root 1.3 Layout::gen_cave (int subtype)
305 root 1.2 {
306     switch (subtype)
307     {
308     // a rough cave
309     case 0:
310     fill_rand (rmg_rndm (80, 95));
311     break;
312    
313     // corridors
314     case 1:
315     fill_rand (rmg_rndm (5, 40));
316     erode_1_2 (5, 2, 10);
317     erode_1_2 (5, -1, 10);
318     erode_1_2 (5, 2, 1);
319     break;
320    
321     // somewhat open, roundish
322     case 2:
323     fill_rand (45);
324     erode_1_2 (5, 0, 5);
325     erode_1_2 (5, 1, 1);
326     break;
327    
328     // wide open, some room-like structures
329     case 3:
330     fill_rand (45);
331     erode_1_2 (5, 2, 4);
332     erode_1_2 (5, -1, 3);
333     break;
334     }
335    
336     border ();
337     isolation_remover ();
338 root 1.1 }
339    
340     #if 0
341     static struct demo
342     {
343     demo ()
344     {
345     Layout maze (40, 25);
346     rmg_rndm.seed (time (0));
347    
348     for(int i=1;i<10;i)
349     {
350 root 1.3 maze.fill_rand (97);
351     maze.border ();
352     maze.isolation_remover ();
353     maze.print ();
354 root 1.1 }
355 root 1.2
356 root 1.1 exit (1);
357     }
358     } demo;
359     #endif