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