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

# Content
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 Layout::Layout (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 Layout::~Layout ()
40 {
41 int size = (sizeof (char *) + sizeof (char) * h) * w;
42
43 sfree ((char *)col, size);
44 }
45
46 void
47 Layout::fill (char fill)
48 {
49 memset (col [0], fill, w * h);
50 }
51
52 void
53 Layout::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 Layout::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 void
66 Layout::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 /////////////////////////////////////////////////////////////////////////////
76
77 // erode by cellular automata
78 void
79 Layout::erode_1_2 (int c1, int c2, int repeat)
80 {
81 Layout neu (w, h);
82
83 while (repeat--)
84 {
85 for (int x = 0; x < w; ++x)
86 {
87 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 }, { -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 };
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 Layout::print ()
126 {
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
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 push_flood_fill (Layout &dist, pointlist &seeds, int x, int y)
156 {
157 if (dist [x][y])
158 return;
159
160 while (y > 0 && !dist [x][y - 1])
161 --y;
162
163 int y0 = y;
164
165 while (y < dist.h && !dist [x][y])
166 {
167 seeds.push (point (x, y));
168
169 dist [x][y] = 1;
170 ++y;
171 }
172
173 while (--y >= y0)
174 {
175 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 }
178 }
179
180 static inline void
181 make_tunnel (Layout &dist, pointlist &seeds, int x, int y, U8 d)
182 {
183 for (;;)
184 {
185 if (x > 1 && U8 (dist [x - 1][y]) < d && dist [x - 1][y] > 1)
186 --x;
187 else if (x < dist.w - 2 && U8 (dist [x + 1][y]) < d && dist [x + 1][y] > 1)
188 ++x;
189 else if (y > 1 && U8 (dist [x][y - 1]) < d && dist [x][y - 1] > 1)
190 --y;
191 else if (y < dist.h - 2 && U8 (dist [x][y + 1]) < d && dist [x][y + 1] > 1)
192 ++y;
193 else
194 break;
195
196 d = dist [x][y];
197 dist [x][y] = 1;
198 seeds.push (point (x, y));
199 }
200 }
201
202 static void inline
203 maybe_push (Layout &dist, pointlist &seeds, int x, int y, U8 d)
204 {
205 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
212 seeds.push (point (x, y));
213 }
214
215 static void
216 isolation_remover (Layout &maze, bool dirty)
217 {
218 Layout dist (maze.w, maze.h);
219
220 // dist contains
221 // 0 == invisited rooms
222 // 1 == visited rooms
223 // 2+ shortest distance to random near room
224
225 // phase 1, initialise dist array, find seed
226 int cnt = 0;
227 int x, y;
228
229 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
242 if (!cnt)
243 return;
244
245 fixed_stack<point> seeds (maze.w * maze.h * 5);
246
247 // 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
252 // phase 2, while we have seeds, if
253 // seed is empty, floodfill, else grow
254
255 while (seeds.size)
256 {
257 coroapi::cede_to_tick ();
258
259 point p = seeds.remove (rmg_rndm (seeds.size));
260
261 x = p.x;
262 y = p.y;
263
264 if (!dist [x][y])
265 {
266 // found new isolated area, make tunnel
267 if (!dirty)
268 push_flood_fill (dist, seeds, x, y);
269
270 make_tunnel (dist, seeds, x, y, 255);
271
272 if (dirty)
273 push_flood_fill (dist, seeds, x, y);
274 }
275 else
276 {
277 // nothing here, continue to expand
278 U8 d = U8 (dist [x][y]) + 1;
279
280 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
287 // now copy the tunnels over
288 for (int x = 0; x < maze.w; ++x)
289 for (int y = 0; y < maze.h; ++y)
290 if (maze [x][y] == '#' && dist [x][y] == 1)
291 maze [x][y] = 0;
292 }
293
294 void
295 Layout::isolation_remover (bool dirty)
296 {
297 ::isolation_remover (*this, dirty);
298 }
299
300 /////////////////////////////////////////////////////////////////////////////
301
302 // inspired mostly by http://www.jimrandomh.org/misc/caves.txt
303 void
304 Layout::gen_cave (int subtype)
305 {
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 }
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 maze.fill_rand (97);
351 maze.border ();
352 maze.isolation_remover ();
353 maze.print ();
354 }
355
356 exit (1);
357 }
358 } demo;
359 #endif