ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/random_maps/layout.C
Revision: 1.3
Committed: Thu Jul 1 01:22:44 2010 UTC (13 years, 10 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.2: +94 -88 lines
Log Message:
got rid of Layout, better memory management etc.

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     { -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 root 1.3 Layout::print ()
146 root 1.2 {
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.3 push_flood_fill (Layout &dist, pointlist &seeds, int x, int y)
176 root 1.1 {
177 root 1.3 if (dist [x][y])
178 root 1.1 return;
179    
180 root 1.3 while (y > 0 && !dist [x][y - 1])
181 root 1.1 --y;
182    
183     int y0 = y;
184    
185 root 1.3 while (y < dist.h && !dist [x][y])
186 root 1.1 {
187     seeds.push (point (x, y));
188    
189     dist [x][y] = 1;
190     ++y;
191     }
192    
193     while (--y >= y0)
194     {
195 root 1.3 if (x > 0) push_flood_fill (dist, seeds, x - 1, y);
196     if (x < dist.w - 1) push_flood_fill (dist, seeds, x + 1, y);
197 root 1.1 }
198     }
199    
200     static inline void
201 root 1.3 make_tunnel (Layout &dist, pointlist &seeds, int x, int y, U8 d)
202 root 1.1 {
203     for (;;)
204     {
205 root 1.2 if (x > 1 && U8 (dist [x - 1][y]) < d && dist [x - 1][y] > 1)
206 root 1.1 --x;
207 root 1.3 else if (x < dist.w - 2 && U8 (dist [x + 1][y]) < d && dist [x + 1][y] > 1)
208 root 1.1 ++x;
209 root 1.2 else if (y > 1 && U8 (dist [x][y - 1]) < d && dist [x][y - 1] > 1)
210 root 1.1 --y;
211 root 1.3 else if (y < dist.h - 2 && U8 (dist [x][y + 1]) < d && dist [x][y + 1] > 1)
212 root 1.1 ++y;
213     else
214     break;
215    
216     d = dist [x][y];
217 root 1.3 dist [x][y] = 1;
218     seeds.push (point (x, y));
219 root 1.1 }
220     }
221    
222 root 1.3 static void inline
223     maybe_push (Layout &dist, pointlist &seeds, int x, int y, U8 d)
224 root 1.1 {
225 root 1.3 char &D = dist [x][y];
226    
227     if (U8 (D) > d) // if wall and higher distance, lower distance
228     D = d;
229     else if (D) // otherwise, if it's no room, this space is uninteresting
230     return;
231 root 1.1
232 root 1.3 seeds.push (point (x, y));
233     }
234 root 1.1
235 root 1.3 static void
236     isolation_remover (Layout &maze, bool dirty)
237     {
238     Layout dist (maze.w, maze.h);
239 root 1.1
240 root 1.3 // dist contains
241     // 0 == invisited rooms
242     // 1 == visited rooms
243     // 2+ shortest distance to random near room
244 root 1.1
245 root 1.3 // phase 1, initialise dist array, find seed
246     int cnt = 0;
247     int x, y;
248 root 1.1
249 root 1.3 for (int i = 0; i < maze.w; ++i)
250     for (int j = 0; j < maze.h; ++j)
251     {
252     if (maze [i][j] == '#')
253     dist [i][j] = U8 (255);
254     else
255     {
256     dist [i][j] = 0;
257     if (!rmg_rndm (++cnt))
258     x = i, y = j;
259     }
260     }
261 root 1.2
262 root 1.3 if (!cnt)
263     return;
264 root 1.1
265 root 1.3 fixed_stack<point> seeds (maze.w * maze.h * 5);
266 root 1.1
267 root 1.3 // found first free space - picking the first one gives
268     // us a slight bias for tunnels, but usually you won't
269     // notice that in-game
270     seeds.push (point (x, y));
271 root 1.1
272 root 1.3 // phase 2, while we have seeds, if
273     // seed is empty, floodfill, else grow
274 root 1.1
275 root 1.3 while (seeds.size)
276     {
277     coroapi::cede_to_tick ();
278 root 1.1
279 root 1.3 point p = seeds.remove (rmg_rndm (seeds.size));
280 root 1.1
281 root 1.3 x = p.x;
282     y = p.y;
283 root 1.1
284 root 1.3 if (!dist [x][y])
285     {
286     // found new isolated area, make tunnel
287     if (!dirty)
288     push_flood_fill (dist, seeds, x, y);
289 root 1.1
290 root 1.3 make_tunnel (dist, seeds, x, y, 255);
291 root 1.1
292 root 1.3 if (dirty)
293     push_flood_fill (dist, seeds, x, y);
294 root 1.1 }
295 root 1.3 else
296     {
297     // nothing here, continue to expand
298     U8 d = U8 (dist [x][y]) + 1;
299 root 1.1
300 root 1.3 if (x < dist.w - 1) maybe_push (dist, seeds, x + 1, y, d);
301     if (x > 0) maybe_push (dist, seeds, x - 1, y, d);
302     if (y < dist.h - 1) maybe_push (dist, seeds, x, y + 1, d);
303     if (y > 0) maybe_push (dist, seeds, x, y - 1, d);
304     }
305     }
306 root 1.1
307 root 1.3 // now copy the tunnels over
308 root 1.2 for (int x = 0; x < maze.w; ++x)
309     for (int y = 0; y < maze.h; ++y)
310 root 1.3 if (maze [x][y] == '#' && dist [x][y] == 1)
311 root 1.1 maze [x][y] = 0;
312     }
313    
314     void
315 root 1.3 Layout::isolation_remover (bool dirty)
316 root 1.1 {
317 root 1.2 ::isolation_remover (*this, dirty);
318     }
319    
320     /////////////////////////////////////////////////////////////////////////////
321    
322     // inspired mostly by http://www.jimrandomh.org/misc/caves.txt
323     void
324 root 1.3 Layout::gen_cave (int subtype)
325 root 1.2 {
326     switch (subtype)
327     {
328     // a rough cave
329     case 0:
330     fill_rand (rmg_rndm (80, 95));
331     break;
332    
333     // corridors
334     case 1:
335     fill_rand (rmg_rndm (5, 40));
336     erode_1_2 (5, 2, 10);
337     erode_1_2 (5, -1, 10);
338     erode_1_2 (5, 2, 1);
339     break;
340    
341     // somewhat open, roundish
342     case 2:
343     fill_rand (45);
344     erode_1_2 (5, 0, 5);
345     erode_1_2 (5, 1, 1);
346     break;
347    
348     // wide open, some room-like structures
349     case 3:
350     fill_rand (45);
351     erode_1_2 (5, 2, 4);
352     erode_1_2 (5, -1, 3);
353     break;
354     }
355    
356     border ();
357     isolation_remover ();
358 root 1.1 }
359    
360     #if 0
361     static struct demo
362     {
363     demo ()
364     {
365     Layout maze (40, 25);
366     rmg_rndm.seed (time (0));
367    
368     for(int i=1;i<10;i)
369     {
370 root 1.3 maze.fill_rand (97);
371     maze.border ();
372     maze.isolation_remover ();
373     maze.print ();
374 root 1.1 }
375 root 1.2
376 root 1.1 exit (1);
377     }
378     } demo;
379     #endif