]> git.lizzy.rs Git - minetest.git/blob - src/cavegen.cpp
VoxelManip cleanups (const ref, const move) + function removal (#6169)
[minetest.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 "mg_biome.h"
27 #include "cavegen.h"
28
29 static NoiseParams nparams_caveliquids(0, 1, v3f(150.0, 150.0, 150.0), 776, 3, 0.6, 2.0);
30
31
32 ////
33 //// CavesNoiseIntersection
34 ////
35
36 CavesNoiseIntersection::CavesNoiseIntersection(
37         INodeDefManager *nodedef, BiomeManager *biomemgr, v3s16 chunksize,
38         NoiseParams *np_cave1, NoiseParams *np_cave2, s32 seed, float cave_width)
39 {
40         assert(nodedef);
41         assert(biomemgr);
42
43         m_ndef = nodedef;
44         m_bmgr = biomemgr;
45
46         m_csize = chunksize;
47         m_cave_width = cave_width;
48
49         m_ystride    = m_csize.X;
50         m_zstride_1d = m_csize.X * (m_csize.Y + 1);
51
52         // Noises are created using 1-down overgeneration
53         // A Nx-by-1-by-Nz-sized plane is at the bottom of the desired for
54         // re-carving the solid overtop placed for blocking sunlight
55         noise_cave1 = new Noise(np_cave1, seed, m_csize.X, m_csize.Y + 1, m_csize.Z);
56         noise_cave2 = new Noise(np_cave2, seed, m_csize.X, m_csize.Y + 1, m_csize.Z);
57 }
58
59
60 CavesNoiseIntersection::~CavesNoiseIntersection()
61 {
62         delete noise_cave1;
63         delete noise_cave2;
64 }
65
66
67 void CavesNoiseIntersection::generateCaves(MMVManip *vm,
68         v3s16 nmin, v3s16 nmax, u8 *biomemap)
69 {
70         assert(vm);
71         assert(biomemap);
72
73         noise_cave1->perlinMap3D(nmin.X, nmin.Y - 1, nmin.Z);
74         noise_cave2->perlinMap3D(nmin.X, nmin.Y - 1, nmin.Z);
75
76         const v3s16 &em = vm->m_area.getExtent();
77         u32 index2d = 0;  // Biomemap index
78
79         for (s16 z = nmin.Z; z <= nmax.Z; z++)
80         for (s16 x = nmin.X; x <= nmax.X; x++, index2d++) {
81                 bool column_is_open = false;  // Is column open to overground
82                 bool is_under_river = false;  // Is column under river water
83                 bool is_under_tunnel = false;  // Is tunnel or is under tunnel
84                 // Indexes at column top
85                 u32 vi = vm->m_area.index(x, nmax.Y, z);
86                 u32 index3d = (z - nmin.Z) * m_zstride_1d + m_csize.Y * m_ystride +
87                         (x - nmin.X);  // 3D noise index
88                 // Biome of column
89                 Biome *biome = (Biome *)m_bmgr->getRaw(biomemap[index2d]);
90                 u16 depth_top = biome->depth_top;
91                 u16 base_filler = depth_top + biome->depth_filler;
92                 u16 depth_riverbed = biome->depth_riverbed;
93                 u16 nplaced = 0;
94                 // Don't excavate the overgenerated stone at nmax.Y + 1,
95                 // this creates a 'roof' over the tunnel, preventing light in
96                 // tunnels at mapchunk borders when generating mapchunks upwards.
97                 // This 'roof' is removed when the mapchunk above is generated.
98                 for (s16 y = nmax.Y; y >= nmin.Y - 1; y--,
99                                 index3d -= m_ystride,
100                                 vm->m_area.add_y(em, vi, -1)) {
101
102                         content_t c = vm->m_data[vi].getContent();
103                         if (c == CONTENT_AIR || c == biome->c_water_top ||
104                                         c == biome->c_water) {
105                                 column_is_open = true;
106                                 continue;
107                         } else if (c == biome->c_river_water) {
108                                 column_is_open = true;
109                                 is_under_river = true;
110                                 continue;
111                         }
112                         // Ground
113                         float d1 = contour(noise_cave1->result[index3d]);
114                         float d2 = contour(noise_cave2->result[index3d]);
115
116                         if (d1 * d2 > m_cave_width && m_ndef->get(c).is_ground_content) {
117                                 // In tunnel and ground content, excavate
118                                 vm->m_data[vi] = MapNode(CONTENT_AIR);
119                                 is_under_tunnel = true;
120                         } else if (column_is_open && is_under_tunnel &&
121                                         (c == biome->c_stone || c == biome->c_filler)) {
122                                 // Tunnel entrance floor, place biome surface nodes
123                                 if (is_under_river) {
124                                         if (nplaced < depth_riverbed) {
125                                                 vm->m_data[vi] = MapNode(biome->c_riverbed);
126                                                 nplaced++;
127                                         } else {
128                                                 // Disable top/filler placement
129                                                 column_is_open = false;
130                                                 is_under_river = false;
131                                                 is_under_tunnel = false;
132                                         }
133                                 } else if (nplaced < depth_top) {
134                                         vm->m_data[vi] = MapNode(biome->c_top);
135                                         nplaced++;
136                                 } else if (nplaced < base_filler) {
137                                         vm->m_data[vi] = MapNode(biome->c_filler);
138                                         nplaced++;
139                                 } else {
140                                         // Disable top/filler placement
141                                         column_is_open = false;
142                                         is_under_tunnel = false;
143                                 }
144                         } else {
145                                 // Not tunnel or tunnel entrance floor
146                                 column_is_open = false;
147                         }
148                 }
149         }
150 }
151
152
153 ////
154 //// CavernsNoise
155 ////
156
157 CavernsNoise::CavernsNoise(
158         INodeDefManager *nodedef, v3s16 chunksize, NoiseParams *np_cavern,
159         s32 seed, float cavern_limit, float cavern_taper, float cavern_threshold)
160 {
161         assert(nodedef);
162
163         m_ndef  = nodedef;
164
165         m_csize            = chunksize;
166         m_cavern_limit     = cavern_limit;
167         m_cavern_taper     = cavern_taper;
168         m_cavern_threshold = cavern_threshold;
169
170         m_ystride = m_csize.X;
171         m_zstride_1d = m_csize.X * (m_csize.Y + 1);
172
173         // Noise is created using 1-down overgeneration
174         // A Nx-by-1-by-Nz-sized plane is at the bottom of the desired for
175         // re-carving the solid overtop placed for blocking sunlight
176         noise_cavern = new Noise(np_cavern, seed, m_csize.X, m_csize.Y + 1, m_csize.Z);
177
178         c_water_source = m_ndef->getId("mapgen_water_source");
179         if (c_water_source == CONTENT_IGNORE)
180                 c_water_source = CONTENT_AIR;
181
182         c_lava_source = m_ndef->getId("mapgen_lava_source");
183         if (c_lava_source == CONTENT_IGNORE)
184                 c_lava_source = CONTENT_AIR;
185 }
186
187
188 CavernsNoise::~CavernsNoise()
189 {
190         delete noise_cavern;
191 }
192
193
194 bool CavernsNoise::generateCaverns(MMVManip *vm, v3s16 nmin, v3s16 nmax)
195 {
196         assert(vm);
197
198         // Calculate noise
199         noise_cavern->perlinMap3D(nmin.X, nmin.Y - 1, nmin.Z);
200
201         // Cache cavern_amp values
202         float *cavern_amp = new float[m_csize.Y + 1];
203         u8 cavern_amp_index = 0;  // Index zero at column top
204         for (s16 y = nmax.Y; y >= nmin.Y - 1; y--, cavern_amp_index++) {
205                 cavern_amp[cavern_amp_index] =
206                         MYMIN((m_cavern_limit - y) / (float)m_cavern_taper, 1.0f);
207         }
208
209         //// Place nodes
210         bool near_cavern = false;
211         const v3s16 &em = vm->m_area.getExtent();
212         u32 index2d = 0;
213
214         for (s16 z = nmin.Z; z <= nmax.Z; z++)
215         for (s16 x = nmin.X; x <= nmax.X; x++, index2d++) {
216                 // Reset cave_amp index to column top
217                 cavern_amp_index = 0;
218                 // Initial voxelmanip index at column top
219                 u32 vi = vm->m_area.index(x, nmax.Y, z);
220                 // Initial 3D noise index at column top
221                 u32 index3d = (z - nmin.Z) * m_zstride_1d + m_csize.Y * m_ystride +
222                         (x - nmin.X);
223                 // Don't excavate the overgenerated stone at node_max.Y + 1,
224                 // this creates a 'roof' over the cavern, preventing light in
225                 // caverns at mapchunk borders when generating mapchunks upwards.
226                 // This 'roof' is excavated when the mapchunk above is generated.
227                 for (s16 y = nmax.Y; y >= nmin.Y - 1; y--,
228                                 index3d -= m_ystride,
229                                 vm->m_area.add_y(em, vi, -1),
230                                 cavern_amp_index++) {
231                         content_t c = vm->m_data[vi].getContent();
232                         float n_absamp_cavern = fabs(noise_cavern->result[index3d]) *
233                                 cavern_amp[cavern_amp_index];
234                         // Disable CavesRandomWalk at a safe distance from caverns
235                         // to avoid excessively spreading liquids in caverns.
236                         if (n_absamp_cavern > m_cavern_threshold - 0.1f) {
237                                 near_cavern = true;
238                                 if (n_absamp_cavern > m_cavern_threshold &&
239                                                 m_ndef->get(c).is_ground_content)
240                                         vm->m_data[vi] = MapNode(CONTENT_AIR);
241                         }
242                 }
243         }
244
245         delete[] cavern_amp;
246
247         return near_cavern;
248 }
249
250
251 ////
252 //// CavesRandomWalk
253 ////
254
255 CavesRandomWalk::CavesRandomWalk(
256         INodeDefManager *ndef,
257         GenerateNotifier *gennotify,
258         s32 seed,
259         int water_level,
260         content_t water_source,
261         content_t lava_source,
262         int lava_depth)
263 {
264         assert(ndef);
265
266         this->ndef           = ndef;
267         this->gennotify      = gennotify;
268         this->seed           = seed;
269         this->water_level    = water_level;
270         this->np_caveliquids = &nparams_caveliquids;
271         this->lava_depth     = lava_depth;
272
273         c_water_source = water_source;
274         if (c_water_source == CONTENT_IGNORE)
275                 c_water_source = ndef->getId("mapgen_water_source");
276         if (c_water_source == CONTENT_IGNORE)
277                 c_water_source = CONTENT_AIR;
278
279         c_lava_source = lava_source;
280         if (c_lava_source == CONTENT_IGNORE)
281                 c_lava_source = ndef->getId("mapgen_lava_source");
282         if (c_lava_source == CONTENT_IGNORE)
283                 c_lava_source = CONTENT_AIR;
284 }
285
286
287 void CavesRandomWalk::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax,
288         PseudoRandom *ps, bool is_large_cave, int max_stone_height, s16 *heightmap)
289 {
290         assert(vm);
291         assert(ps);
292
293         this->vm         = vm;
294         this->ps         = ps;
295         this->node_min   = nmin;
296         this->node_max   = nmax;
297         this->heightmap  = heightmap;
298         this->large_cave = is_large_cave;
299
300         this->ystride = nmax.X - nmin.X + 1;
301
302         // Set initial parameters from randomness
303         int dswitchint = ps->range(1, 14);
304         flooded = ps->range(1, 2) == 2;
305
306         if (large_cave) {
307                 part_max_length_rs = ps->range(2, 4);
308                 tunnel_routepoints = ps->range(5, ps->range(15, 30));
309                 min_tunnel_diameter = 5;
310                 max_tunnel_diameter = ps->range(7, ps->range(8, 24));
311         } else {
312                 part_max_length_rs = ps->range(2, 9);
313                 tunnel_routepoints = ps->range(10, ps->range(15, 30));
314                 min_tunnel_diameter = 2;
315                 max_tunnel_diameter = ps->range(2, 6);
316         }
317
318         large_cave_is_flat = (ps->range(0, 1) == 0);
319
320         main_direction = v3f(0, 0, 0);
321
322         // Allowed route area size in nodes
323         ar = node_max - node_min + v3s16(1, 1, 1);
324         // Area starting point in nodes
325         of = node_min;
326
327         // Allow a bit more
328         //(this should be more than the maximum radius of the tunnel)
329         const s16 insure = 10;
330         s16 more = MYMAX(MAP_BLOCKSIZE - max_tunnel_diameter / 2 - insure, 1);
331         ar += v3s16(1, 0, 1) * more * 2;
332         of -= v3s16(1, 0, 1) * more;
333
334         route_y_min = 0;
335         // Allow half a diameter + 7 over stone surface
336         route_y_max = -of.Y + max_stone_y + max_tunnel_diameter / 2 + 7;
337
338         // Limit maximum to area
339         route_y_max = rangelim(route_y_max, 0, ar.Y - 1);
340
341         if (large_cave) {
342                 s16 minpos = 0;
343                 if (node_min.Y < water_level && node_max.Y > water_level) {
344                         minpos = water_level - max_tunnel_diameter / 3 - of.Y;
345                         route_y_max = water_level + max_tunnel_diameter / 3 - of.Y;
346                 }
347                 route_y_min = ps->range(minpos, minpos + max_tunnel_diameter);
348                 route_y_min = rangelim(route_y_min, 0, route_y_max);
349         }
350
351         s16 route_start_y_min = route_y_min;
352         s16 route_start_y_max = route_y_max;
353
354         route_start_y_min = rangelim(route_start_y_min, 0, ar.Y - 1);
355         route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y - 1);
356
357         // Randomize starting position
358         orp.Z = (float)(ps->next() % ar.Z) + 0.5f;
359         orp.Y = (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5f;
360         orp.X = (float)(ps->next() % ar.X) + 0.5f;
361
362         // Add generation notify begin event
363         if (gennotify) {
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                 gennotify->addEvent(notifytype, abs_pos);
368         }
369
370         // Generate some tunnel starting from orp
371         for (u16 j = 0; j < tunnel_routepoints; j++)
372                 makeTunnel(j % dswitchint == 0);
373
374         // Add generation notify end event
375         if (gennotify) {
376                 v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
377                 GenNotifyType notifytype = large_cave ?
378                         GENNOTIFY_LARGECAVE_END : GENNOTIFY_CAVE_END;
379                 gennotify->addEvent(notifytype, abs_pos);
380         }
381 }
382
383
384 void CavesRandomWalk::makeTunnel(bool dirswitch)
385 {
386         if (dirswitch && !large_cave) {
387                 main_direction.Z = ((float)(ps->next() % 20) - (float)10) / 10;
388                 main_direction.Y = ((float)(ps->next() % 20) - (float)10) / 30;
389                 main_direction.X = ((float)(ps->next() % 20) - (float)10) / 10;
390
391                 main_direction *= (float)ps->range(0, 10) / 10;
392         }
393
394         // Randomize size
395         s16 min_d = min_tunnel_diameter;
396         s16 max_d = max_tunnel_diameter;
397         rs = ps->range(min_d, max_d);
398         s16 rs_part_max_length_rs = rs * part_max_length_rs;
399
400         v3s16 maxlen;
401         if (large_cave) {
402                 maxlen = v3s16(
403                         rs_part_max_length_rs,
404                         rs_part_max_length_rs / 2,
405                         rs_part_max_length_rs
406                 );
407         } else {
408                 maxlen = v3s16(
409                         rs_part_max_length_rs,
410                         ps->range(1, rs_part_max_length_rs),
411                         rs_part_max_length_rs
412                 );
413         }
414
415         v3f vec;
416         // Jump downward sometimes
417         if (!large_cave && ps->range(0, 12) == 0) {
418                 vec.Z = (float)(ps->next() % (maxlen.Z * 1)) - (float)maxlen.Z / 2;
419                 vec.Y = (float)(ps->next() % (maxlen.Y * 2)) - (float)maxlen.Y;
420                 vec.X = (float)(ps->next() % (maxlen.X * 1)) - (float)maxlen.X / 2;
421         } else {
422                 vec.Z = (float)(ps->next() % (maxlen.Z * 1)) - (float)maxlen.Z / 2;
423                 vec.Y = (float)(ps->next() % (maxlen.Y * 1)) - (float)maxlen.Y / 2;
424                 vec.X = (float)(ps->next() % (maxlen.X * 1)) - (float)maxlen.X / 2;
425         }
426
427         // Do not make caves that are above ground.
428         // It is only necessary to check the startpoint and endpoint.
429         v3s16 p1 = v3s16(orp.X, orp.Y, orp.Z) + of + rs / 2;
430         v3s16 p2 = v3s16(vec.X, vec.Y, vec.Z) + p1;
431         if (isPosAboveSurface(p1) || isPosAboveSurface(p2))
432                 return;
433
434         vec += main_direction;
435
436         v3f rp = orp + vec;
437         if (rp.X < 0)
438                 rp.X = 0;
439         else if (rp.X >= ar.X)
440                 rp.X = ar.X - 1;
441
442         if (rp.Y < route_y_min)
443                 rp.Y = route_y_min;
444         else if (rp.Y >= route_y_max)
445                 rp.Y = route_y_max - 1;
446
447         if (rp.Z < 0)
448                 rp.Z = 0;
449         else if (rp.Z >= ar.Z)
450                 rp.Z = ar.Z - 1;
451
452         vec = rp - orp;
453
454         float veclen = vec.getLength();
455         if (veclen < 0.05f)
456                 veclen = 1.0f;
457
458         // Every second section is rough
459         bool randomize_xz = (ps->range(1, 2) == 1);
460
461         // Carve routes
462         for (float f = 0.f; f < 1.0f; f += 1.0f / veclen)
463                 carveRoute(vec, f, randomize_xz);
464
465         orp = rp;
466 }
467
468
469 void CavesRandomWalk::carveRoute(v3f vec, float f, bool randomize_xz)
470 {
471         MapNode airnode(CONTENT_AIR);
472         MapNode waternode(c_water_source);
473         MapNode lavanode(c_lava_source);
474
475         v3s16 startp(orp.X, orp.Y, orp.Z);
476         startp += of;
477
478         float nval = NoisePerlin3D(np_caveliquids, startp.X,
479                 startp.Y, startp.Z, seed);
480         MapNode liquidnode = (nval < 0.40f && node_max.Y < lava_depth) ?
481                 lavanode : waternode;
482
483         v3f fp = orp + vec * f;
484         fp.X += 0.1f * ps->range(-10, 10);
485         fp.Z += 0.1f * ps->range(-10, 10);
486         v3s16 cp(fp.X, fp.Y, fp.Z);
487
488         s16 d0 = -rs / 2;
489         s16 d1 = d0 + rs;
490         if (randomize_xz) {
491                 d0 += ps->range(-1, 1);
492                 d1 += ps->range(-1, 1);
493         }
494
495         bool flat_cave_floor = !large_cave && ps->range(0, 2) == 2;
496
497         for (s16 z0 = d0; z0 <= d1; z0++) {
498                 s16 si = rs / 2 - MYMAX(0, abs(z0) - rs / 7 - 1);
499                 for (s16 x0 = -si - ps->range(0,1); x0 <= si - 1 + ps->range(0,1); x0++) {
500                         s16 maxabsxz = MYMAX(abs(x0), abs(z0));
501
502                         s16 si2 = rs / 2 - MYMAX(0, maxabsxz - rs / 7 - 1);
503
504                         for (s16 y0 = -si2; y0 <= si2; y0++) {
505                                 // Make better floors in small caves
506                                 if (flat_cave_floor && y0 <= -rs / 2 && rs <= 7)
507                                         continue;
508
509                                 if (large_cave_is_flat) {
510                                         // Make large caves not so tall
511                                         if (rs > 7 && abs(y0) >= rs / 3)
512                                                 continue;
513                                 }
514
515                                 v3s16 p(cp.X + x0, cp.Y + y0, cp.Z + z0);
516                                 p += of;
517
518                                 if (vm->m_area.contains(p) == false)
519                                         continue;
520
521                                 u32 i = vm->m_area.index(p);
522                                 content_t c = vm->m_data[i].getContent();
523                                 if (!ndef->get(c).is_ground_content)
524                                         continue;
525
526                                 if (large_cave) {
527                                         int full_ymin = node_min.Y - MAP_BLOCKSIZE;
528                                         int full_ymax = node_max.Y + MAP_BLOCKSIZE;
529
530                                         if (flooded && full_ymin < water_level && full_ymax > water_level)
531                                                 vm->m_data[i] = (p.Y <= water_level) ? waternode : airnode;
532                                         else if (flooded && full_ymax < water_level)
533                                                 vm->m_data[i] = (p.Y < startp.Y - 4) ? liquidnode : airnode;
534                                         else
535                                                 vm->m_data[i] = airnode;
536                                 } else {
537                                         if (c == CONTENT_IGNORE)
538                                                 continue;
539
540                                         vm->m_data[i] = airnode;
541                                         vm->m_flags[i] |= VMANIP_FLAG_CAVE;
542                                 }
543                         }
544                 }
545         }
546 }
547
548
549 inline bool CavesRandomWalk::isPosAboveSurface(v3s16 p)
550 {
551         if (heightmap != NULL &&
552                         p.Z >= node_min.Z && p.Z <= node_max.Z &&
553                         p.X >= node_min.X && p.X <= node_max.X) {
554                 u32 index = (p.Z - node_min.Z) * ystride + (p.X - node_min.X);
555                 if (heightmap[index] < p.Y)
556                         return true;
557         } else if (p.Y > water_level) {
558                 return true;
559         }
560
561         return false;
562 }
563
564
565 ////
566 //// CavesV6
567 ////
568
569 CavesV6::CavesV6(INodeDefManager *ndef, GenerateNotifier *gennotify,
570         int water_level, content_t water_source, content_t lava_source)
571 {
572         assert(ndef);
573
574         this->ndef        = ndef;
575         this->gennotify   = gennotify;
576         this->water_level = water_level;
577
578         c_water_source = water_source;
579         if (c_water_source == CONTENT_IGNORE)
580                 c_water_source = ndef->getId("mapgen_water_source");
581         if (c_water_source == CONTENT_IGNORE)
582                 c_water_source = CONTENT_AIR;
583
584         c_lava_source = lava_source;
585         if (c_lava_source == CONTENT_IGNORE)
586                 c_lava_source = ndef->getId("mapgen_lava_source");
587         if (c_lava_source == CONTENT_IGNORE)
588                 c_lava_source = CONTENT_AIR;
589 }
590
591
592 void CavesV6::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax,
593         PseudoRandom *ps, PseudoRandom *ps2,
594         bool is_large_cave, int max_stone_height, s16 *heightmap)
595 {
596         assert(vm);
597         assert(ps);
598         assert(ps2);
599
600         this->vm         = vm;
601         this->ps         = ps;
602         this->ps2        = ps2;
603         this->node_min   = nmin;
604         this->node_max   = nmax;
605         this->heightmap  = heightmap;
606         this->large_cave = is_large_cave;
607
608         this->ystride = nmax.X - nmin.X + 1;
609
610         // Set initial parameters from randomness
611         min_tunnel_diameter = 2;
612         max_tunnel_diameter = ps->range(2, 6);
613         int dswitchint      = ps->range(1, 14);
614         if (large_cave) {
615                 part_max_length_rs  = ps->range(2, 4);
616                 tunnel_routepoints  = ps->range(5, ps->range(15, 30));
617                 min_tunnel_diameter = 5;
618                 max_tunnel_diameter = ps->range(7, ps->range(8, 24));
619         } else {
620                 part_max_length_rs = ps->range(2, 9);
621                 tunnel_routepoints = ps->range(10, ps->range(15, 30));
622         }
623         large_cave_is_flat = (ps->range(0, 1) == 0);
624
625         main_direction = v3f(0, 0, 0);
626
627         // Allowed route area size in nodes
628         ar = node_max - node_min + v3s16(1, 1, 1);
629         // Area starting point in nodes
630         of = node_min;
631
632         // Allow a bit more
633         //(this should be more than the maximum radius of the tunnel)
634         const s16 max_spread_amount = MAP_BLOCKSIZE;
635         const s16 insure = 10;
636         s16 more = MYMAX(max_spread_amount - max_tunnel_diameter / 2 - insure, 1);
637         ar += v3s16(1, 0, 1) * more * 2;
638         of -= v3s16(1, 0, 1) * more;
639
640         route_y_min = 0;
641         // Allow half a diameter + 7 over stone surface
642         route_y_max = -of.Y + max_stone_height + max_tunnel_diameter / 2 + 7;
643
644         // Limit maximum to area
645         route_y_max = rangelim(route_y_max, 0, ar.Y - 1);
646
647         if (large_cave) {
648                 s16 minpos = 0;
649                 if (node_min.Y < water_level && node_max.Y > water_level) {
650                         minpos = water_level - max_tunnel_diameter / 3 - of.Y;
651                         route_y_max = water_level + max_tunnel_diameter / 3 - of.Y;
652                 }
653                 route_y_min = ps->range(minpos, minpos + max_tunnel_diameter);
654                 route_y_min = rangelim(route_y_min, 0, route_y_max);
655         }
656
657         s16 route_start_y_min = route_y_min;
658         s16 route_start_y_max = route_y_max;
659
660         route_start_y_min = rangelim(route_start_y_min, 0, ar.Y - 1);
661         route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y - 1);
662
663         // Randomize starting position
664         orp.Z = (float)(ps->next() % ar.Z) + 0.5f;
665         orp.Y = (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5f;
666         orp.X = (float)(ps->next() % ar.X) + 0.5f;
667
668         // Add generation notify begin event
669         if (gennotify != NULL) {
670                 v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
671                 GenNotifyType notifytype = large_cave ?
672                         GENNOTIFY_LARGECAVE_BEGIN : GENNOTIFY_CAVE_BEGIN;
673                 gennotify->addEvent(notifytype, abs_pos);
674         }
675
676         // Generate some tunnel starting from orp
677         for (u16 j = 0; j < tunnel_routepoints; j++)
678                 makeTunnel(j % dswitchint == 0);
679
680         // Add generation notify end event
681         if (gennotify != NULL) {
682                 v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
683                 GenNotifyType notifytype = large_cave ?
684                         GENNOTIFY_LARGECAVE_END : GENNOTIFY_CAVE_END;
685                 gennotify->addEvent(notifytype, abs_pos);
686         }
687 }
688
689
690 void CavesV6::makeTunnel(bool dirswitch)
691 {
692         if (dirswitch && !large_cave) {
693                 main_direction.Z = ((float)(ps->next() % 20) - (float)10) / 10;
694                 main_direction.Y = ((float)(ps->next() % 20) - (float)10) / 30;
695                 main_direction.X = ((float)(ps->next() % 20) - (float)10) / 10;
696
697                 main_direction *= (float)ps->range(0, 10) / 10;
698         }
699
700         // Randomize size
701         s16 min_d = min_tunnel_diameter;
702         s16 max_d = max_tunnel_diameter;
703         rs = ps->range(min_d, max_d);
704         s16 rs_part_max_length_rs = rs * part_max_length_rs;
705
706         v3s16 maxlen;
707         if (large_cave) {
708                 maxlen = v3s16(
709                         rs_part_max_length_rs,
710                         rs_part_max_length_rs / 2,
711                         rs_part_max_length_rs
712                 );
713         } else {
714                 maxlen = v3s16(
715                         rs_part_max_length_rs,
716                         ps->range(1, rs_part_max_length_rs),
717                         rs_part_max_length_rs
718                 );
719         }
720
721         v3f vec;
722         vec.Z = (float)(ps->next() % maxlen.Z) - (float)maxlen.Z / 2;
723         vec.Y = (float)(ps->next() % maxlen.Y) - (float)maxlen.Y / 2;
724         vec.X = (float)(ps->next() % maxlen.X) - (float)maxlen.X / 2;
725
726         // Jump downward sometimes
727         if (!large_cave && ps->range(0, 12) == 0) {
728                 vec.Z = (float)(ps->next() % maxlen.Z) - (float)maxlen.Z / 2;
729                 vec.Y = (float)(ps->next() % (maxlen.Y * 2)) - (float)maxlen.Y;
730                 vec.X = (float)(ps->next() % maxlen.X) - (float)maxlen.X / 2;
731         }
732
733         // Do not make caves that are entirely above ground, to fix shadow bugs
734         // caused by overgenerated large caves.
735         // It is only necessary to check the startpoint and endpoint.
736         v3s16 p1 = v3s16(orp.X, orp.Y, orp.Z) + of + rs / 2;
737         v3s16 p2 = v3s16(vec.X, vec.Y, vec.Z) + p1;
738
739         // If startpoint and endpoint are above ground, disable placement of nodes
740         // in carveRoute while still running all PseudoRandom calls to ensure caves
741         // are consistent with existing worlds.
742         bool tunnel_above_ground =
743                 p1.Y > getSurfaceFromHeightmap(p1) &&
744                 p2.Y > getSurfaceFromHeightmap(p2);
745
746         vec += main_direction;
747
748         v3f rp = orp + vec;
749         if (rp.X < 0)
750                 rp.X = 0;
751         else if (rp.X >= ar.X)
752                 rp.X = ar.X - 1;
753
754         if (rp.Y < route_y_min)
755                 rp.Y = route_y_min;
756         else if (rp.Y >= route_y_max)
757                 rp.Y = route_y_max - 1;
758
759         if (rp.Z < 0)
760                 rp.Z = 0;
761         else if (rp.Z >= ar.Z)
762                 rp.Z = ar.Z - 1;
763
764         vec = rp - orp;
765
766         float veclen = vec.getLength();
767         // As odd as it sounds, veclen is *exactly* 0.0 sometimes, causing a FPE
768         if (veclen < 0.05f)
769                 veclen = 1.0f;
770
771         // Every second section is rough
772         bool randomize_xz = (ps2->range(1, 2) == 1);
773
774         // Carve routes
775         for (float f = 0.f; f < 1.0f; f += 1.0f / veclen)
776                 carveRoute(vec, f, randomize_xz, tunnel_above_ground);
777
778         orp = rp;
779 }
780
781
782 void CavesV6::carveRoute(v3f vec, float f, bool randomize_xz,
783         bool tunnel_above_ground)
784 {
785         MapNode airnode(CONTENT_AIR);
786         MapNode waternode(c_water_source);
787         MapNode lavanode(c_lava_source);
788
789         v3s16 startp(orp.X, orp.Y, orp.Z);
790         startp += of;
791
792         v3f fp = orp + vec * f;
793         fp.X += 0.1f * ps->range(-10, 10);
794         fp.Z += 0.1f * ps->range(-10, 10);
795         v3s16 cp(fp.X, fp.Y, fp.Z);
796
797         s16 d0 = -rs / 2;
798         s16 d1 = d0 + rs;
799         if (randomize_xz) {
800                 d0 += ps->range(-1, 1);
801                 d1 += ps->range(-1, 1);
802         }
803
804         for (s16 z0 = d0; z0 <= d1; z0++) {
805                 s16 si = rs / 2 - MYMAX(0, abs(z0) - rs / 7 - 1);
806                 for (s16 x0 = -si - ps->range(0,1); x0 <= si - 1 + ps->range(0,1); x0++) {
807                         if (tunnel_above_ground)
808                                 continue;
809
810                         s16 maxabsxz = MYMAX(abs(x0), abs(z0));
811                         s16 si2 = rs / 2 - MYMAX(0, maxabsxz - rs / 7 - 1);
812                         for (s16 y0 = -si2; y0 <= si2; y0++) {
813                                 if (large_cave_is_flat) {
814                                         // Make large caves not so tall
815                                         if (rs > 7 && abs(y0) >= rs / 3)
816                                                 continue;
817                                 }
818
819                                 v3s16 p(cp.X + x0, cp.Y + y0, cp.Z + z0);
820                                 p += of;
821
822                                 if (vm->m_area.contains(p) == false)
823                                         continue;
824
825                                 u32 i = vm->m_area.index(p);
826                                 content_t c = vm->m_data[i].getContent();
827                                 if (!ndef->get(c).is_ground_content)
828                                         continue;
829
830                                 if (large_cave) {
831                                         int full_ymin = node_min.Y - MAP_BLOCKSIZE;
832                                         int full_ymax = node_max.Y + MAP_BLOCKSIZE;
833
834                                         if (full_ymin < water_level && full_ymax > water_level) {
835                                                 vm->m_data[i] = (p.Y <= water_level) ? waternode : airnode;
836                                         } else if (full_ymax < water_level) {
837                                                 vm->m_data[i] = (p.Y < startp.Y - 2) ? lavanode : airnode;
838                                         } else {
839                                                 vm->m_data[i] = airnode;
840                                         }
841                                 } else {
842                                         if (c == CONTENT_IGNORE || c == CONTENT_AIR)
843                                                 continue;
844
845                                         vm->m_data[i] = airnode;
846                                         vm->m_flags[i] |= VMANIP_FLAG_CAVE;
847                                 }
848                         }
849                 }
850         }
851 }
852
853
854 inline s16 CavesV6::getSurfaceFromHeightmap(v3s16 p)
855 {
856         if (heightmap != NULL &&
857                         p.Z >= node_min.Z && p.Z <= node_max.Z &&
858                         p.X >= node_min.X && p.X <= node_max.X) {
859                 u32 index = (p.Z - node_min.Z) * ystride + (p.X - node_min.X);
860                 return heightmap[index];
861         } else {
862                 return water_level;
863         }
864 }