]> git.lizzy.rs Git - minetest.git/blob - src/cavegen.cpp
Use narrow_to_wide in gettext instead of os dependent conversion fct
[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_v6.h"
24 #include "mapgen_v7.h"
25 #include "cavegen.h"
26
27 NoiseParams nparams_caveliquids(0, 1, v3f(150.0, 150.0, 150.0), 776, 3, 0.6);
28
29
30 ///////////////////////////////////////////////////////////////////////////////
31
32
33 CaveV6::CaveV6(MapgenV6 *mg, PseudoRandom *ps, PseudoRandom *ps2, bool is_large_cave) {
34         this->mg   = mg;
35         this->vm   = mg->vm;
36         this->ndef = mg->ndef;
37         this->water_level = mg->water_level;
38         this->large_cave = is_large_cave;
39         this->ps  = ps;
40         this->ps2 = ps2;
41         this->c_water_source = mg->c_water_source;
42         this->c_lava_source  = mg->c_lava_source;
43
44         min_tunnel_diameter = 2;
45         max_tunnel_diameter = ps->range(2, 6);
46         dswitchint = ps->range(1, 14);
47         flooded = true;
48         
49         if (large_cave) {
50                 part_max_length_rs = ps->range(2,4);
51                 tunnel_routepoints = ps->range(5, ps->range(15,30));
52                 min_tunnel_diameter = 5;
53                 max_tunnel_diameter = ps->range(7, ps->range(8,24));
54         } else {
55                 part_max_length_rs = ps->range(2,9);
56                 tunnel_routepoints = ps->range(10, ps->range(15,30));
57         }
58         
59         large_cave_is_flat = (ps->range(0,1) == 0);
60 }
61
62
63 void CaveV6::makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height) {
64         node_min = nmin;
65         node_max = nmax;
66         max_stone_y = max_stone_height;
67         main_direction = v3f(0, 0, 0);
68
69         // Allowed route area size in nodes
70         ar = node_max - node_min + v3s16(1, 1, 1);
71         // Area starting point in nodes
72         of = node_min;
73
74         // Allow a bit more
75         //(this should be more than the maximum radius of the tunnel)
76         const s16 max_spread_amount = MAP_BLOCKSIZE;
77         s16 insure = 10;
78         s16 more = MYMAX(max_spread_amount - max_tunnel_diameter / 2 - insure, 1);
79         ar += v3s16(1,0,1) * more * 2;
80         of -= v3s16(1,0,1) * more;
81
82         route_y_min = 0;
83         // Allow half a diameter + 7 over stone surface
84         route_y_max = -of.Y + max_stone_y + max_tunnel_diameter / 2 + 7;
85
86         // Limit maximum to area
87         route_y_max = rangelim(route_y_max, 0, ar.Y - 1);
88
89         if (large_cave) {
90                 s16 min = 0;
91                 if (node_min.Y < water_level && node_max.Y > water_level) {
92                         min = water_level - max_tunnel_diameter/3 - of.Y;
93                         route_y_max = water_level + max_tunnel_diameter/3 - of.Y;
94                 }
95                 route_y_min = ps->range(min, min + max_tunnel_diameter);
96                 route_y_min = rangelim(route_y_min, 0, route_y_max);
97         }
98
99         s16 route_start_y_min = route_y_min;
100         s16 route_start_y_max = route_y_max;
101
102         route_start_y_min = rangelim(route_start_y_min, 0, ar.Y-1);
103         route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y-1);
104
105         // Randomize starting position
106         orp = v3f(
107                 (float)(ps->next() % ar.X) + 0.5,
108                 (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5,
109                 (float)(ps->next() % ar.Z) + 0.5
110         );
111
112         int notifytype = large_cave ? GENNOTIFY_LARGECAVE_BEGIN : GENNOTIFY_CAVE_BEGIN;
113         if (mg->gennotify & (1 << notifytype)) {
114                 std::vector <v3s16> *nvec = mg->gen_notifications[notifytype];
115                 nvec->push_back(v3s16(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z));
116         }
117
118         // Generate some tunnel starting from orp
119         for (u16 j = 0; j < tunnel_routepoints; j++)
120                 makeTunnel(j % dswitchint == 0);
121
122         notifytype = large_cave ? GENNOTIFY_LARGECAVE_END : GENNOTIFY_CAVE_END;
123         if (mg->gennotify & (1 << notifytype)) {
124                 std::vector <v3s16> *nvec = mg->gen_notifications[notifytype];
125                 nvec->push_back(v3s16(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z));
126         }
127 }
128
129
130 void CaveV6::makeTunnel(bool dirswitch) {
131         if (dirswitch && !large_cave) {
132                 main_direction = v3f(
133                         ((float)(ps->next() % 20) - (float)10) / 10,
134                         ((float)(ps->next() % 20) - (float)10) / 30,
135                         ((float)(ps->next() % 20) - (float)10) / 10
136                 );
137                 main_direction *= (float)ps->range(0, 10) / 10;
138         }
139
140         // Randomize size
141         s16 min_d = min_tunnel_diameter;
142         s16 max_d = max_tunnel_diameter;
143         rs = ps->range(min_d, max_d);
144
145         v3s16 maxlen;
146         if (large_cave) {
147                 maxlen = v3s16(
148                         rs * part_max_length_rs,
149                         rs * part_max_length_rs / 2,
150                         rs * part_max_length_rs
151                 );
152         } else {
153                 maxlen = v3s16(
154                         rs * part_max_length_rs,
155                         ps->range(1, rs * part_max_length_rs),
156                         rs * part_max_length_rs
157                 );
158         }
159
160         v3f vec(
161                 (float)(ps->next() % (maxlen.X * 1)) - (float)maxlen.X / 2,
162                 (float)(ps->next() % (maxlen.Y * 1)) - (float)maxlen.Y / 2,
163                 (float)(ps->next() % (maxlen.Z * 1)) - (float)maxlen.Z / 2
164         );
165
166         // Jump downward sometimes
167         if (!large_cave && ps->range(0, 12) == 0) {
168                 vec = v3f(
169                         (float)(ps->next() % (maxlen.X * 1)) - (float)maxlen.X / 2,
170                         (float)(ps->next() % (maxlen.Y * 2)) - (float)maxlen.Y,
171                         (float)(ps->next() % (maxlen.Z * 1)) - (float)maxlen.Z / 2
172                 );
173         }
174
175         vec += main_direction;
176
177         v3f rp = orp + vec;
178         if (rp.X < 0)
179                 rp.X = 0;
180         else if (rp.X >= ar.X)
181                 rp.X = ar.X - 1;
182         
183         if (rp.Y < route_y_min)
184                 rp.Y = route_y_min;
185         else if (rp.Y >= route_y_max)
186                 rp.Y = route_y_max - 1;
187         
188         if (rp.Z < 0)
189                 rp.Z = 0;
190         else if (rp.Z >= ar.Z)
191                 rp.Z = ar.Z - 1;
192         
193         vec = rp - orp;
194
195         float veclen = vec.getLength();
196         // As odd as it sounds, veclen is *exactly* 0.0 sometimes, causing a FPE
197         if (veclen < 0.05)
198                 veclen = 1.0;
199                 
200         // Every second section is rough
201         bool randomize_xz = (ps2->range(1, 2) == 1);
202
203         // Carve routes
204         for (float f = 0; f < 1.0; f += 1.0 / veclen)
205                 carveRoute(vec, f, randomize_xz);
206         
207         orp = rp;
208 }
209
210
211 void CaveV6::carveRoute(v3f vec, float f, bool randomize_xz) {
212         MapNode airnode(CONTENT_AIR);
213         MapNode waternode(c_water_source);
214         MapNode lavanode(c_lava_source);
215         
216         v3s16 startp(orp.X, orp.Y, orp.Z);
217         startp += of;
218         
219         v3f fp = orp + vec * f;
220         fp.X += 0.1 * ps->range(-10, 10);
221         fp.Z += 0.1 * ps->range(-10, 10);
222         v3s16 cp(fp.X, fp.Y, fp.Z);
223
224         s16 d0 = -rs/2;
225         s16 d1 = d0 + rs;
226         if (randomize_xz) {
227                 d0 += ps->range(-1, 1);
228                 d1 += ps->range(-1, 1);
229         }
230         
231         for (s16 z0 = d0; z0 <= d1; z0++) {
232                 s16 si = rs / 2 - MYMAX(0, abs(z0) - rs / 7 - 1);
233                 for (s16 x0 = -si - ps->range(0,1); x0 <= si - 1 + ps->range(0,1); x0++) {
234                         s16 maxabsxz = MYMAX(abs(x0), abs(z0));
235                         s16 si2 = rs / 2 - MYMAX(0, maxabsxz - rs / 7 - 1);
236                         for (s16 y0 = -si2; y0 <= si2; y0++) {                          
237                                 if (large_cave_is_flat) {
238                                         // Make large caves not so tall
239                                         if (rs > 7 && abs(y0) >= rs / 3)
240                                                 continue;
241                                 }
242
243                                 v3s16 p(cp.X + x0, cp.Y + y0, cp.Z + z0);
244                                 p += of;
245
246                                 if (vm->m_area.contains(p) == false)
247                                         continue;
248
249                                 u32 i = vm->m_area.index(p);
250                                 content_t c = vm->m_data[i].getContent();
251                                 if (!ndef->get(c).is_ground_content)
252                                         continue;
253
254                                 if (large_cave) {
255                                         int full_ymin = node_min.Y - MAP_BLOCKSIZE;
256                                         int full_ymax = node_max.Y + MAP_BLOCKSIZE;
257
258                                         if (flooded && full_ymin < water_level && full_ymax > water_level) {
259                                                 vm->m_data[i] = (p.Y <= water_level) ? waternode : airnode;
260                                         } else if (flooded && full_ymax < water_level) {
261                                                 vm->m_data[i] = (p.Y < startp.Y - 2) ? lavanode : airnode;
262                                         } else {
263                                                 vm->m_data[i] = airnode;
264                                         }
265                                 } else {
266                                         // Don't replace air or water or lava or ignore
267                                         if (c == CONTENT_IGNORE || c == CONTENT_AIR ||
268                                                 c == c_water_source || c == c_lava_source)
269                                                 continue;
270
271                                         vm->m_data[i] = airnode;
272                                         vm->m_flags[i] |= VMANIP_FLAG_CAVE;
273                                 }
274                         }
275                 }
276         }
277 }
278
279
280 ///////////////////////////////////////// Caves V7
281
282 CaveV7::CaveV7(MapgenV7 *mg, PseudoRandom *ps, bool is_large_cave) {
283         this->mg   = mg;
284         this->vm   = mg->vm;
285         this->ndef = mg->ndef;
286         this->water_level = mg->water_level;
287         this->large_cave  = is_large_cave;
288         this->ps = ps;
289         this->c_water_source = mg->c_water_source;
290         this->c_lava_source  = mg->c_lava_source;
291         this->c_ice          = mg->c_ice;
292         this->np_caveliquids = &nparams_caveliquids;
293
294         dswitchint = ps->range(1, 14);
295         flooded    = ps->range(1, 2) == 2;
296         
297         if (large_cave) {
298                 part_max_length_rs = ps->range(2, 4);
299                 tunnel_routepoints = ps->range(5, ps->range(15, 30));
300                 min_tunnel_diameter = 5;
301                 max_tunnel_diameter = ps->range(7, ps->range(8, 24));
302         } else {
303                 part_max_length_rs = ps->range(2, 9);
304                 tunnel_routepoints = ps->range(10, ps->range(15, 30));
305                 min_tunnel_diameter = 2;
306                 max_tunnel_diameter = ps->range(2, 6);
307         }
308         
309         large_cave_is_flat = (ps->range(0, 1) == 0);
310 }
311
312
313 void CaveV7::makeCave(v3s16 nmin, v3s16 nmax, int max_stone_height) {
314         node_min = nmin;
315         node_max = nmax;
316         max_stone_y = max_stone_height;
317         main_direction = v3f(0, 0, 0);
318
319         // Allowed route area size in nodes
320         ar = node_max - node_min + v3s16(1, 1, 1);
321         // Area starting point in nodes
322         of = node_min;
323
324         // Allow a bit more
325         //(this should be more than the maximum radius of the tunnel)
326         s16 insure = 10;
327         s16 more = MYMAX(MAP_BLOCKSIZE - max_tunnel_diameter / 2 - insure, 1);
328         ar += v3s16(1,0,1) * more * 2;
329         of -= v3s16(1,0,1) * more;
330
331         route_y_min = 0;
332         // Allow half a diameter + 7 over stone surface
333         route_y_max = -of.Y + max_stone_y + max_tunnel_diameter / 2 + 7;
334
335         // Limit maximum to area
336         route_y_max = rangelim(route_y_max, 0, ar.Y - 1);
337
338         if (large_cave) {
339                 s16 min = 0;
340                 if (node_min.Y < water_level && node_max.Y > water_level) {
341                         min = water_level - max_tunnel_diameter/3 - of.Y;
342                         route_y_max = water_level + max_tunnel_diameter/3 - of.Y;
343                 }
344                 route_y_min = ps->range(min, min + max_tunnel_diameter);
345                 route_y_min = rangelim(route_y_min, 0, route_y_max);
346         }
347
348         s16 route_start_y_min = route_y_min;
349         s16 route_start_y_max = route_y_max;
350
351         route_start_y_min = rangelim(route_start_y_min, 0, ar.Y - 1);
352         route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y - 1);
353
354         // Randomize starting position
355         orp = v3f(
356                 (float)(ps->next() % ar.X) + 0.5,
357                 (float)(ps->range(route_start_y_min, route_start_y_max)) + 0.5,
358                 (float)(ps->next() % ar.Z) + 0.5
359         );
360
361         int notifytype = large_cave ? GENNOTIFY_LARGECAVE_BEGIN : GENNOTIFY_CAVE_BEGIN;
362         if (mg->gennotify & (1 << notifytype)) {
363                 std::vector <v3s16> *nvec = mg->gen_notifications[notifytype];
364                 nvec->push_back(v3s16(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z));
365         }
366
367         // Generate some tunnel starting from orp
368         for (u16 j = 0; j < tunnel_routepoints; j++)
369                 makeTunnel(j % dswitchint == 0);
370
371         notifytype = large_cave ? GENNOTIFY_LARGECAVE_END : GENNOTIFY_CAVE_END;
372         if (mg->gennotify & (1 << notifytype)) {
373                 std::vector <v3s16> *nvec = mg->gen_notifications[notifytype];
374                 nvec->push_back(v3s16(of.X + orp.X, of.Y + orp.Y, of.Z + orp.Z));
375         }
376 }
377
378
379 void CaveV7::makeTunnel(bool dirswitch) {
380         if (dirswitch && !large_cave) {
381                 main_direction = v3f(
382                         ((float)(ps->next() % 20) - (float)10) / 10,
383                         ((float)(ps->next() % 20) - (float)10) / 30,
384                         ((float)(ps->next() % 20) - (float)10) / 10
385                 );
386                 main_direction *= (float)ps->range(0, 10) / 10;
387         }
388
389         // Randomize size
390         s16 min_d = min_tunnel_diameter;
391         s16 max_d = max_tunnel_diameter;
392         rs = ps->range(min_d, max_d);
393
394         v3s16 maxlen;
395         if (large_cave) {
396                 maxlen = v3s16(
397                         rs * part_max_length_rs,
398                         rs * part_max_length_rs / 2,
399                         rs * part_max_length_rs
400                 );
401         } else {
402                 maxlen = v3s16(
403                         rs * part_max_length_rs,
404                         ps->range(1, rs * part_max_length_rs),
405                         rs * part_max_length_rs
406                 );
407         }
408
409         v3f vec;
410         // Jump downward sometimes
411         if (!large_cave && ps->range(0, 12) == 0) {
412                 vec = v3f(
413                         (float)(ps->next() % (maxlen.X * 1)) - (float)maxlen.X / 2,
414                         (float)(ps->next() % (maxlen.Y * 2)) - (float)maxlen.Y,
415                         (float)(ps->next() % (maxlen.Z * 1)) - (float)maxlen.Z / 2
416                 );
417         } else {
418                 vec = v3f(
419                         (float)(ps->next() % (maxlen.X * 1)) - (float)maxlen.X / 2,
420                         (float)(ps->next() % (maxlen.Y * 1)) - (float)maxlen.Y / 2,
421                         (float)(ps->next() % (maxlen.Z * 1)) - (float)maxlen.Z / 2
422                 );
423         }
424
425         // Do not make large caves that are above ground.
426         // It is only necessary to check the startpoint and endpoint.
427         if (large_cave) {
428                 v3s16 orpi(orp.X, orp.Y, orp.Z);
429                 v3s16 veci(vec.X, vec.Y, vec.Z);
430                 v3s16 p;
431                 
432                 p = orpi + veci + of + rs / 2;
433                 if (p.Z >= node_min.Z && p.Z <= node_max.Z &&
434                         p.X >= node_min.X && p.X <= node_max.X) {
435                         u32 index = (p.Z - node_min.Z) * mg->ystride + (p.X - node_min.X);
436                         s16 h = mg->ridge_heightmap[index];
437                         if (h < p.Y)
438                                 return;
439                 } else if (p.Y > water_level) {
440                         return; // If it's not in our heightmap, use a simple heuristic
441                 }
442                 
443                 p = orpi + of + rs / 2;
444                 if (p.Z >= node_min.Z && p.Z <= node_max.Z &&
445                         p.X >= node_min.X && p.X <= node_max.X) {
446                         u32 index = (p.Z - node_min.Z) * mg->ystride + (p.X - node_min.X);
447                         s16 h = mg->ridge_heightmap[index];
448                         if (h < p.Y)
449                                 return;
450                 } else if (p.Y > water_level) { 
451                         return;
452                 }
453         }
454
455         vec += main_direction;
456
457         v3f rp = orp + vec;
458         if (rp.X < 0)
459                 rp.X = 0;
460         else if (rp.X >= ar.X)
461                 rp.X = ar.X - 1;
462         
463         if (rp.Y < route_y_min)
464                 rp.Y = route_y_min;
465         else if (rp.Y >= route_y_max)
466                 rp.Y = route_y_max - 1;
467         
468         if (rp.Z < 0)
469                 rp.Z = 0;
470         else if (rp.Z >= ar.Z)
471                 rp.Z = ar.Z - 1;
472         
473         vec = rp - orp;
474
475         float veclen = vec.getLength();
476         if (veclen < 0.05)
477                 veclen = 1.0;
478                 
479         // Every second section is rough
480         bool randomize_xz = (ps->range(1, 2) == 1);
481
482         // Make a ravine every once in a while if it's long enough
483         //float xylen = vec.X * vec.X + vec.Z * vec.Z;
484         //disable ravines for now
485         bool is_ravine = false; //(xylen > 500.0) && !large_cave && (ps->range(1, 8) == 1);
486
487         // Carve routes
488         for (float f = 0; f < 1.0; f += 1.0 / veclen)
489                 carveRoute(vec, f, randomize_xz, is_ravine);
490         
491         orp = rp;
492 }
493
494
495 void CaveV7::carveRoute(v3f vec, float f, bool randomize_xz, bool is_ravine) {
496         MapNode airnode(CONTENT_AIR);
497         MapNode waternode(c_water_source);
498         MapNode lavanode(c_lava_source);
499         
500         v3s16 startp(orp.X, orp.Y, orp.Z);
501         startp += of;
502         
503         float nval = NoisePerlin3D(np_caveliquids, startp.X,
504                                                         startp.Y, startp.Z, mg->seed);
505         MapNode liquidnode = nval < 0.40 ? lavanode : waternode;
506         
507         v3f fp = orp + vec * f;
508         fp.X += 0.1 * ps->range(-10, 10);
509         fp.Z += 0.1 * ps->range(-10, 10);
510         v3s16 cp(fp.X, fp.Y, fp.Z);
511
512         s16 d0 = -rs/2;
513         s16 d1 = d0 + rs;
514         if (randomize_xz) {
515                 d0 += ps->range(-1, 1);
516                 d1 += ps->range(-1, 1);
517         }
518         
519         bool flat_cave_floor = !large_cave && ps->range(0, 2) == 2;
520         bool should_make_cave_hole = ps->range(1, 10) == 1;
521         
522         for (s16 z0 = d0; z0 <= d1; z0++) {
523                 s16 si = rs / 2 - MYMAX(0, abs(z0) - rs / 7 - 1);
524                 for (s16 x0 = -si - ps->range(0,1); x0 <= si - 1 + ps->range(0,1); x0++) {
525                         s16 maxabsxz = MYMAX(abs(x0), abs(z0));
526         
527                         s16 si2 = is_ravine ? MYMIN(ps->range(25, 26), ar.Y) :
528                                                                  rs / 2 - MYMAX(0, maxabsxz - rs / 7 - 1);
529                         
530                         for (s16 y0 = -si2; y0 <= si2; y0++) {
531                                 // Make better floors in small caves
532                                 if(flat_cave_floor && y0 <= -rs/2 && rs<=7)
533                                         continue;
534                                 
535                                 if (large_cave_is_flat) {
536                                         // Make large caves not so tall
537                                         if (rs > 7 && abs(y0) >= rs / 3)
538                                                 continue;
539                                 }
540
541                                 v3s16 p(cp.X + x0, cp.Y + y0, cp.Z + z0);
542                                 p += of;
543                                 
544                                 if (!is_ravine && mg->heightmap && should_make_cave_hole &&
545                                         p.X <= node_max.X && p.Z <= node_max.Z) {
546                                         int maplen = node_max.X - node_min.X + 1;
547                                         int idx = (p.Z - node_min.Z) * maplen + (p.X - node_min.X);
548                                         if (p.Y >= mg->heightmap[idx] - 2)
549                                                 continue;
550                                 }
551
552                                 if (vm->m_area.contains(p) == false)
553                                         continue;
554
555                                 u32 i = vm->m_area.index(p);
556                                 
557                                 // Don't replace air, water, lava, or ice
558                                 content_t c = vm->m_data[i].getContent();
559                                 if (!ndef->get(c).is_ground_content || c == CONTENT_AIR ||
560                                         c == c_water_source || c == c_lava_source || c == c_ice)
561                                         continue;
562                                         
563                                 if (large_cave) {
564                                         int full_ymin = node_min.Y - MAP_BLOCKSIZE;
565                                         int full_ymax = node_max.Y + MAP_BLOCKSIZE;
566
567                                         if (flooded && full_ymin < water_level && full_ymax > water_level)
568                                                 vm->m_data[i] = (p.Y <= water_level) ? waternode : airnode;
569                                         else if (flooded && full_ymax < water_level)
570                                                 vm->m_data[i] = (p.Y < startp.Y - 4) ? liquidnode : airnode;
571                                         else
572                                                 vm->m_data[i] = airnode;
573                                 } else {
574                                         if (c == CONTENT_IGNORE)
575                                                 continue;
576                                         
577                                         vm->m_data[i] = airnode;
578                                         vm->m_flags[i] |= VMANIP_FLAG_CAVE;
579                                 }
580                         }
581                 }
582         }
583 }