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

# 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 },
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 Layout::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
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 push_flood_fill (Layout &dist, pointlist &seeds, int x, int y)
176 {
177 if (dist [x][y])
178 return;
179
180 while (y > 0 && !dist [x][y - 1])
181 --y;
182
183 int y0 = y;
184
185 while (y < dist.h && !dist [x][y])
186 {
187 seeds.push (point (x, y));
188
189 dist [x][y] = 1;
190 ++y;
191 }
192
193 while (--y >= y0)
194 {
195 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 }
198 }
199
200 static inline void
201 make_tunnel (Layout &dist, pointlist &seeds, int x, int y, U8 d)
202 {
203 for (;;)
204 {
205 if (x > 1 && U8 (dist [x - 1][y]) < d && dist [x - 1][y] > 1)
206 --x;
207 else if (x < dist.w - 2 && U8 (dist [x + 1][y]) < d && dist [x + 1][y] > 1)
208 ++x;
209 else if (y > 1 && U8 (dist [x][y - 1]) < d && dist [x][y - 1] > 1)
210 --y;
211 else if (y < dist.h - 2 && U8 (dist [x][y + 1]) < d && dist [x][y + 1] > 1)
212 ++y;
213 else
214 break;
215
216 d = dist [x][y];
217 dist [x][y] = 1;
218 seeds.push (point (x, y));
219 }
220 }
221
222 static void inline
223 maybe_push (Layout &dist, pointlist &seeds, int x, int y, U8 d)
224 {
225 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
232 seeds.push (point (x, y));
233 }
234
235 static void
236 isolation_remover (Layout &maze, bool dirty)
237 {
238 Layout dist (maze.w, maze.h);
239
240 // dist contains
241 // 0 == invisited rooms
242 // 1 == visited rooms
243 // 2+ shortest distance to random near room
244
245 // phase 1, initialise dist array, find seed
246 int cnt = 0;
247 int x, y;
248
249 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
262 if (!cnt)
263 return;
264
265 fixed_stack<point> seeds (maze.w * maze.h * 5);
266
267 // 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
272 // phase 2, while we have seeds, if
273 // seed is empty, floodfill, else grow
274
275 while (seeds.size)
276 {
277 coroapi::cede_to_tick ();
278
279 point p = seeds.remove (rmg_rndm (seeds.size));
280
281 x = p.x;
282 y = p.y;
283
284 if (!dist [x][y])
285 {
286 // found new isolated area, make tunnel
287 if (!dirty)
288 push_flood_fill (dist, seeds, x, y);
289
290 make_tunnel (dist, seeds, x, y, 255);
291
292 if (dirty)
293 push_flood_fill (dist, seeds, x, y);
294 }
295 else
296 {
297 // nothing here, continue to expand
298 U8 d = U8 (dist [x][y]) + 1;
299
300 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
307 // now copy the tunnels over
308 for (int x = 0; x < maze.w; ++x)
309 for (int y = 0; y < maze.h; ++y)
310 if (maze [x][y] == '#' && dist [x][y] == 1)
311 maze [x][y] = 0;
312 }
313
314 void
315 Layout::isolation_remover (bool dirty)
316 {
317 ::isolation_remover (*this, dirty);
318 }
319
320 /////////////////////////////////////////////////////////////////////////////
321
322 // inspired mostly by http://www.jimrandomh.org/misc/caves.txt
323 void
324 Layout::gen_cave (int subtype)
325 {
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 }
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 maze.fill_rand (97);
371 maze.border ();
372 maze.isolation_remover ();
373 maze.print ();
374 }
375
376 exit (1);
377 }
378 } demo;
379 #endif