]> git.lizzy.rs Git - dragonfireclient.git/blob - src/cavegen.cpp
Document zoom_fov in settingtypes.txt and minetest.conf.example
[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 "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         v3s16 em = vm->m_area.getExtent();
77         u32 index2d = 0;
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_tunnel = false;  // Is tunnel or tunnel floor
84                 u32 vi = vm->m_area.index(x, nmax.Y, z);
85                 u32 index3d = (z - nmin.Z) * m_zstride_1d + m_csize.Y * m_ystride +
86                         (x - nmin.X);
87                 // Biome of column
88                 Biome *biome = (Biome *)m_bmgr->getRaw(biomemap[index2d]);
89
90                 // Don't excavate the overgenerated stone at nmax.Y + 1,
91                 // this creates a 'roof' over the tunnel, preventing light in
92                 // tunnels at mapchunk borders when generating mapchunks upwards.
93                 // This 'roof' is removed when the mapchunk above is generated.
94                 for (s16 y = nmax.Y; y >= nmin.Y - 1; y--,
95                                 index3d -= m_ystride,
96                                 vm->m_area.add_y(em, vi, -1)) {
97
98                         content_t c = vm->m_data[vi].getContent();
99                         if (c == CONTENT_AIR || c == biome->c_water_top ||
100                                         c == biome->c_water) {
101                                 column_is_open = true;
102                                 continue;
103                         } else if (c == biome->c_river_water) {
104                                 column_is_open = true;
105                                 is_under_river = true;
106                                 continue;
107                         }
108                         // Ground
109                         float d1 = contour(noise_cave1->result[index3d]);
110                         float d2 = contour(noise_cave2->result[index3d]);
111
112                         if (d1 * d2 > m_cave_width && m_ndef->get(c).is_ground_content) {
113                                 // In tunnel and ground content, excavate
114                                 vm->m_data[vi] = MapNode(CONTENT_AIR);
115                                 is_tunnel = true;
116                         } else {
117                                 // Not in tunnel or not ground content
118                                 if (is_tunnel && column_is_open &&
119                                                 (c == biome->c_filler || c == biome->c_stone)) {
120                                         // Tunnel entrance floor
121                                         if (is_under_river)
122                                                 vm->m_data[vi] = MapNode(biome->c_riverbed);
123                                         else
124                                                 vm->m_data[vi] = MapNode(biome->c_top);
125                                 }
126
127                                 column_is_open = false;
128                                 is_tunnel = false;
129                         }
130                 }
131         }
132 }
133
134
135 ////
136 //// CavesRandomWalk
137 ////
138
139 CavesRandomWalk::CavesRandomWalk(
140         INodeDefManager *ndef,
141         GenerateNotifier *gennotify,
142         s32 seed,
143         int water_level,
144         content_t water_source,
145         content_t lava_source)
146 {
147         assert(ndef);
148
149         this->ndef           = ndef;
150         this->gennotify      = gennotify;
151         this->seed           = seed;
152         this->water_level    = water_level;
153         this->np_caveliquids = &nparams_caveliquids;
154         this->lava_depth     = DEFAULT_LAVA_DEPTH;
155
156         c_water_source = water_source;
157         if (c_water_source == CONTENT_IGNORE)
158                 c_water_source = ndef->getId("mapgen_water_source");
159         if (c_water_source == CONTENT_IGNORE)
160                 c_water_source = CONTENT_AIR;
161
162         c_lava_source = lava_source;
163         if (c_lava_source == CONTENT_IGNORE)
164                 c_lava_source = ndef->getId("mapgen_lava_source");
165         if (c_lava_source == CONTENT_IGNORE)
166                 c_lava_source = CONTENT_AIR;
167 }
168
169
170 void CavesRandomWalk::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax,
171         PseudoRandom *ps, bool is_large_cave, int max_stone_height, s16 *heightmap)
172 {
173         assert(vm);
174         assert(ps);
175
176         this->vm         = vm;
177         this->ps         = ps;
178         this->node_min   = nmin;
179         this->node_max   = nmax;
180         this->heightmap  = heightmap;
181         this->large_cave = is_large_cave;
182
183         this->ystride = nmax.X - nmin.X + 1;
184
185         // Set initial parameters from randomness
186         int dswitchint = ps->range(1, 14);
187         flooded = ps->range(1, 2) == 2;
188
189         if (large_cave) {
190                 part_max_length_rs = ps->range(2, 4);
191                 tunnel_routepoints = ps->range(5, ps->range(15, 30));
192                 min_tunnel_diameter = 5;
193                 max_tunnel_diameter = ps->range(7, ps->range(8, 24));
194         } else {
195                 part_max_length_rs = ps->range(2, 9);
196                 tunnel_routepoints = ps->range(10, ps->range(15, 30));
197                 min_tunnel_diameter = 2;
198                 max_tunnel_diameter = ps->range(2, 6);
199         }
200
201         large_cave_is_flat = (ps->range(0, 1) == 0);
202
203         main_direction = v3f(0, 0, 0);
204
205         // Allowed route area size in nodes
206         ar = node_max - node_min + v3s16(1, 1, 1);
207         // Area starting point in nodes
208         of = node_min;
209
210         // Allow a bit more
211         //(this should be more than the maximum radius of the tunnel)
212         const s16 insure = 10;
213         s16 more = MYMAX(MAP_BLOCKSIZE - max_tunnel_diameter / 2 - insure, 1);
214         ar += v3s16(1, 0, 1) * more * 2;
215         of -= v3s16(1, 0, 1) * more;
216
217         route_y_min = 0;
218         // Allow half a diameter + 7 over stone surface
219         route_y_max = -of.Y + max_stone_y + max_tunnel_diameter / 2 + 7;
220
221         // Limit maximum to area
222         route_y_max = rangelim(route_y_max, 0, ar.Y - 1);
223
224         if (large_cave) {
225                 s16 minpos = 0;
226                 if (node_min.Y < water_level && node_max.Y > water_level) {
227                         minpos = water_level - max_tunnel_diameter / 3 - of.Y;
228                         route_y_max = water_level + max_tunnel_diameter / 3 - of.Y;
229                 }
230                 route_y_min = ps->range(minpos, minpos + max_tunnel_diameter);
231                 route_y_min = rangelim(route_y_min, 0, route_y_max);
232         }
233
234         s16 route_start_y_min = route_y_min;
235         s16 route_start_y_max = route_y_max;
236
237         route_start_y_min = rangelim(route_start_y_min, 0, ar.Y - 1);
238         route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y - 1);
239
240         // Randomize starting position
241         orp.Z = (float)(ps->next() % ar.Z) + 0.5f;
242         orp.Y = (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5f;
243         orp.X = (float)(ps->next() % ar.X) + 0.5f;
244
245         // Add generation notify begin event
246         if (gennotify) {
247                 v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
248                 GenNotifyType notifytype = large_cave ?
249                         GENNOTIFY_LARGECAVE_BEGIN : GENNOTIFY_CAVE_BEGIN;
250                 gennotify->addEvent(notifytype, abs_pos);
251         }
252
253         // Generate some tunnel starting from orp
254         for (u16 j = 0; j < tunnel_routepoints; j++)
255                 makeTunnel(j % dswitchint == 0);
256
257         // Add generation notify end event
258         if (gennotify) {
259                 v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
260                 GenNotifyType notifytype = large_cave ?
261                         GENNOTIFY_LARGECAVE_END : GENNOTIFY_CAVE_END;
262                 gennotify->addEvent(notifytype, abs_pos);
263         }
264 }
265
266
267 void CavesRandomWalk::makeTunnel(bool dirswitch)
268 {
269         if (dirswitch && !large_cave) {
270                 main_direction.Z = ((float)(ps->next() % 20) - (float)10) / 10;
271                 main_direction.Y = ((float)(ps->next() % 20) - (float)10) / 30;
272                 main_direction.X = ((float)(ps->next() % 20) - (float)10) / 10;
273
274                 main_direction *= (float)ps->range(0, 10) / 10;
275         }
276
277         // Randomize size
278         s16 min_d = min_tunnel_diameter;
279         s16 max_d = max_tunnel_diameter;
280         rs = ps->range(min_d, max_d);
281         s16 rs_part_max_length_rs = rs * part_max_length_rs;
282
283         v3s16 maxlen;
284         if (large_cave) {
285                 maxlen = v3s16(
286                         rs_part_max_length_rs,
287                         rs_part_max_length_rs / 2,
288                         rs_part_max_length_rs
289                 );
290         } else {
291                 maxlen = v3s16(
292                         rs_part_max_length_rs,
293                         ps->range(1, rs_part_max_length_rs),
294                         rs_part_max_length_rs
295                 );
296         }
297
298         v3f vec;
299         // Jump downward sometimes
300         if (!large_cave && ps->range(0, 12) == 0) {
301                 vec.Z = (float)(ps->next() % (maxlen.Z * 1)) - (float)maxlen.Z / 2;
302                 vec.Y = (float)(ps->next() % (maxlen.Y * 2)) - (float)maxlen.Y;
303                 vec.X = (float)(ps->next() % (maxlen.X * 1)) - (float)maxlen.X / 2;
304         } else {
305                 vec.Z = (float)(ps->next() % (maxlen.Z * 1)) - (float)maxlen.Z / 2;
306                 vec.Y = (float)(ps->next() % (maxlen.Y * 1)) - (float)maxlen.Y / 2;
307                 vec.X = (float)(ps->next() % (maxlen.X * 1)) - (float)maxlen.X / 2;
308         }
309
310         // Do not make caves that are above ground.
311         // It is only necessary to check the startpoint and endpoint.
312         v3s16 p1 = v3s16(orp.X, orp.Y, orp.Z) + of + rs / 2;
313         v3s16 p2 = v3s16(vec.X, vec.Y, vec.Z) + p1;
314         if (isPosAboveSurface(p1) || isPosAboveSurface(p2))
315                 return;
316
317         vec += main_direction;
318
319         v3f rp = orp + vec;
320         if (rp.X < 0)
321                 rp.X = 0;
322         else if (rp.X >= ar.X)
323                 rp.X = ar.X - 1;
324
325         if (rp.Y < route_y_min)
326                 rp.Y = route_y_min;
327         else if (rp.Y >= route_y_max)
328                 rp.Y = route_y_max - 1;
329
330         if (rp.Z < 0)
331                 rp.Z = 0;
332         else if (rp.Z >= ar.Z)
333                 rp.Z = ar.Z - 1;
334
335         vec = rp - orp;
336
337         float veclen = vec.getLength();
338         if (veclen < 0.05f)
339                 veclen = 1.0f;
340
341         // Every second section is rough
342         bool randomize_xz = (ps->range(1, 2) == 1);
343
344         // Carve routes
345         for (float f = 0.f; f < 1.0f; f += 1.0f / veclen)
346                 carveRoute(vec, f, randomize_xz);
347
348         orp = rp;
349 }
350
351
352 void CavesRandomWalk::carveRoute(v3f vec, float f, bool randomize_xz)
353 {
354         MapNode airnode(CONTENT_AIR);
355         MapNode waternode(c_water_source);
356         MapNode lavanode(c_lava_source);
357
358         v3s16 startp(orp.X, orp.Y, orp.Z);
359         startp += of;
360
361         float nval = NoisePerlin3D(np_caveliquids, startp.X,
362                 startp.Y, startp.Z, seed);
363         MapNode liquidnode = (nval < 0.40f && node_max.Y < lava_depth) ?
364                 lavanode : waternode;
365
366         v3f fp = orp + vec * f;
367         fp.X += 0.1f * ps->range(-10, 10);
368         fp.Z += 0.1f * ps->range(-10, 10);
369         v3s16 cp(fp.X, fp.Y, fp.Z);
370
371         s16 d0 = -rs / 2;
372         s16 d1 = d0 + rs;
373         if (randomize_xz) {
374                 d0 += ps->range(-1, 1);
375                 d1 += ps->range(-1, 1);
376         }
377
378         bool flat_cave_floor = !large_cave && ps->range(0, 2) == 2;
379
380         for (s16 z0 = d0; z0 <= d1; z0++) {
381                 s16 si = rs / 2 - MYMAX(0, abs(z0) - rs / 7 - 1);
382                 for (s16 x0 = -si - ps->range(0,1); x0 <= si - 1 + ps->range(0,1); x0++) {
383                         s16 maxabsxz = MYMAX(abs(x0), abs(z0));
384
385                         s16 si2 = rs / 2 - MYMAX(0, maxabsxz - rs / 7 - 1);
386
387                         for (s16 y0 = -si2; y0 <= si2; y0++) {
388                                 // Make better floors in small caves
389                                 if (flat_cave_floor && y0 <= -rs / 2 && rs <= 7)
390                                         continue;
391
392                                 if (large_cave_is_flat) {
393                                         // Make large caves not so tall
394                                         if (rs > 7 && abs(y0) >= rs / 3)
395                                                 continue;
396                                 }
397
398                                 v3s16 p(cp.X + x0, cp.Y + y0, cp.Z + z0);
399                                 p += of;
400
401                                 if (vm->m_area.contains(p) == false)
402                                         continue;
403
404                                 u32 i = vm->m_area.index(p);
405                                 content_t c = vm->m_data[i].getContent();
406                                 if (!ndef->get(c).is_ground_content)
407                                         continue;
408
409                                 if (large_cave) {
410                                         int full_ymin = node_min.Y - MAP_BLOCKSIZE;
411                                         int full_ymax = node_max.Y + MAP_BLOCKSIZE;
412
413                                         if (flooded && full_ymin < water_level && full_ymax > water_level)
414                                                 vm->m_data[i] = (p.Y <= water_level) ? waternode : airnode;
415                                         else if (flooded && full_ymax < water_level)
416                                                 vm->m_data[i] = (p.Y < startp.Y - 4) ? liquidnode : airnode;
417                                         else
418                                                 vm->m_data[i] = airnode;
419                                 } else {
420                                         if (c == CONTENT_IGNORE)
421                                                 continue;
422
423                                         vm->m_data[i] = airnode;
424                                         vm->m_flags[i] |= VMANIP_FLAG_CAVE;
425                                 }
426                         }
427                 }
428         }
429 }
430
431
432 inline bool CavesRandomWalk::isPosAboveSurface(v3s16 p)
433 {
434         if (heightmap != NULL &&
435                         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) * ystride + (p.X - node_min.X);
438                 if (heightmap[index] < p.Y)
439                         return true;
440         } else if (p.Y > water_level) {
441                 return true;
442         }
443
444         return false;
445 }
446
447
448 ////
449 //// CavesV6
450 ////
451
452 CavesV6::CavesV6(INodeDefManager *ndef, GenerateNotifier *gennotify,
453         int water_level, content_t water_source, content_t lava_source)
454 {
455         assert(ndef);
456
457         this->ndef        = ndef;
458         this->gennotify   = gennotify;
459         this->water_level = water_level;
460
461         c_water_source = water_source;
462         if (c_water_source == CONTENT_IGNORE)
463                 c_water_source = ndef->getId("mapgen_water_source");
464         if (c_water_source == CONTENT_IGNORE)
465                 c_water_source = CONTENT_AIR;
466
467         c_lava_source = lava_source;
468         if (c_lava_source == CONTENT_IGNORE)
469                 c_lava_source = ndef->getId("mapgen_lava_source");
470         if (c_lava_source == CONTENT_IGNORE)
471                 c_lava_source = CONTENT_AIR;
472 }
473
474
475 void CavesV6::makeCave(MMVManip *vm, v3s16 nmin, v3s16 nmax,
476         PseudoRandom *ps, PseudoRandom *ps2,
477         bool is_large_cave, int max_stone_height, s16 *heightmap)
478 {
479         assert(vm);
480         assert(ps);
481         assert(ps2);
482
483         this->vm         = vm;
484         this->ps         = ps;
485         this->ps2        = ps2;
486         this->node_min   = nmin;
487         this->node_max   = nmax;
488         this->heightmap  = heightmap;
489         this->large_cave = is_large_cave;
490
491         this->ystride = nmax.X - nmin.X + 1;
492
493         // Set initial parameters from randomness
494         min_tunnel_diameter = 2;
495         max_tunnel_diameter = ps->range(2, 6);
496         int dswitchint      = ps->range(1, 14);
497         if (large_cave) {
498                 part_max_length_rs  = ps->range(2, 4);
499                 tunnel_routepoints  = ps->range(5, ps->range(15, 30));
500                 min_tunnel_diameter = 5;
501                 max_tunnel_diameter = ps->range(7, ps->range(8, 24));
502         } else {
503                 part_max_length_rs = ps->range(2, 9);
504                 tunnel_routepoints = ps->range(10, ps->range(15, 30));
505         }
506         large_cave_is_flat = (ps->range(0, 1) == 0);
507
508         main_direction = v3f(0, 0, 0);
509
510         // Allowed route area size in nodes
511         ar = node_max - node_min + v3s16(1, 1, 1);
512         // Area starting point in nodes
513         of = node_min;
514
515         // Allow a bit more
516         //(this should be more than the maximum radius of the tunnel)
517         const s16 max_spread_amount = MAP_BLOCKSIZE;
518         const s16 insure = 10;
519         s16 more = MYMAX(max_spread_amount - max_tunnel_diameter / 2 - insure, 1);
520         ar += v3s16(1, 0, 1) * more * 2;
521         of -= v3s16(1, 0, 1) * more;
522
523         route_y_min = 0;
524         // Allow half a diameter + 7 over stone surface
525         route_y_max = -of.Y + max_stone_height + max_tunnel_diameter / 2 + 7;
526
527         // Limit maximum to area
528         route_y_max = rangelim(route_y_max, 0, ar.Y - 1);
529
530         if (large_cave) {
531                 s16 minpos = 0;
532                 if (node_min.Y < water_level && node_max.Y > water_level) {
533                         minpos = water_level - max_tunnel_diameter / 3 - of.Y;
534                         route_y_max = water_level + max_tunnel_diameter / 3 - of.Y;
535                 }
536                 route_y_min = ps->range(minpos, minpos + max_tunnel_diameter);
537                 route_y_min = rangelim(route_y_min, 0, route_y_max);
538         }
539
540         s16 route_start_y_min = route_y_min;
541         s16 route_start_y_max = route_y_max;
542
543         route_start_y_min = rangelim(route_start_y_min, 0, ar.Y - 1);
544         route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y - 1);
545
546         // Randomize starting position
547         orp.Z = (float)(ps->next() % ar.Z) + 0.5f;
548         orp.Y = (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5f;
549         orp.X = (float)(ps->next() % ar.X) + 0.5f;
550
551         // Add generation notify begin event
552         if (gennotify != NULL) {
553                 v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
554                 GenNotifyType notifytype = large_cave ?
555                         GENNOTIFY_LARGECAVE_BEGIN : GENNOTIFY_CAVE_BEGIN;
556                 gennotify->addEvent(notifytype, abs_pos);
557         }
558
559         // Generate some tunnel starting from orp
560         for (u16 j = 0; j < tunnel_routepoints; j++)
561                 makeTunnel(j % dswitchint == 0);
562
563         // Add generation notify end event
564         if (gennotify != NULL) {
565                 v3s16 abs_pos(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z);
566                 GenNotifyType notifytype = large_cave ?
567                         GENNOTIFY_LARGECAVE_END : GENNOTIFY_CAVE_END;
568                 gennotify->addEvent(notifytype, abs_pos);
569         }
570 }
571
572
573 void CavesV6::makeTunnel(bool dirswitch)
574 {
575         if (dirswitch && !large_cave) {
576                 main_direction.Z = ((float)(ps->next() % 20) - (float)10) / 10;
577                 main_direction.Y = ((float)(ps->next() % 20) - (float)10) / 30;
578                 main_direction.X = ((float)(ps->next() % 20) - (float)10) / 10;
579
580                 main_direction *= (float)ps->range(0, 10) / 10;
581         }
582
583         // Randomize size
584         s16 min_d = min_tunnel_diameter;
585         s16 max_d = max_tunnel_diameter;
586         rs = ps->range(min_d, max_d);
587         s16 rs_part_max_length_rs = rs * part_max_length_rs;
588
589         v3s16 maxlen;
590         if (large_cave) {
591                 maxlen = v3s16(
592                         rs_part_max_length_rs,
593                         rs_part_max_length_rs / 2,
594                         rs_part_max_length_rs
595                 );
596         } else {
597                 maxlen = v3s16(
598                         rs_part_max_length_rs,
599                         ps->range(1, rs_part_max_length_rs),
600                         rs_part_max_length_rs
601                 );
602         }
603
604         v3f vec;
605         vec.Z = (float)(ps->next() % maxlen.Z) - (float)maxlen.Z / 2;
606         vec.Y = (float)(ps->next() % maxlen.Y) - (float)maxlen.Y / 2;
607         vec.X = (float)(ps->next() % maxlen.X) - (float)maxlen.X / 2;
608
609         // Jump downward sometimes
610         if (!large_cave && ps->range(0, 12) == 0) {
611                 vec.Z = (float)(ps->next() % maxlen.Z) - (float)maxlen.Z / 2;
612                 vec.Y = (float)(ps->next() % (maxlen.Y * 2)) - (float)maxlen.Y;
613                 vec.X = (float)(ps->next() % maxlen.X) - (float)maxlen.X / 2;
614         }
615
616         // Do not make caves that are entirely above ground, to fix shadow bugs
617         // caused by overgenerated large caves.
618         // It is only necessary to check the startpoint and endpoint.
619         v3s16 p1 = v3s16(orp.X, orp.Y, orp.Z) + of + rs / 2;
620         v3s16 p2 = v3s16(vec.X, vec.Y, vec.Z) + p1;
621
622         // If startpoint and endpoint are above ground, disable placement of nodes
623         // in carveRoute while still running all PseudoRandom calls to ensure caves
624         // are consistent with existing worlds.
625         bool tunnel_above_ground =
626                 p1.Y > getSurfaceFromHeightmap(p1) &&
627                 p2.Y > getSurfaceFromHeightmap(p2);
628
629         vec += main_direction;
630
631         v3f rp = orp + vec;
632         if (rp.X < 0)
633                 rp.X = 0;
634         else if (rp.X >= ar.X)
635                 rp.X = ar.X - 1;
636
637         if (rp.Y < route_y_min)
638                 rp.Y = route_y_min;
639         else if (rp.Y >= route_y_max)
640                 rp.Y = route_y_max - 1;
641
642         if (rp.Z < 0)
643                 rp.Z = 0;
644         else if (rp.Z >= ar.Z)
645                 rp.Z = ar.Z - 1;
646
647         vec = rp - orp;
648
649         float veclen = vec.getLength();
650         // As odd as it sounds, veclen is *exactly* 0.0 sometimes, causing a FPE
651         if (veclen < 0.05f)
652                 veclen = 1.0f;
653
654         // Every second section is rough
655         bool randomize_xz = (ps2->range(1, 2) == 1);
656
657         // Carve routes
658         for (float f = 0.f; f < 1.0f; f += 1.0f / veclen)
659                 carveRoute(vec, f, randomize_xz, tunnel_above_ground);
660
661         orp = rp;
662 }
663
664
665 void CavesV6::carveRoute(v3f vec, float f, bool randomize_xz,
666         bool tunnel_above_ground)
667 {
668         MapNode airnode(CONTENT_AIR);
669         MapNode waternode(c_water_source);
670         MapNode lavanode(c_lava_source);
671
672         v3s16 startp(orp.X, orp.Y, orp.Z);
673         startp += of;
674
675         v3f fp = orp + vec * f;
676         fp.X += 0.1f * ps->range(-10, 10);
677         fp.Z += 0.1f * ps->range(-10, 10);
678         v3s16 cp(fp.X, fp.Y, fp.Z);
679
680         s16 d0 = -rs / 2;
681         s16 d1 = d0 + rs;
682         if (randomize_xz) {
683                 d0 += ps->range(-1, 1);
684                 d1 += ps->range(-1, 1);
685         }
686
687         for (s16 z0 = d0; z0 <= d1; z0++) {
688                 s16 si = rs / 2 - MYMAX(0, abs(z0) - rs / 7 - 1);
689                 for (s16 x0 = -si - ps->range(0,1); x0 <= si - 1 + ps->range(0,1); x0++) {
690                         if (tunnel_above_ground)
691                                 continue;
692
693                         s16 maxabsxz = MYMAX(abs(x0), abs(z0));
694                         s16 si2 = rs / 2 - MYMAX(0, maxabsxz - rs / 7 - 1);
695                         for (s16 y0 = -si2; y0 <= si2; y0++) {
696                                 if (large_cave_is_flat) {
697                                         // Make large caves not so tall
698                                         if (rs > 7 && abs(y0) >= rs / 3)
699                                                 continue;
700                                 }
701
702                                 v3s16 p(cp.X + x0, cp.Y + y0, cp.Z + z0);
703                                 p += of;
704
705                                 if (vm->m_area.contains(p) == false)
706                                         continue;
707
708                                 u32 i = vm->m_area.index(p);
709                                 content_t c = vm->m_data[i].getContent();
710                                 if (!ndef->get(c).is_ground_content)
711                                         continue;
712
713                                 if (large_cave) {
714                                         int full_ymin = node_min.Y - MAP_BLOCKSIZE;
715                                         int full_ymax = node_max.Y + MAP_BLOCKSIZE;
716
717                                         if (full_ymin < water_level && full_ymax > water_level) {
718                                                 vm->m_data[i] = (p.Y <= water_level) ? waternode : airnode;
719                                         } else if (full_ymax < water_level) {
720                                                 vm->m_data[i] = (p.Y < startp.Y - 2) ? lavanode : airnode;
721                                         } else {
722                                                 vm->m_data[i] = airnode;
723                                         }
724                                 } else {
725                                         if (c == CONTENT_IGNORE || c == CONTENT_AIR)
726                                                 continue;
727
728                                         vm->m_data[i] = airnode;
729                                         vm->m_flags[i] |= VMANIP_FLAG_CAVE;
730                                 }
731                         }
732                 }
733         }
734 }
735
736
737 inline s16 CavesV6::getSurfaceFromHeightmap(v3s16 p)
738 {
739         if (heightmap != NULL &&
740                         p.Z >= node_min.Z && p.Z <= node_max.Z &&
741                         p.X >= node_min.X && p.X <= node_max.X) {
742                 u32 index = (p.Z - node_min.Z) * ystride + (p.X - node_min.X);
743                 return heightmap[index];
744         } else {
745                 return water_level;
746         }
747 }