]> git.lizzy.rs Git - dragonfireclient.git/blob - src/cavegen.cpp
Fractal mapgen: Add seabed and large pseudorandom caves
[dragonfireclient.git] / src / cavegen.cpp
1 /*
2 Minetest
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "util/numeric.h"
21 #include "map.h"
22 #include "mapgen.h"
23 #include "mapgen_v5.h"
24 #include "mapgen_v6.h"
25 #include "mapgen_v7.h"
26 #include "mapgen_fractal.h"
27 #include "cavegen.h"
28
29 NoiseParams nparams_caveliquids(0, 1, v3f(150.0, 150.0, 150.0), 776, 3, 0.6, 2.0);
30
31
32 ///////////////////////////////////////// Caves V5
33
34
35 CaveV5::CaveV5(MapgenV5 *mg, PseudoRandom *ps)
36 {
37         this->mg             = mg;
38         this->vm             = mg->vm;
39         this->ndef           = mg->ndef;
40         this->water_level    = mg->water_level;
41         this->ps             = ps;
42         this->c_water_source = mg->c_water_source;
43         this->c_lava_source  = mg->c_lava_source;
44         this->c_ice          = mg->c_ice;
45         this->np_caveliquids = &nparams_caveliquids;
46
47         dswitchint = ps->range(1, 14);
48         flooded    = ps->range(1, 2) == 2;
49
50         part_max_length_rs  = ps->range(2, 4);
51         tunnel_routepoints  = ps->range(5, ps->range(15, 30));
52         min_tunnel_diameter = 5;
53         max_tunnel_diameter = ps->range(7, ps->range(8, 24));
54
55         large_cave_is_flat = (ps->range(0, 1) == 0);
56 }
57
58
59 void CaveV5::makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height)
60 {
61         node_min = nmin;
62         node_max = nmax;
63         main_direction = v3f(0, 0, 0);
64
65         // Allowed route area size in nodes
66         ar = node_max - node_min + v3s16(1, 1, 1);
67         // Area starting point in nodes
68         of = node_min;
69
70         // Allow a bit more
71         //(this should be more than the maximum radius of the tunnel)
72         s16 insure = 10;
73         s16 more = MYMAX(MAP_BLOCKSIZE - max_tunnel_diameter / 2 - insure, 1);
74         ar += v3s16(1,0,1) * more * 2;
75         of -= v3s16(1,0,1) * more;
76
77         route_y_min = 0;
78         // Allow half a diameter + 7 over stone surface
79         route_y_max = -of.Y + max_stone_y + max_tunnel_diameter / 2 + 7;
80
81         // Limit maximum to area
82         route_y_max = rangelim(route_y_max, 0, ar.Y - 1);
83
84         s16 min = 0;
85                 if (node_min.Y < water_level && node_max.Y > water_level) {
86                         min = water_level - max_tunnel_diameter/3 - of.Y;
87                         route_y_max = water_level + max_tunnel_diameter/3 - of.Y;
88                 }
89         route_y_min = ps->range(min, min + max_tunnel_diameter);
90         route_y_min = rangelim(route_y_min, 0, route_y_max);
91
92         s16 route_start_y_min = route_y_min;
93         s16 route_start_y_max = route_y_max;
94
95         route_start_y_min = rangelim(route_start_y_min, 0, ar.Y - 1);
96         route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y - 1);
97
98         // Randomize starting position
99         orp = v3f(
100                 (float)(ps->next() % ar.X) + 0.5,
101                 (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5,
102                 (float)(ps->next() % ar.Z) + 0.5
103         );
104
105         // Add generation notify begin event
106         v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
107         GenNotifyType notifytype = GENNOTIFY_LARGECAVE_BEGIN;
108         mg->gennotify.addEvent(notifytype, abs_pos);
109
110         // Generate some tunnel starting from orp
111         for (u16 j = 0; j < tunnel_routepoints; j++)
112                 makeTunnel(j % dswitchint == 0);
113
114         // Add generation notify end event
115         abs_pos = v3s16(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
116         notifytype = GENNOTIFY_LARGECAVE_END;
117         mg->gennotify.addEvent(notifytype, abs_pos);
118 }
119
120
121 void CaveV5::makeTunnel(bool dirswitch)
122 {
123         // Randomize size
124         s16 min_d = min_tunnel_diameter;
125         s16 max_d = max_tunnel_diameter;
126         rs = ps->range(min_d, max_d);
127         s16 rs_part_max_length_rs = rs * part_max_length_rs;
128
129         v3s16 maxlen;
130         maxlen = v3s16(
131                 rs_part_max_length_rs,
132                 rs_part_max_length_rs / 2,
133                 rs_part_max_length_rs
134         );
135
136         v3f vec;
137         // Jump downward sometimes
138         vec = v3f(
139                 (float)(ps->next() % maxlen.X) - (float)maxlen.X / 2,
140                 (float)(ps->next() % maxlen.Y) - (float)maxlen.Y / 2,
141                 (float)(ps->next() % maxlen.Z) - (float)maxlen.Z / 2
142         );
143
144         // Do not make caves that are above ground.
145         // It is only necessary to check the startpoint and endpoint.
146         v3s16 orpi(orp.X, orp.Y, orp.Z);
147         v3s16 veci(vec.X, vec.Y, vec.Z);
148         v3s16 p;
149
150         p = orpi + veci + of + rs / 2;
151         if (p.Z >= node_min.Z && p.Z <= node_max.Z &&
152                         p.X >= node_min.X && p.X <= node_max.X) {
153                 u32 index = (p.Z - node_min.Z) * mg->ystride + (p.X - node_min.X);
154                 s16 h = mg->heightmap[index];
155                 if (h < p.Y)
156                         return;
157         } else if (p.Y > water_level) {
158                 return; // If it's not in our heightmap, use a simple heuristic
159         }
160
161         p = orpi + of + rs / 2;
162         if (p.Z >= node_min.Z && p.Z <= node_max.Z &&
163                         p.X >= node_min.X && p.X <= node_max.X) {
164                 u32 index = (p.Z - node_min.Z) * mg->ystride + (p.X - node_min.X);
165                 s16 h = mg->heightmap[index];
166                 if (h < p.Y)
167                         return;
168         } else if (p.Y > water_level) {
169                 return;
170         }
171
172         vec += main_direction;
173
174         v3f rp = orp + vec;
175         if (rp.X < 0)
176                 rp.X = 0;
177         else if (rp.X >= ar.X)
178                 rp.X = ar.X - 1;
179
180         if (rp.Y < route_y_min)
181                 rp.Y = route_y_min;
182         else if (rp.Y >= route_y_max)
183                 rp.Y = route_y_max - 1;
184
185         if (rp.Z < 0)
186                 rp.Z = 0;
187         else if (rp.Z >= ar.Z)
188                 rp.Z = ar.Z - 1;
189
190         vec = rp - orp;
191
192         float veclen = vec.getLength();
193         if (veclen < 0.05)
194                 veclen = 1.0;
195
196         // Every second section is rough
197         bool randomize_xz = (ps->range(1, 2) == 1);
198
199         // Carve routes
200         for (float f = 0; f < 1.0; f += 1.0 / veclen)
201                 carveRoute(vec, f, randomize_xz);
202
203         orp = rp;
204 }
205
206
207 void CaveV5::carveRoute(v3f vec, float f, bool randomize_xz) 
208 {
209         MapNode airnode(CONTENT_AIR);
210         MapNode waternode(c_water_source);
211         MapNode lavanode(c_lava_source);
212
213         v3s16 startp(orp.X, orp.Y, orp.Z);
214         startp += of;
215
216         float nval = NoisePerlin3D(np_caveliquids, startp.X,
217                 startp.Y, startp.Z, mg->seed);
218         MapNode liquidnode = nval < 0.40 ? lavanode : waternode;
219
220         v3f fp = orp + vec * f;
221         fp.X += 0.1 * ps->range(-10, 10);
222         fp.Z += 0.1 * ps->range(-10, 10);
223         v3s16 cp(fp.X, fp.Y, fp.Z);
224
225         s16 d0 = -rs/2;
226         s16 d1 = d0 + rs;
227         if (randomize_xz) {
228                 d0 += ps->range(-1, 1);
229                 d1 += ps->range(-1, 1);
230         }
231
232         for (s16 z0 = d0; z0 <= d1; z0++) {
233                 s16 si = rs / 2 - MYMAX(0, abs(z0) - rs / 7 - 1);
234                 for (s16 x0 = -si - ps->range(0,1); x0 <= si - 1 + ps->range(0,1); x0++) {
235                         s16 maxabsxz = MYMAX(abs(x0), abs(z0));
236
237                         s16 si2 = rs / 2 - MYMAX(0, maxabsxz - rs / 7 - 1);
238
239                         for (s16 y0 = -si2; y0 <= si2; y0++) {
240                                 if (large_cave_is_flat) {
241                                         // Make large caves not so tall
242                                         if (rs > 7 && abs(y0) >= rs / 3)
243                                                 continue;
244                                 }
245
246                                 v3s16 p(cp.X + x0, cp.Y + y0, cp.Z + z0);
247                                 p += of;
248
249                                 if (vm->m_area.contains(p) == false)
250                                         continue;
251
252                                 u32 i = vm->m_area.index(p);
253                                 content_t c = vm->m_data[i].getContent();
254                                 if (!ndef->get(c).is_ground_content)
255                                         continue;
256
257                                 int full_ymin = node_min.Y - MAP_BLOCKSIZE;
258                                 int full_ymax = node_max.Y + MAP_BLOCKSIZE;
259
260                                 if (flooded && full_ymin < water_level &&
261                                                 full_ymax > water_level)
262                                         vm->m_data[i] = (p.Y <= water_level) ?
263                                                 waternode : airnode;
264                                 else if (flooded && full_ymax < water_level)
265                                         vm->m_data[i] = (p.Y < startp.Y - 4) ?
266                                                 liquidnode : airnode;
267                                 else
268                                         vm->m_data[i] = airnode;
269                         }
270                 }
271         }
272 }
273
274
275 ///////////////////////////////////////// Caves V6
276
277
278 CaveV6::CaveV6(MapgenV6 *mg, PseudoRandom *ps, PseudoRandom *ps2, bool is_large_cave)
279 {
280         this->mg             = mg;
281         this->vm             = mg->vm;
282         this->ndef           = mg->ndef;
283         this->water_level    = mg->water_level;
284         this->large_cave     = is_large_cave;
285         this->ps             = ps;
286         this->ps2            = ps2;
287         this->c_water_source = mg->c_water_source;
288         this->c_lava_source  = mg->c_lava_source;
289
290         min_tunnel_diameter = 2;
291         max_tunnel_diameter = ps->range(2, 6);
292         dswitchint          = ps->range(1, 14);
293         flooded             = true;
294
295         if (large_cave) {
296                 part_max_length_rs  = ps->range(2,4);
297                 tunnel_routepoints  = ps->range(5, ps->range(15,30));
298                 min_tunnel_diameter = 5;
299                 max_tunnel_diameter = ps->range(7, ps->range(8,24));
300         } else {
301                 part_max_length_rs = ps->range(2,9);
302                 tunnel_routepoints = ps->range(10, ps->range(15,30));
303         }
304
305         large_cave_is_flat = (ps->range(0,1) == 0);
306 }
307
308
309 void CaveV6::makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height)
310 {
311         node_min = nmin;
312         node_max = nmax;
313         max_stone_y = max_stone_height;
314         main_direction = v3f(0, 0, 0);
315
316         // Allowed route area size in nodes
317         ar = node_max - node_min + v3s16(1, 1, 1);
318         // Area starting point in nodes
319         of = node_min;
320
321         // Allow a bit more
322         //(this should be more than the maximum radius of the tunnel)
323         const s16 max_spread_amount = MAP_BLOCKSIZE;
324         s16 insure = 10;
325         s16 more = MYMAX(max_spread_amount - max_tunnel_diameter / 2 - insure, 1);
326         ar += v3s16(1,0,1) * more * 2;
327         of -= v3s16(1,0,1) * more;
328
329         route_y_min = 0;
330         // Allow half a diameter + 7 over stone surface
331         route_y_max = -of.Y + max_stone_y + max_tunnel_diameter / 2 + 7;
332
333         // Limit maximum to area
334         route_y_max = rangelim(route_y_max, 0, ar.Y - 1);
335
336         if (large_cave) {
337                 s16 min = 0;
338                 if (node_min.Y < water_level && node_max.Y > water_level) {
339                         min = water_level - max_tunnel_diameter/3 - of.Y;
340                         route_y_max = water_level + max_tunnel_diameter/3 - of.Y;
341                 }
342                 route_y_min = ps->range(min, min + max_tunnel_diameter);
343                 route_y_min = rangelim(route_y_min, 0, route_y_max);
344         }
345
346         s16 route_start_y_min = route_y_min;
347         s16 route_start_y_max = route_y_max;
348
349         route_start_y_min = rangelim(route_start_y_min, 0, ar.Y-1);
350         route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y-1);
351
352         // Randomize starting position
353         orp = v3f(
354                 (float)(ps->next() % ar.X) + 0.5,
355                 (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5,
356                 (float)(ps->next() % ar.Z) + 0.5
357         );
358
359         // Add generation notify begin event
360         v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
361         GenNotifyType notifytype = large_cave ?
362                 GENNOTIFY_LARGECAVE_BEGIN : GENNOTIFY_CAVE_BEGIN;
363         mg->gennotify.addEvent(notifytype, abs_pos);
364
365         // Generate some tunnel starting from orp
366         for (u16 j = 0; j < tunnel_routepoints; j++)
367                 makeTunnel(j % dswitchint == 0);
368
369         // Add generation notify end event
370         abs_pos = v3s16(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
371         notifytype = large_cave ?
372                 GENNOTIFY_LARGECAVE_END : GENNOTIFY_CAVE_END;
373         mg->gennotify.addEvent(notifytype, abs_pos);
374 }
375
376
377 void CaveV6::makeTunnel(bool dirswitch)
378 {
379         if (dirswitch && !large_cave) {
380                 main_direction = v3f(
381                         ((float)(ps->next() % 20) - (float)10) / 10,
382                         ((float)(ps->next() % 20) - (float)10) / 30,
383                         ((float)(ps->next() % 20) - (float)10) / 10
384                 );
385                 main_direction *= (float)ps->range(0, 10) / 10;
386         }
387
388         // Randomize size
389         s16 min_d = min_tunnel_diameter;
390         s16 max_d = max_tunnel_diameter;
391         rs = ps->range(min_d, max_d);
392         s16 rs_part_max_length_rs = rs * part_max_length_rs;
393
394         v3s16 maxlen;
395         if (large_cave) {
396                 maxlen = v3s16(
397                         rs_part_max_length_rs,
398                         rs_part_max_length_rs / 2,
399                         rs_part_max_length_rs
400                 );
401         } else {
402                 maxlen = v3s16(
403                         rs_part_max_length_rs,
404                         ps->range(1, rs_part_max_length_rs),
405                         rs_part_max_length_rs
406                 );
407         }
408
409         v3f vec(
410                 (float)(ps->next() % maxlen.X) - (float)maxlen.X / 2,
411                 (float)(ps->next() % maxlen.Y) - (float)maxlen.Y / 2,
412                 (float)(ps->next() % maxlen.Z) - (float)maxlen.Z / 2
413         );
414
415         // Jump downward sometimes
416         if (!large_cave && ps->range(0, 12) == 0) {
417                 vec = v3f(
418                         (float)(ps->next() % maxlen.X) - (float)maxlen.X / 2,
419                         (float)(ps->next() % (maxlen.Y * 2)) - (float)maxlen.Y,
420                         (float)(ps->next() % maxlen.Z) - (float)maxlen.Z / 2
421                 );
422         }
423
424         // Do not make caves that are entirely above ground, to fix
425         // shadow bugs caused by overgenerated large caves.
426         // It is only necessary to check the startpoint and endpoint.
427         v3s16 orpi(orp.X, orp.Y, orp.Z);
428         v3s16 veci(vec.X, vec.Y, vec.Z);
429         s16 h1;
430         s16 h2;
431
432         v3s16 p1 = orpi + veci + of + rs / 2;
433         if (p1.Z >= node_min.Z && p1.Z <= node_max.Z &&
434                         p1.X >= node_min.X && p1.X <= node_max.X) {
435                 u32 index1 = (p1.Z - node_min.Z) * mg->ystride +
436                         (p1.X - node_min.X);
437                 h1 = mg->heightmap[index1];
438         } else {
439                 h1 = water_level; // If not in heightmap
440         }
441
442         v3s16 p2 = orpi + of + rs / 2;
443         if (p2.Z >= node_min.Z && p2.Z <= node_max.Z &&
444                         p2.X >= node_min.X && p2.X <= node_max.X) {
445                 u32 index2 = (p2.Z - node_min.Z) * mg->ystride +
446                         (p2.X - node_min.X);
447                 h2 = mg->heightmap[index2];
448         } else {
449                 h2 = water_level;
450         }
451
452         // If startpoint and endpoint are above ground,
453         // disable placing of nodes in carveRoute while
454         // still running all pseudorandom calls to ensure
455         // caves consistent with existing worlds.
456         bool tunnel_above_ground = p1.Y > h1 && p2.Y > h2;
457
458         vec += main_direction;
459
460         v3f rp = orp + vec;
461         if (rp.X < 0)
462                 rp.X = 0;
463         else if (rp.X >= ar.X)
464                 rp.X = ar.X - 1;
465
466         if (rp.Y < route_y_min)
467                 rp.Y = route_y_min;
468         else if (rp.Y >= route_y_max)
469                 rp.Y = route_y_max - 1;
470
471         if (rp.Z < 0)
472                 rp.Z = 0;
473         else if (rp.Z >= ar.Z)
474                 rp.Z = ar.Z - 1;
475
476         vec = rp - orp;
477
478         float veclen = vec.getLength();
479         // As odd as it sounds, veclen is *exactly* 0.0 sometimes, causing a FPE
480         if (veclen < 0.05)
481                 veclen = 1.0;
482
483         // Every second section is rough
484         bool randomize_xz = (ps2->range(1, 2) == 1);
485
486         // Carve routes
487         for (float f = 0; f < 1.0; f += 1.0 / veclen)
488                 carveRoute(vec, f, randomize_xz, tunnel_above_ground);
489
490         orp = rp;
491 }
492
493
494 void CaveV6::carveRoute(v3f vec, float f, bool randomize_xz, bool tunnel_above_ground)
495 {
496         MapNode airnode(CONTENT_AIR);
497         MapNode waternode(c_water_source);
498         MapNode lavanode(c_lava_source);
499
500         v3s16 startp(orp.X, orp.Y, orp.Z);
501         startp += of;
502
503         v3f fp = orp + vec * f;
504         fp.X += 0.1 * ps->range(-10, 10);
505         fp.Z += 0.1 * ps->range(-10, 10);
506         v3s16 cp(fp.X, fp.Y, fp.Z);
507
508         s16 d0 = -rs/2;
509         s16 d1 = d0 + rs;
510         if (randomize_xz) {
511                 d0 += ps->range(-1, 1);
512                 d1 += ps->range(-1, 1);
513         }
514
515         for (s16 z0 = d0; z0 <= d1; z0++) {
516                 s16 si = rs / 2 - MYMAX(0, abs(z0) - rs / 7 - 1);
517                 for (s16 x0 = -si - ps->range(0,1); x0 <= si - 1 + ps->range(0,1); x0++) {
518                         if (tunnel_above_ground)
519                                 continue;
520
521                         s16 maxabsxz = MYMAX(abs(x0), abs(z0));
522                         s16 si2 = rs / 2 - MYMAX(0, maxabsxz - rs / 7 - 1);
523                         for (s16 y0 = -si2; y0 <= si2; y0++) {
524                                 if (large_cave_is_flat) {
525                                         // Make large caves not so tall
526                                         if (rs > 7 && abs(y0) >= rs / 3)
527                                                 continue;
528                                 }
529
530                                 v3s16 p(cp.X + x0, cp.Y + y0, cp.Z + z0);
531                                 p += of;
532
533                                 if (vm->m_area.contains(p) == false)
534                                         continue;
535
536                                 u32 i = vm->m_area.index(p);
537                                 content_t c = vm->m_data[i].getContent();
538                                 if (!ndef->get(c).is_ground_content)
539                                         continue;
540
541                                 if (large_cave) {
542                                         int full_ymin = node_min.Y - MAP_BLOCKSIZE;
543                                         int full_ymax = node_max.Y + MAP_BLOCKSIZE;
544
545                                         if (flooded && full_ymin < water_level &&
546                                                         full_ymax > water_level) {
547                                                 vm->m_data[i] = (p.Y <= water_level) ?
548                                                         waternode : airnode;
549                                         } else if (flooded && full_ymax < water_level) {
550                                                 vm->m_data[i] = (p.Y < startp.Y - 2) ?
551                                                         lavanode : airnode;
552                                         } else {
553                                                 vm->m_data[i] = airnode;
554                                         }
555                                 } else {
556                                         if (c == CONTENT_IGNORE || c == CONTENT_AIR)
557                                                 continue;
558
559                                         vm->m_data[i] = airnode;
560                                         vm->m_flags[i] |= VMANIP_FLAG_CAVE;
561                                 }
562                         }
563                 }
564         }
565 }
566
567
568 ///////////////////////////////////////// Caves V7
569
570
571 CaveV7::CaveV7(MapgenV7 *mg, PseudoRandom *ps)
572 {
573         this->mg             = mg;
574         this->vm             = mg->vm;
575         this->ndef           = mg->ndef;
576         this->water_level    = mg->water_level;
577         this->ps             = ps;
578         this->c_water_source = mg->c_water_source;
579         this->c_lava_source  = mg->c_lava_source;
580         this->c_ice          = mg->c_ice;
581         this->np_caveliquids = &nparams_caveliquids;
582
583         dswitchint = ps->range(1, 14);
584         flooded    = ps->range(1, 2) == 2;
585
586         part_max_length_rs  = ps->range(2, 4);
587         tunnel_routepoints  = ps->range(5, ps->range(15, 30));
588         min_tunnel_diameter = 5;
589         max_tunnel_diameter = ps->range(7, ps->range(8, 24));
590
591         large_cave_is_flat = (ps->range(0, 1) == 0);
592 }
593
594
595 void CaveV7::makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height)
596 {
597         node_min = nmin;
598         node_max = nmax;
599         max_stone_y = max_stone_height;
600         main_direction = v3f(0, 0, 0);
601
602         // Allowed route area size in nodes
603         ar = node_max - node_min + v3s16(1, 1, 1);
604         // Area starting point in nodes
605         of = node_min;
606
607         // Allow a bit more
608         //(this should be more than the maximum radius of the tunnel)
609         s16 insure = 10;
610         s16 more = MYMAX(MAP_BLOCKSIZE - max_tunnel_diameter / 2 - insure, 1);
611         ar += v3s16(1,0,1) * more * 2;
612         of -= v3s16(1,0,1) * more;
613
614         route_y_min = 0;
615         // Allow half a diameter + 7 over stone surface
616         route_y_max = -of.Y + max_stone_y + max_tunnel_diameter / 2 + 7;
617
618         // Limit maximum to area
619         route_y_max = rangelim(route_y_max, 0, ar.Y - 1);
620
621         s16 min = 0;
622         if (node_min.Y < water_level && node_max.Y > water_level) {
623                 min = water_level - max_tunnel_diameter/3 - of.Y;
624                 route_y_max = water_level + max_tunnel_diameter/3 - of.Y;
625         }
626         route_y_min = ps->range(min, min + max_tunnel_diameter);
627         route_y_min = rangelim(route_y_min, 0, route_y_max);
628
629         s16 route_start_y_min = route_y_min;
630         s16 route_start_y_max = route_y_max;
631
632         route_start_y_min = rangelim(route_start_y_min, 0, ar.Y - 1);
633         route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y - 1);
634
635         // Randomize starting position
636         orp = v3f(
637                 (float)(ps->next() % ar.X) + 0.5,
638                 (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5,
639                 (float)(ps->next() % ar.Z) + 0.5
640         );
641
642         // Add generation notify begin event
643         v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
644         GenNotifyType notifytype = GENNOTIFY_LARGECAVE_BEGIN;
645         mg->gennotify.addEvent(notifytype, abs_pos);
646
647         // Generate some tunnel starting from orp
648         for (u16 j = 0; j < tunnel_routepoints; j++)
649                 makeTunnel(j % dswitchint == 0);
650
651         // Add generation notify end event
652         abs_pos = v3s16(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
653         notifytype = GENNOTIFY_LARGECAVE_END;
654         mg->gennotify.addEvent(notifytype, abs_pos);
655 }
656
657
658 void CaveV7::makeTunnel(bool dirswitch)
659 {
660         // Randomize size
661         s16 min_d = min_tunnel_diameter;
662         s16 max_d = max_tunnel_diameter;
663         rs = ps->range(min_d, max_d);
664         s16 rs_part_max_length_rs = rs * part_max_length_rs;
665
666         v3s16 maxlen;
667         maxlen = v3s16(
668                 rs_part_max_length_rs,
669                 rs_part_max_length_rs / 2,
670                 rs_part_max_length_rs
671         );
672
673         v3f vec;
674         // Jump downward sometimes
675         vec = v3f(
676                 (float)(ps->next() % maxlen.X) - (float)maxlen.X / 2,
677                 (float)(ps->next() % maxlen.Y) - (float)maxlen.Y / 2,
678                 (float)(ps->next() % maxlen.Z) - (float)maxlen.Z / 2
679         );
680
681         // Do not make caves that are above ground.
682         // It is only necessary to check the startpoint and endpoint.
683         v3s16 orpi(orp.X, orp.Y, orp.Z);
684         v3s16 veci(vec.X, vec.Y, vec.Z);
685         v3s16 p;
686
687         p = orpi + veci + of + rs / 2;
688         if (p.Z >= node_min.Z && p.Z <= node_max.Z &&
689                         p.X >= node_min.X && p.X <= node_max.X) {
690                 u32 index = (p.Z - node_min.Z) * mg->ystride + (p.X - node_min.X);
691                 s16 h = mg->ridge_heightmap[index];
692                 if (h < p.Y)
693                         return;
694         } else if (p.Y > water_level) {
695                 return; // If it's not in our heightmap, use a simple heuristic
696         }
697
698         p = orpi + of + rs / 2;
699         if (p.Z >= node_min.Z && p.Z <= node_max.Z &&
700                         p.X >= node_min.X && p.X <= node_max.X) {
701                 u32 index = (p.Z - node_min.Z) * mg->ystride + (p.X - node_min.X);
702                 s16 h = mg->ridge_heightmap[index];
703                 if (h < p.Y)
704                         return;
705         } else if (p.Y > water_level) {
706                 return;
707         }
708
709         vec += main_direction;
710
711         v3f rp = orp + vec;
712         if (rp.X < 0)
713                 rp.X = 0;
714         else if (rp.X >= ar.X)
715                 rp.X = ar.X - 1;
716
717         if (rp.Y < route_y_min)
718                 rp.Y = route_y_min;
719         else if (rp.Y >= route_y_max)
720                 rp.Y = route_y_max - 1;
721
722         if (rp.Z < 0)
723                 rp.Z = 0;
724         else if (rp.Z >= ar.Z)
725                 rp.Z = ar.Z - 1;
726
727         vec = rp - orp;
728
729         float veclen = vec.getLength();
730         if (veclen < 0.05)
731                 veclen = 1.0;
732
733         // Every second section is rough
734         bool randomize_xz = (ps->range(1, 2) == 1);
735
736         // Carve routes
737         for (float f = 0; f < 1.0; f += 1.0 / veclen)
738                 carveRoute(vec, f, randomize_xz);
739
740         orp = rp;
741 }
742
743
744 void CaveV7::carveRoute(v3f vec, float f, bool randomize_xz)
745 {
746         MapNode airnode(CONTENT_AIR);
747         MapNode waternode(c_water_source);
748         MapNode lavanode(c_lava_source);
749
750         v3s16 startp(orp.X, orp.Y, orp.Z);
751         startp += of;
752
753         float nval = NoisePerlin3D(np_caveliquids, startp.X,
754                 startp.Y, startp.Z, mg->seed);
755         MapNode liquidnode = (nval < 0.40 && node_max.Y < MGV7_LAVA_DEPTH) ?
756                 lavanode : waternode;
757
758         v3f fp = orp + vec * f;
759         fp.X += 0.1 * ps->range(-10, 10);
760         fp.Z += 0.1 * ps->range(-10, 10);
761         v3s16 cp(fp.X, fp.Y, fp.Z);
762
763         s16 d0 = -rs/2;
764         s16 d1 = d0 + rs;
765         if (randomize_xz) {
766                 d0 += ps->range(-1, 1);
767                 d1 += ps->range(-1, 1);
768         }
769
770         for (s16 z0 = d0; z0 <= d1; z0++) {
771                 s16 si = rs / 2 - MYMAX(0, abs(z0) - rs / 7 - 1);
772                 for (s16 x0 = -si - ps->range(0,1); x0 <= si - 1 + ps->range(0,1); x0++) {
773                         s16 maxabsxz = MYMAX(abs(x0), abs(z0));
774
775                         s16 si2 = rs / 2 - MYMAX(0, maxabsxz - rs / 7 - 1);
776
777                         for (s16 y0 = -si2; y0 <= si2; y0++) {
778                                 if (large_cave_is_flat) {
779                                         // Make large caves not so tall
780                                         if (rs > 7 && abs(y0) >= rs / 3)
781                                                 continue;
782                                 }
783
784                                 v3s16 p(cp.X + x0, cp.Y + y0, cp.Z + z0);
785                                 p += of;
786
787                                 if (vm->m_area.contains(p) == false)
788                                         continue;
789
790                                 u32 i = vm->m_area.index(p);
791                                 content_t c = vm->m_data[i].getContent();
792                                 if (!ndef->get(c).is_ground_content)
793                                         continue;
794
795                                 int full_ymin = node_min.Y - MAP_BLOCKSIZE;
796                                 int full_ymax = node_max.Y + MAP_BLOCKSIZE;
797
798                                 if (flooded && full_ymin < water_level &&
799                                                 full_ymax > water_level)
800                                         vm->m_data[i] = (p.Y <= water_level) ?
801                                                 waternode : airnode;
802                                 else if (flooded && full_ymax < water_level)
803                                         vm->m_data[i] = (p.Y < startp.Y - 4) ?
804                                                 liquidnode : airnode;
805                                 else
806                                         vm->m_data[i] = airnode;
807                         }
808                 }
809         }
810 }
811
812
813 ///////////////////////////////////////// Caves Fractal
814
815
816 CaveFractal::CaveFractal(MapgenFractal *mg, PseudoRandom *ps)
817 {
818         this->mg             = mg;
819         this->vm             = mg->vm;
820         this->ndef           = mg->ndef;
821         this->water_level    = mg->water_level;
822         this->ps             = ps;
823         this->c_water_source = mg->c_water_source;
824         this->c_lava_source  = mg->c_lava_source;
825         this->c_ice          = mg->c_ice;
826         this->np_caveliquids = &nparams_caveliquids;
827
828         dswitchint = ps->range(1, 14);
829         flooded    = ps->range(1, 2) == 2;
830
831         part_max_length_rs  = ps->range(2, 4);
832         tunnel_routepoints  = ps->range(5, ps->range(15, 30));
833         min_tunnel_diameter = 5;
834         max_tunnel_diameter = ps->range(7, ps->range(8, 24));
835
836         large_cave_is_flat = (ps->range(0, 1) == 0);
837 }
838
839
840 void CaveFractal::makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height)
841 {
842         node_min = nmin;
843         node_max = nmax;
844         main_direction = v3f(0, 0, 0);
845
846         // Allowed route area size in nodes
847         ar = node_max - node_min + v3s16(1, 1, 1);
848         // Area starting point in nodes
849         of = node_min;
850
851         // Allow a bit more
852         //(this should be more than the maximum radius of the tunnel)
853         s16 insure = 10;
854         s16 more = MYMAX(MAP_BLOCKSIZE - max_tunnel_diameter / 2 - insure, 1);
855         ar += v3s16(1,0,1) * more * 2;
856         of -= v3s16(1,0,1) * more;
857
858         route_y_min = 0;
859         // Allow half a diameter + 7 over stone surface
860         route_y_max = -of.Y + max_stone_y + max_tunnel_diameter / 2 + 7;
861
862         // Limit maximum to area
863         route_y_max = rangelim(route_y_max, 0, ar.Y - 1);
864
865         s16 min = 0;
866                 if (node_min.Y < water_level && node_max.Y > water_level) {
867                         min = water_level - max_tunnel_diameter/3 - of.Y;
868                         route_y_max = water_level + max_tunnel_diameter/3 - of.Y;
869                 }
870         route_y_min = ps->range(min, min + max_tunnel_diameter);
871         route_y_min = rangelim(route_y_min, 0, route_y_max);
872
873         s16 route_start_y_min = route_y_min;
874         s16 route_start_y_max = route_y_max;
875
876         route_start_y_min = rangelim(route_start_y_min, 0, ar.Y - 1);
877         route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y - 1);
878
879         // Randomize starting position
880         orp = v3f(
881                 (float)(ps->next() % ar.X) + 0.5,
882                 (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5,
883                 (float)(ps->next() % ar.Z) + 0.5
884         );
885
886         // Add generation notify begin event
887         v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
888         GenNotifyType notifytype = GENNOTIFY_LARGECAVE_BEGIN;
889         mg->gennotify.addEvent(notifytype, abs_pos);
890
891         // Generate some tunnel starting from orp
892         for (u16 j = 0; j < tunnel_routepoints; j++)
893                 makeTunnel(j % dswitchint == 0);
894
895         // Add generation notify end event
896         abs_pos = v3s16(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
897         notifytype = GENNOTIFY_LARGECAVE_END;
898         mg->gennotify.addEvent(notifytype, abs_pos);
899 }
900
901
902 void CaveFractal::makeTunnel(bool dirswitch)
903 {
904         // Randomize size
905         s16 min_d = min_tunnel_diameter;
906         s16 max_d = max_tunnel_diameter;
907         rs = ps->range(min_d, max_d);
908         s16 rs_part_max_length_rs = rs * part_max_length_rs;
909
910         v3s16 maxlen;
911         maxlen = v3s16(
912                 rs_part_max_length_rs,
913                 rs_part_max_length_rs / 2,
914                 rs_part_max_length_rs
915         );
916
917         v3f vec;
918         // Jump downward sometimes
919         vec = v3f(
920                 (float)(ps->next() % maxlen.X) - (float)maxlen.X / 2,
921                 (float)(ps->next() % maxlen.Y) - (float)maxlen.Y / 2,
922                 (float)(ps->next() % maxlen.Z) - (float)maxlen.Z / 2
923         );
924
925         // Do not make caves that are above ground.
926         // It is only necessary to check the startpoint and endpoint.
927         v3s16 orpi(orp.X, orp.Y, orp.Z);
928         v3s16 veci(vec.X, vec.Y, vec.Z);
929         v3s16 p;
930
931         p = orpi + veci + of + rs / 2;
932         if (p.Z >= node_min.Z && p.Z <= node_max.Z &&
933                         p.X >= node_min.X && p.X <= node_max.X) {
934                 u32 index = (p.Z - node_min.Z) * mg->ystride + (p.X - node_min.X);
935                 s16 h = mg->heightmap[index];
936                 if (h < p.Y)
937                         return;
938         } else if (p.Y > water_level) {
939                 return; // If it's not in our heightmap, use a simple heuristic
940         }
941
942         p = orpi + of + rs / 2;
943         if (p.Z >= node_min.Z && p.Z <= node_max.Z &&
944                         p.X >= node_min.X && p.X <= node_max.X) {
945                 u32 index = (p.Z - node_min.Z) * mg->ystride + (p.X - node_min.X);
946                 s16 h = mg->heightmap[index];
947                 if (h < p.Y)
948                         return;
949         } else if (p.Y > water_level) {
950                 return;
951         }
952
953         vec += main_direction;
954
955         v3f rp = orp + vec;
956         if (rp.X < 0)
957                 rp.X = 0;
958         else if (rp.X >= ar.X)
959                 rp.X = ar.X - 1;
960
961         if (rp.Y < route_y_min)
962                 rp.Y = route_y_min;
963         else if (rp.Y >= route_y_max)
964                 rp.Y = route_y_max - 1;
965
966         if (rp.Z < 0)
967                 rp.Z = 0;
968         else if (rp.Z >= ar.Z)
969                 rp.Z = ar.Z - 1;
970
971         vec = rp - orp;
972
973         float veclen = vec.getLength();
974         if (veclen < 0.05)
975                 veclen = 1.0;
976
977         // Every second section is rough
978         bool randomize_xz = (ps->range(1, 2) == 1);
979
980         // Carve routes
981         for (float f = 0; f < 1.0; f += 1.0 / veclen)
982                 carveRoute(vec, f, randomize_xz);
983
984         orp = rp;
985 }
986
987
988 void CaveFractal::carveRoute(v3f vec, float f, bool randomize_xz) 
989 {
990         MapNode airnode(CONTENT_AIR);
991         MapNode waternode(c_water_source);
992         MapNode lavanode(c_lava_source);
993
994         v3s16 startp(orp.X, orp.Y, orp.Z);
995         startp += of;
996
997         float nval = NoisePerlin3D(np_caveliquids, startp.X,
998                 startp.Y, startp.Z, mg->seed);
999         MapNode liquidnode = (nval < 0.40 && node_max.Y < MGFRACTAL_LAVA_DEPTH) ?
1000                 lavanode : waternode;
1001
1002         v3f fp = orp + vec * f;
1003         fp.X += 0.1 * ps->range(-10, 10);
1004         fp.Z += 0.1 * ps->range(-10, 10);
1005         v3s16 cp(fp.X, fp.Y, fp.Z);
1006
1007         s16 d0 = -rs/2;
1008         s16 d1 = d0 + rs;
1009         if (randomize_xz) {
1010                 d0 += ps->range(-1, 1);
1011                 d1 += ps->range(-1, 1);
1012         }
1013
1014         for (s16 z0 = d0; z0 <= d1; z0++) {
1015                 s16 si = rs / 2 - MYMAX(0, abs(z0) - rs / 7 - 1);
1016                 for (s16 x0 = -si - ps->range(0,1); x0 <= si - 1 + ps->range(0,1); x0++) {
1017                         s16 maxabsxz = MYMAX(abs(x0), abs(z0));
1018
1019                         s16 si2 = rs / 2 - MYMAX(0, maxabsxz - rs / 7 - 1);
1020
1021                         for (s16 y0 = -si2; y0 <= si2; y0++) {
1022                                 if (large_cave_is_flat) {
1023                                         // Make large caves not so tall
1024                                         if (rs > 7 && abs(y0) >= rs / 3)
1025                                                 continue;
1026                                 }
1027
1028                                 v3s16 p(cp.X + x0, cp.Y + y0, cp.Z + z0);
1029                                 p += of;
1030
1031                                 if (vm->m_area.contains(p) == false)
1032                                         continue;
1033
1034                                 u32 i = vm->m_area.index(p);
1035                                 content_t c = vm->m_data[i].getContent();
1036                                 if (!ndef->get(c).is_ground_content)
1037                                         continue;
1038
1039                                 int full_ymin = node_min.Y - MAP_BLOCKSIZE;
1040                                 int full_ymax = node_max.Y + MAP_BLOCKSIZE;
1041
1042                                 if (flooded && full_ymin < water_level &&
1043                                                 full_ymax > water_level)
1044                                         vm->m_data[i] = (p.Y <= water_level) ?
1045                                                 waternode : airnode;
1046                                 else if (flooded && full_ymax < water_level)
1047                                         vm->m_data[i] = (p.Y < startp.Y - 4) ?
1048                                                 liquidnode : airnode;
1049                                 else
1050                                         vm->m_data[i] = airnode;
1051                         }
1052                 }
1053         }
1054 }