]> git.lizzy.rs Git - dragonfireclient.git/blob - src/cavegen.cpp
Mgv5: Remove blobgen. Remove crumble and wetness noises
[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 "cavegen.h"
27
28 NoiseParams nparams_caveliquids(0, 1, v3f(150.0, 150.0, 150.0), 776, 3, 0.6, 2.0);
29
30
31 ///////////////////////////////////////////////////////////////////////////////
32
33
34 CaveV6::CaveV6(MapgenV6 *mg, PseudoRandom *ps, PseudoRandom *ps2, bool is_large_cave) {
35         this->mg   = mg;
36         this->vm   = mg->vm;
37         this->ndef = mg->ndef;
38         this->water_level = mg->water_level;
39         this->large_cave = is_large_cave;
40         this->ps  = ps;
41         this->ps2 = ps2;
42         this->c_water_source = mg->c_water_source;
43         this->c_lava_source  = mg->c_lava_source;
44
45         min_tunnel_diameter = 2;
46         max_tunnel_diameter = ps->range(2, 6);
47         dswitchint = ps->range(1, 14);
48         flooded = true;
49
50         if (large_cave) {
51                 part_max_length_rs = ps->range(2,4);
52                 tunnel_routepoints = ps->range(5, ps->range(15,30));
53                 min_tunnel_diameter = 5;
54                 max_tunnel_diameter = ps->range(7, ps->range(8,24));
55         } else {
56                 part_max_length_rs = ps->range(2,9);
57                 tunnel_routepoints = ps->range(10, ps->range(15,30));
58         }
59
60         large_cave_is_flat = (ps->range(0,1) == 0);
61 }
62
63
64 void CaveV6::makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height) {
65         node_min = nmin;
66         node_max = nmax;
67         max_stone_y = max_stone_height;
68         main_direction = v3f(0, 0, 0);
69
70         // Allowed route area size in nodes
71         ar = node_max - node_min + v3s16(1, 1, 1);
72         // Area starting point in nodes
73         of = node_min;
74
75         // Allow a bit more
76         //(this should be more than the maximum radius of the tunnel)
77         const s16 max_spread_amount = MAP_BLOCKSIZE;
78         s16 insure = 10;
79         s16 more = MYMAX(max_spread_amount - max_tunnel_diameter / 2 - insure, 1);
80         ar += v3s16(1,0,1) * more * 2;
81         of -= v3s16(1,0,1) * more;
82
83         route_y_min = 0;
84         // Allow half a diameter + 7 over stone surface
85         route_y_max = -of.Y + max_stone_y + max_tunnel_diameter / 2 + 7;
86
87         // Limit maximum to area
88         route_y_max = rangelim(route_y_max, 0, ar.Y - 1);
89
90         if (large_cave) {
91                 s16 min = 0;
92                 if (node_min.Y < water_level && node_max.Y > water_level) {
93                         min = water_level - max_tunnel_diameter/3 - of.Y;
94                         route_y_max = water_level + max_tunnel_diameter/3 - of.Y;
95                 }
96                 route_y_min = ps->range(min, min + max_tunnel_diameter);
97                 route_y_min = rangelim(route_y_min, 0, route_y_max);
98         }
99
100         s16 route_start_y_min = route_y_min;
101         s16 route_start_y_max = route_y_max;
102
103         route_start_y_min = rangelim(route_start_y_min, 0, ar.Y-1);
104         route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y-1);
105
106         // Randomize starting position
107         orp = v3f(
108                 (float)(ps->next() % ar.X) + 0.5,
109                 (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5,
110                 (float)(ps->next() % ar.Z) + 0.5
111         );
112
113         // Add generation notify begin event
114         v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
115         GenNotifyType notifytype = large_cave ?
116                 GENNOTIFY_LARGECAVE_BEGIN : GENNOTIFY_CAVE_BEGIN;
117         mg->gennotify.addEvent(notifytype, abs_pos);
118
119         // Generate some tunnel starting from orp
120         for (u16 j = 0; j < tunnel_routepoints; j++)
121                 makeTunnel(j % dswitchint == 0);
122
123         // Add generation notify end event
124         abs_pos = v3s16(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
125         notifytype = large_cave ?
126                 GENNOTIFY_LARGECAVE_END : GENNOTIFY_CAVE_END;
127         mg->gennotify.addEvent(notifytype, abs_pos);
128 }
129
130
131 void CaveV6::makeTunnel(bool dirswitch) {
132         if (dirswitch && !large_cave) {
133                 main_direction = v3f(
134                         ((float)(ps->next() % 20) - (float)10) / 10,
135                         ((float)(ps->next() % 20) - (float)10) / 30,
136                         ((float)(ps->next() % 20) - (float)10) / 10
137                 );
138                 main_direction *= (float)ps->range(0, 10) / 10;
139         }
140
141         // Randomize size
142         s16 min_d = min_tunnel_diameter;
143         s16 max_d = max_tunnel_diameter;
144         rs = ps->range(min_d, max_d);
145
146         v3s16 maxlen;
147         if (large_cave) {
148                 maxlen = v3s16(
149                         rs * part_max_length_rs,
150                         rs * part_max_length_rs / 2,
151                         rs * part_max_length_rs
152                 );
153         } else {
154                 maxlen = v3s16(
155                         rs * part_max_length_rs,
156                         ps->range(1, rs * part_max_length_rs),
157                         rs * part_max_length_rs
158                 );
159         }
160
161         v3f vec(
162                 (float)(ps->next() % (maxlen.X * 1)) - (float)maxlen.X / 2,
163                 (float)(ps->next() % (maxlen.Y * 1)) - (float)maxlen.Y / 2,
164                 (float)(ps->next() % (maxlen.Z * 1)) - (float)maxlen.Z / 2
165         );
166
167         // Jump downward sometimes
168         if (!large_cave && ps->range(0, 12) == 0) {
169                 vec = v3f(
170                         (float)(ps->next() % (maxlen.X * 1)) - (float)maxlen.X / 2,
171                         (float)(ps->next() % (maxlen.Y * 2)) - (float)maxlen.Y,
172                         (float)(ps->next() % (maxlen.Z * 1)) - (float)maxlen.Z / 2
173                 );
174         }
175
176         vec += main_direction;
177
178         v3f rp = orp + vec;
179         if (rp.X < 0)
180                 rp.X = 0;
181         else if (rp.X >= ar.X)
182                 rp.X = ar.X - 1;
183
184         if (rp.Y < route_y_min)
185                 rp.Y = route_y_min;
186         else if (rp.Y >= route_y_max)
187                 rp.Y = route_y_max - 1;
188
189         if (rp.Z < 0)
190                 rp.Z = 0;
191         else if (rp.Z >= ar.Z)
192                 rp.Z = ar.Z - 1;
193
194         vec = rp - orp;
195
196         float veclen = vec.getLength();
197         // As odd as it sounds, veclen is *exactly* 0.0 sometimes, causing a FPE
198         if (veclen < 0.05)
199                 veclen = 1.0;
200
201         // Every second section is rough
202         bool randomize_xz = (ps2->range(1, 2) == 1);
203
204         // Carve routes
205         for (float f = 0; f < 1.0; f += 1.0 / veclen)
206                 carveRoute(vec, f, randomize_xz);
207
208         orp = rp;
209 }
210
211
212 void CaveV6::carveRoute(v3f vec, float f, bool randomize_xz) {
213         MapNode airnode(CONTENT_AIR);
214         MapNode waternode(c_water_source);
215         MapNode lavanode(c_lava_source);
216
217         v3s16 startp(orp.X, orp.Y, orp.Z);
218         startp += of;
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                         s16 si2 = rs / 2 - MYMAX(0, maxabsxz - rs / 7 - 1);
237                         for (s16 y0 = -si2; y0 <= si2; y0++) {
238                                 if (large_cave_is_flat) {
239                                         // Make large caves not so tall
240                                         if (rs > 7 && abs(y0) >= rs / 3)
241                                                 continue;
242                                 }
243
244                                 v3s16 p(cp.X + x0, cp.Y + y0, cp.Z + z0);
245                                 p += of;
246
247                                 if (vm->m_area.contains(p) == false)
248                                         continue;
249
250                                 u32 i = vm->m_area.index(p);
251                                 content_t c = vm->m_data[i].getContent();
252                                 if (!ndef->get(c).is_ground_content)
253                                         continue;
254
255                                 if (large_cave) {
256                                         int full_ymin = node_min.Y - MAP_BLOCKSIZE;
257                                         int full_ymax = node_max.Y + MAP_BLOCKSIZE;
258
259                                         if (flooded && full_ymin < water_level && full_ymax > water_level) {
260                                                 vm->m_data[i] = (p.Y <= water_level) ? waternode : airnode;
261                                         } else if (flooded && full_ymax < water_level) {
262                                                 vm->m_data[i] = (p.Y < startp.Y - 2) ? lavanode : airnode;
263                                         } else {
264                                                 vm->m_data[i] = airnode;
265                                         }
266                                 } else {
267                                         // Don't replace air or water or lava or ignore
268                                         if (c == CONTENT_IGNORE || c == CONTENT_AIR ||
269                                                 c == c_water_source || c == c_lava_source)
270                                                 continue;
271
272                                         vm->m_data[i] = airnode;
273                                         vm->m_flags[i] |= VMANIP_FLAG_CAVE;
274                                 }
275                         }
276                 }
277         }
278 }
279
280
281 ///////////////////////////////////////// Caves V7
282
283
284 CaveV7::CaveV7(MapgenV7 *mg, PseudoRandom *ps, bool is_large_cave) {
285         this->mg   = mg;
286         this->vm   = mg->vm;
287         this->ndef = mg->ndef;
288         this->water_level = mg->water_level;
289         this->large_cave  = is_large_cave;
290         this->ps = ps;
291         this->c_water_source = mg->c_water_source;
292         this->c_lava_source  = mg->c_lava_source;
293         this->c_ice          = mg->c_ice;
294         this->np_caveliquids = &nparams_caveliquids;
295
296         dswitchint = ps->range(1, 14);
297         flooded    = ps->range(1, 2) == 2;
298
299         if (large_cave) {
300                 part_max_length_rs = ps->range(2, 4);
301                 tunnel_routepoints = ps->range(5, ps->range(15, 30));
302                 min_tunnel_diameter = 5;
303                 max_tunnel_diameter = ps->range(7, ps->range(8, 24));
304         } else {
305                 part_max_length_rs = ps->range(2, 9);
306                 tunnel_routepoints = ps->range(10, ps->range(15, 30));
307                 min_tunnel_diameter = 2;
308                 max_tunnel_diameter = ps->range(2, 6);
309         }
310
311         large_cave_is_flat = (ps->range(0, 1) == 0);
312 }
313
314
315 void CaveV7::makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height) {
316         node_min = nmin;
317         node_max = nmax;
318         max_stone_y = max_stone_height;
319         main_direction = v3f(0, 0, 0);
320
321         // Allowed route area size in nodes
322         ar = node_max - node_min + v3s16(1, 1, 1);
323         // Area starting point in nodes
324         of = node_min;
325
326         // Allow a bit more
327         //(this should be more than the maximum radius of the tunnel)
328         s16 insure = 10;
329         s16 more = MYMAX(MAP_BLOCKSIZE - max_tunnel_diameter / 2 - insure, 1);
330         ar += v3s16(1,0,1) * more * 2;
331         of -= v3s16(1,0,1) * more;
332
333         route_y_min = 0;
334         // Allow half a diameter + 7 over stone surface
335         route_y_max = -of.Y + max_stone_y + max_tunnel_diameter / 2 + 7;
336
337         // Limit maximum to area
338         route_y_max = rangelim(route_y_max, 0, ar.Y - 1);
339
340         if (large_cave) {
341                 s16 min = 0;
342                 if (node_min.Y < water_level && node_max.Y > water_level) {
343                         min = water_level - max_tunnel_diameter/3 - of.Y;
344                         route_y_max = water_level + max_tunnel_diameter/3 - of.Y;
345                 }
346                 route_y_min = ps->range(min, min + max_tunnel_diameter);
347                 route_y_min = rangelim(route_y_min, 0, route_y_max);
348         }
349
350         s16 route_start_y_min = route_y_min;
351         s16 route_start_y_max = route_y_max;
352
353         route_start_y_min = rangelim(route_start_y_min, 0, ar.Y - 1);
354         route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y - 1);
355
356         // Randomize starting position
357         orp = v3f(
358                 (float)(ps->next() % ar.X) + 0.5,
359                 (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5,
360                 (float)(ps->next() % ar.Z) + 0.5
361         );
362
363         // Add generation notify begin event
364         v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
365         GenNotifyType notifytype = large_cave ?
366                 GENNOTIFY_LARGECAVE_BEGIN : GENNOTIFY_CAVE_BEGIN;
367         mg->gennotify.addEvent(notifytype, abs_pos);
368
369         // Generate some tunnel starting from orp
370         for (u16 j = 0; j < tunnel_routepoints; j++)
371                 makeTunnel(j % dswitchint == 0);
372
373         // Add generation notify end event
374         abs_pos = v3s16(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
375         notifytype = large_cave ?
376                 GENNOTIFY_LARGECAVE_END : GENNOTIFY_CAVE_END;
377         mg->gennotify.addEvent(notifytype, abs_pos);
378 }
379
380
381 void CaveV7::makeTunnel(bool dirswitch) {
382         if (dirswitch && !large_cave) {
383                 main_direction = v3f(
384                         ((float)(ps->next() % 20) - (float)10) / 10,
385                         ((float)(ps->next() % 20) - (float)10) / 30,
386                         ((float)(ps->next() % 20) - (float)10) / 10
387                 );
388                 main_direction *= (float)ps->range(0, 10) / 10;
389         }
390
391         // Randomize size
392         s16 min_d = min_tunnel_diameter;
393         s16 max_d = max_tunnel_diameter;
394         rs = ps->range(min_d, max_d);
395
396         v3s16 maxlen;
397         if (large_cave) {
398                 maxlen = v3s16(
399                         rs * part_max_length_rs,
400                         rs * part_max_length_rs / 2,
401                         rs * part_max_length_rs
402                 );
403         } else {
404                 maxlen = v3s16(
405                         rs * part_max_length_rs,
406                         ps->range(1, rs * part_max_length_rs),
407                         rs * part_max_length_rs
408                 );
409         }
410
411         v3f vec;
412         // Jump downward sometimes
413         if (!large_cave && ps->range(0, 12) == 0) {
414                 vec = v3f(
415                         (float)(ps->next() % (maxlen.X * 1)) - (float)maxlen.X / 2,
416                         (float)(ps->next() % (maxlen.Y * 2)) - (float)maxlen.Y,
417                         (float)(ps->next() % (maxlen.Z * 1)) - (float)maxlen.Z / 2
418                 );
419         } else {
420                 vec = v3f(
421                         (float)(ps->next() % (maxlen.X * 1)) - (float)maxlen.X / 2,
422                         (float)(ps->next() % (maxlen.Y * 1)) - (float)maxlen.Y / 2,
423                         (float)(ps->next() % (maxlen.Z * 1)) - (float)maxlen.Z / 2
424                 );
425         }
426
427         // Do not make large caves that are above ground.
428         // It is only necessary to check the startpoint and endpoint.
429         if (large_cave) {
430                 v3s16 orpi(orp.X, orp.Y, orp.Z);
431                 v3s16 veci(vec.X, vec.Y, vec.Z);
432                 v3s16 p;
433
434                 p = orpi + veci + of + rs / 2;
435                 if (p.Z >= node_min.Z && p.Z <= node_max.Z &&
436                                 p.X >= node_min.X && p.X <= node_max.X) {
437                         u32 index = (p.Z - node_min.Z) * mg->ystride + (p.X - node_min.X);
438                         s16 h = mg->ridge_heightmap[index];
439                         if (h < p.Y)
440                                 return;
441                 } else if (p.Y > water_level) {
442                         return; // If it's not in our heightmap, use a simple heuristic
443                 }
444
445                 p = orpi + of + rs / 2;
446                 if (p.Z >= node_min.Z && p.Z <= node_max.Z &&
447                                 p.X >= node_min.X && p.X <= node_max.X) {
448                         u32 index = (p.Z - node_min.Z) * mg->ystride + (p.X - node_min.X);
449                         s16 h = mg->ridge_heightmap[index];
450                         if (h < p.Y)
451                                 return;
452                 } else if (p.Y > water_level) {
453                         return;
454                 }
455         }
456
457         vec += main_direction;
458
459         v3f rp = orp + vec;
460         if (rp.X < 0)
461                 rp.X = 0;
462         else if (rp.X >= ar.X)
463                 rp.X = ar.X - 1;
464
465         if (rp.Y < route_y_min)
466                 rp.Y = route_y_min;
467         else if (rp.Y >= route_y_max)
468                 rp.Y = route_y_max - 1;
469
470         if (rp.Z < 0)
471                 rp.Z = 0;
472         else if (rp.Z >= ar.Z)
473                 rp.Z = ar.Z - 1;
474
475         vec = rp - orp;
476
477         float veclen = vec.getLength();
478         if (veclen < 0.05)
479                 veclen = 1.0;
480
481         // Every second section is rough
482         bool randomize_xz = (ps->range(1, 2) == 1);
483
484         // Make a ravine every once in a while if it's long enough
485         //float xylen = vec.X * vec.X + vec.Z * vec.Z;
486         //disable ravines for now
487         bool is_ravine = false; //(xylen > 500.0) && !large_cave && (ps->range(1, 8) == 1);
488
489         // Carve routes
490         for (float f = 0; f < 1.0; f += 1.0 / veclen)
491                 carveRoute(vec, f, randomize_xz, is_ravine);
492
493         orp = rp;
494 }
495
496
497 void CaveV7::carveRoute(v3f vec, float f, bool randomize_xz, bool is_ravine) {
498         MapNode airnode(CONTENT_AIR);
499         MapNode waternode(c_water_source);
500         MapNode lavanode(c_lava_source);
501
502         v3s16 startp(orp.X, orp.Y, orp.Z);
503         startp += of;
504
505         float nval = NoisePerlin3D(np_caveliquids, startp.X,
506                                                         startp.Y, startp.Z, mg->seed);
507         MapNode liquidnode = nval < 0.40 ? lavanode : waternode;
508
509         v3f fp = orp + vec * f;
510         fp.X += 0.1 * ps->range(-10, 10);
511         fp.Z += 0.1 * ps->range(-10, 10);
512         v3s16 cp(fp.X, fp.Y, fp.Z);
513
514         s16 d0 = -rs/2;
515         s16 d1 = d0 + rs;
516         if (randomize_xz) {
517                 d0 += ps->range(-1, 1);
518                 d1 += ps->range(-1, 1);
519         }
520
521         bool flat_cave_floor = !large_cave && ps->range(0, 2) == 2;
522         bool should_make_cave_hole = ps->range(1, 10) == 1;
523
524         for (s16 z0 = d0; z0 <= d1; z0++) {
525                 s16 si = rs / 2 - MYMAX(0, abs(z0) - rs / 7 - 1);
526                 for (s16 x0 = -si - ps->range(0,1); x0 <= si - 1 + ps->range(0,1); x0++) {
527                         s16 maxabsxz = MYMAX(abs(x0), abs(z0));
528
529                         s16 si2 = is_ravine ? MYMIN(ps->range(25, 26), ar.Y) :
530                                                                  rs / 2 - MYMAX(0, maxabsxz - rs / 7 - 1);
531
532                         for (s16 y0 = -si2; y0 <= si2; y0++) {
533                                 // Make better floors in small caves
534                                 if(flat_cave_floor && y0 <= -rs/2 && rs<=7)
535                                         continue;
536
537                                 if (large_cave_is_flat) {
538                                         // Make large caves not so tall
539                                         if (rs > 7 && abs(y0) >= rs / 3)
540                                                 continue;
541                                 }
542
543                                 v3s16 p(cp.X + x0, cp.Y + y0, cp.Z + z0);
544                                 p += of;
545
546                                 if (!is_ravine && mg->heightmap && should_make_cave_hole &&
547                                         p.X <= node_max.X && p.Z <= node_max.Z) {
548                                         int maplen = node_max.X - node_min.X + 1;
549                                         int idx = (p.Z - node_min.Z) * maplen + (p.X - node_min.X);
550                                         if (p.Y >= mg->heightmap[idx] - 2)
551                                                 continue;
552                                 }
553
554                                 if (vm->m_area.contains(p) == false)
555                                         continue;
556
557                                 u32 i = vm->m_area.index(p);
558
559                                 // Don't replace air, water, lava, or ice
560                                 content_t c = vm->m_data[i].getContent();
561                                 if (!ndef->get(c).is_ground_content || c == CONTENT_AIR ||
562                                         c == c_water_source || c == c_lava_source || c == c_ice)
563                                         continue;
564
565                                 if (large_cave) {
566                                         int full_ymin = node_min.Y - MAP_BLOCKSIZE;
567                                         int full_ymax = node_max.Y + MAP_BLOCKSIZE;
568
569                                         if (flooded && full_ymin < water_level && full_ymax > water_level)
570                                                 vm->m_data[i] = (p.Y <= water_level) ? waternode : airnode;
571                                         else if (flooded && full_ymax < water_level)
572                                                 vm->m_data[i] = (p.Y < startp.Y - 4) ? liquidnode : airnode;
573                                         else
574                                                 vm->m_data[i] = airnode;
575                                 } else {
576                                         if (c == CONTENT_IGNORE)
577                                                 continue;
578
579                                         vm->m_data[i] = airnode;
580                                         vm->m_flags[i] |= VMANIP_FLAG_CAVE;
581                                 }
582                         }
583                 }
584         }
585 }
586
587
588 ///////////////////////////////////////// Caves V5
589
590
591 CaveV5::CaveV5(MapgenV5 *mg, PseudoRandom *ps) {
592         this->mg   = mg;
593         this->vm   = mg->vm;
594         this->ndef = mg->ndef;
595         this->water_level = mg->water_level;
596         this->ps = ps;
597         this->c_water_source = mg->c_water_source;
598         this->c_lava_source  = mg->c_lava_source;
599         this->c_ice          = mg->c_ice;
600         this->np_caveliquids = &nparams_caveliquids;
601
602         dswitchint = ps->range(1, 14);
603         flooded    = ps->range(1, 2) == 2;
604
605         part_max_length_rs = ps->range(2, 4);
606         tunnel_routepoints = ps->range(5, ps->range(15, 30));
607         min_tunnel_diameter = 5;
608         max_tunnel_diameter = ps->range(7, ps->range(8, 24));
609
610         large_cave_is_flat = (ps->range(0, 1) == 0);
611 }
612
613
614 void CaveV5::makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height) {
615         node_min = nmin;
616         node_max = nmax;
617         main_direction = v3f(0, 0, 0);
618
619         // Allowed route area size in nodes
620         ar = node_max - node_min + v3s16(1, 1, 1);
621         // Area starting point in nodes
622         of = node_min;
623
624         // Allow a bit more
625         //(this should be more than the maximum radius of the tunnel)
626         s16 insure = 10;
627         s16 more = MYMAX(MAP_BLOCKSIZE - max_tunnel_diameter / 2 - insure, 1);
628         ar += v3s16(1,0,1) * more * 2;
629         of -= v3s16(1,0,1) * more;
630
631         route_y_min = 0;
632         // Allow half a diameter + 7 over stone surface
633         route_y_max = -of.Y + max_stone_y + max_tunnel_diameter / 2 + 7;
634
635         // Limit maximum to area
636         route_y_max = rangelim(route_y_max, 0, ar.Y - 1);
637
638         s16 min = 0;
639                 if (node_min.Y < water_level && node_max.Y > water_level) {
640                         min = water_level - max_tunnel_diameter/3 - of.Y;
641                         route_y_max = water_level + max_tunnel_diameter/3 - of.Y;
642                 }
643         route_y_min = ps->range(min, min + max_tunnel_diameter);
644         route_y_min = rangelim(route_y_min, 0, route_y_max);
645
646         s16 route_start_y_min = route_y_min;
647         s16 route_start_y_max = route_y_max;
648
649         route_start_y_min = rangelim(route_start_y_min, 0, ar.Y - 1);
650         route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y - 1);
651
652         // Randomize starting position
653         orp = v3f(
654                 (float)(ps->next() % ar.X) + 0.5,
655                 (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5,
656                 (float)(ps->next() % ar.Z) + 0.5
657         );
658
659         // Add generation notify begin event
660         v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
661         GenNotifyType notifytype = GENNOTIFY_LARGECAVE_BEGIN;
662         mg->gennotify.addEvent(notifytype, abs_pos);
663
664         // Generate some tunnel starting from orp
665         for (u16 j = 0; j < tunnel_routepoints; j++)
666                 makeTunnel(j % dswitchint == 0);
667
668         // Add generation notify end event
669         abs_pos = v3s16(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
670         notifytype = GENNOTIFY_LARGECAVE_END;
671         mg->gennotify.addEvent(notifytype, abs_pos);
672 }
673
674
675 void CaveV5::makeTunnel(bool dirswitch) {
676
677         // Randomize size
678         s16 min_d = min_tunnel_diameter;
679         s16 max_d = max_tunnel_diameter;
680         rs = ps->range(min_d, max_d);
681
682         v3s16 maxlen;
683         maxlen = v3s16(
684                 rs * part_max_length_rs,
685                 rs * part_max_length_rs / 2,
686                 rs * part_max_length_rs
687         );
688
689         v3f vec;
690         // Jump downward sometimes
691         vec = v3f(
692                 (float)(ps->next() % (maxlen.X * 1)) - (float)maxlen.X / 2,
693                 (float)(ps->next() % (maxlen.Y * 1)) - (float)maxlen.Y / 2,
694                 (float)(ps->next() % (maxlen.Z * 1)) - (float)maxlen.Z / 2
695         );
696
697         // Do not make large caves that are above ground.
698         // It is only necessary to check the startpoint and endpoint.
699         v3s16 orpi(orp.X, orp.Y, orp.Z);
700         v3s16 veci(vec.X, vec.Y, vec.Z);
701         v3s16 p;
702
703         p = orpi + veci + of + rs / 2;
704         if (p.Z >= node_min.Z && p.Z <= node_max.Z &&
705                         p.X >= node_min.X && p.X <= node_max.X) {
706                 u32 index = (p.Z - node_min.Z) * mg->ystride + (p.X - node_min.X);
707                 s16 h = mg->heightmap[index];
708                 if (h < p.Y)
709                         return;
710         } else if (p.Y > water_level) {
711                 return; // If it's not in our heightmap, use a simple heuristic
712         }
713
714         p = orpi + of + rs / 2;
715         if (p.Z >= node_min.Z && p.Z <= node_max.Z &&
716                 p.X >= node_min.X && p.X <= node_max.X) {
717                 u32 index = (p.Z - node_min.Z) * mg->ystride + (p.X - node_min.X);
718                 s16 h = mg->heightmap[index];
719                 if (h < p.Y)
720                         return;
721         } else if (p.Y > water_level) {
722                 return;
723         }
724
725         vec += main_direction;
726
727         v3f rp = orp + vec;
728         if (rp.X < 0)
729                 rp.X = 0;
730         else if (rp.X >= ar.X)
731                 rp.X = ar.X - 1;
732
733         if (rp.Y < route_y_min)
734                 rp.Y = route_y_min;
735         else if (rp.Y >= route_y_max)
736                 rp.Y = route_y_max - 1;
737
738         if (rp.Z < 0)
739                 rp.Z = 0;
740         else if (rp.Z >= ar.Z)
741                 rp.Z = ar.Z - 1;
742
743         vec = rp - orp;
744
745         float veclen = vec.getLength();
746         if (veclen < 0.05)
747                 veclen = 1.0;
748
749         // Every second section is rough
750         bool randomize_xz = (ps->range(1, 2) == 1);
751
752         // Make a ravine every once in a while if it's long enough
753         //float xylen = vec.X * vec.X + vec.Z * vec.Z;
754         //disable ravines for now
755         bool is_ravine = false; //(xylen > 500.0) && !large_cave && (ps->range(1, 8) == 1);
756
757         // Carve routes
758         for (float f = 0; f < 1.0; f += 1.0 / veclen)
759                 carveRoute(vec, f, randomize_xz, is_ravine);
760
761         orp = rp;
762 }
763
764
765 void CaveV5::carveRoute(v3f vec, float f, bool randomize_xz, bool is_ravine) {
766         MapNode airnode(CONTENT_AIR);
767         MapNode waternode(c_water_source);
768         MapNode lavanode(c_lava_source);
769
770         v3s16 startp(orp.X, orp.Y, orp.Z);
771         startp += of;
772
773         float nval = NoisePerlin3D(np_caveliquids, startp.X,
774                                                         startp.Y, startp.Z, mg->seed);
775         MapNode liquidnode = nval < 0.40 ? lavanode : waternode;
776
777         v3f fp = orp + vec * f;
778         fp.X += 0.1 * ps->range(-10, 10);
779         fp.Z += 0.1 * ps->range(-10, 10);
780         v3s16 cp(fp.X, fp.Y, fp.Z);
781
782         s16 d0 = -rs/2;
783         s16 d1 = d0 + rs;
784         if (randomize_xz) {
785                 d0 += ps->range(-1, 1);
786                 d1 += ps->range(-1, 1);
787         }
788
789         bool should_make_cave_hole = ps->range(1, 10) == 1;
790
791         for (s16 z0 = d0; z0 <= d1; z0++) {
792                 s16 si = rs / 2 - MYMAX(0, abs(z0) - rs / 7 - 1);
793                 for (s16 x0 = -si - ps->range(0,1); x0 <= si - 1 + ps->range(0,1); x0++) {
794                         s16 maxabsxz = MYMAX(abs(x0), abs(z0));
795
796                         s16 si2 = is_ravine ? MYMIN(ps->range(25, 26), ar.Y) :
797                                                                  rs / 2 - MYMAX(0, maxabsxz - rs / 7 - 1);
798
799                         for (s16 y0 = -si2; y0 <= si2; y0++) {
800                                 if (large_cave_is_flat) {
801                                         // Make large caves not so tall
802                                         if (rs > 7 && abs(y0) >= rs / 3)
803                                                 continue;
804                                 }
805
806                                 v3s16 p(cp.X + x0, cp.Y + y0, cp.Z + z0);
807                                 p += of;
808
809                                 if (!is_ravine && mg->heightmap && should_make_cave_hole &&
810                                         p.X <= node_max.X && p.Z <= node_max.Z) {
811                                         int maplen = node_max.X - node_min.X + 1;
812                                         int idx = (p.Z - node_min.Z) * maplen + (p.X - node_min.X);
813                                         if (p.Y >= mg->heightmap[idx] - 2)
814                                                 continue;
815                                 }
816
817                                 if (vm->m_area.contains(p) == false)
818                                         continue;
819
820                                 u32 i = vm->m_area.index(p);
821
822                                 // Don't replace air, water, lava, or ice
823                                 content_t c = vm->m_data[i].getContent();
824                                 if (!ndef->get(c).is_ground_content || c == CONTENT_AIR ||
825                                         c == c_water_source || c == c_lava_source || c == c_ice)
826                                         continue;
827
828                                 int full_ymin = node_min.Y - MAP_BLOCKSIZE;
829                                 int full_ymax = node_max.Y + MAP_BLOCKSIZE;
830
831                                 if (flooded && full_ymin < water_level && full_ymax > water_level)
832                                         vm->m_data[i] = (p.Y <= water_level) ? waternode : airnode;
833                                 else if (flooded && full_ymax < water_level)
834                                         vm->m_data[i] = (p.Y < startp.Y - 4) ? liquidnode : airnode;
835                                 else
836                                         vm->m_data[i] = airnode;
837                         }
838                 }
839         }
840 }
841