3 Copyright (C) 2010-2015 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
4 Copyright (C) 2010-2015 celeron55, Perttu Ahola <celeron55@gmail.com>
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
29 #include "content_sao.h"
32 #include "voxelalgorithms.h"
37 #include "serialization.h"
38 #include "util/serialize.h"
39 #include "util/numeric.h"
42 #include "mapgen_flat.h"
43 #include "mapgen_fractal.h"
44 #include "mapgen_v5.h"
45 #include "mapgen_v6.h"
46 #include "mapgen_v7.h"
47 #include "mapgen_valleys.h"
48 #include "mapgen_singlenode.h"
50 #include "dungeongen.h"
52 FlagDesc flagdesc_mapgen[] = {
54 {"dungeons", MG_DUNGEONS},
56 {"decorations", MG_DECORATIONS},
60 FlagDesc flagdesc_gennotify[] = {
61 {"dungeon", 1 << GENNOTIFY_DUNGEON},
62 {"temple", 1 << GENNOTIFY_TEMPLE},
63 {"cave_begin", 1 << GENNOTIFY_CAVE_BEGIN},
64 {"cave_end", 1 << GENNOTIFY_CAVE_END},
65 {"large_cave_begin", 1 << GENNOTIFY_LARGECAVE_BEGIN},
66 {"large_cave_end", 1 << GENNOTIFY_LARGECAVE_END},
67 {"decoration", 1 << GENNOTIFY_DECORATION},
80 static MapgenDesc g_reg_mapgens[] = {
87 {"singlenode", false},
91 ARRLEN(g_reg_mapgens) == MAPGEN_INVALID,
92 registered_mapgens_is_wrong_size);
115 Mapgen::Mapgen(int mapgenid, MapgenParams *params, EmergeManager *emerge) :
116 gennotify(emerge->gen_notify_on, &emerge->gen_notify_on_deco_ids)
120 water_level = params->water_level;
121 mapgen_limit = params->mapgen_limit;
122 flags = params->flags;
123 csize = v3s16(1, 1, 1) * (params->chunksize * MAP_BLOCKSIZE);
126 We are losing half our entropy by doing this, but it is necessary to
127 preserve reverse compatibility. If the top half of our current 64 bit
128 seeds ever starts getting used, existing worlds will break due to a
129 different hash outcome and no way to differentiate between versions.
131 A solution could be to add a new bit to designate that the top half of
132 the seed value should be used, essentially a 1-bit version code, but
133 this would require increasing the total size of a seed to 9 bytes (yuck)
135 It's probably okay if this never gets fixed. 4.2 billion possibilities
136 ought to be enough for anyone.
138 seed = (s32)params->seed;
153 MapgenType Mapgen::getMapgenType(const std::string &mgname)
155 for (size_t i = 0; i != ARRLEN(g_reg_mapgens); i++) {
156 if (mgname == g_reg_mapgens[i].name)
157 return (MapgenType)i;
160 return MAPGEN_INVALID;
164 const char *Mapgen::getMapgenName(MapgenType mgtype)
166 size_t index = (size_t)mgtype;
167 if (index == MAPGEN_INVALID || index >= ARRLEN(g_reg_mapgens))
170 return g_reg_mapgens[index].name;
174 Mapgen *Mapgen::createMapgen(MapgenType mgtype, int mgid,
175 MapgenParams *params, EmergeManager *emerge)
179 return new MapgenFlat(mgid, (MapgenFlatParams *)params, emerge);
181 return new MapgenFractal(mgid, (MapgenFractalParams *)params, emerge);
182 case MAPGEN_SINGLENODE:
183 return new MapgenSinglenode(mgid, (MapgenSinglenodeParams *)params, emerge);
185 return new MapgenV5(mgid, (MapgenV5Params *)params, emerge);
187 return new MapgenV6(mgid, (MapgenV6Params *)params, emerge);
189 return new MapgenV7(mgid, (MapgenV7Params *)params, emerge);
191 return new MapgenValleys(mgid, (MapgenValleysParams *)params, emerge);
198 MapgenParams *Mapgen::createMapgenParams(MapgenType mgtype)
202 return new MapgenFlatParams;
204 return new MapgenFractalParams;
205 case MAPGEN_SINGLENODE:
206 return new MapgenSinglenodeParams;
208 return new MapgenV5Params;
210 return new MapgenV6Params;
212 return new MapgenV7Params;
214 return new MapgenValleysParams;
221 void Mapgen::getMapgenNames(std::vector<const char *> *mgnames, bool include_hidden)
223 for (u32 i = 0; i != ARRLEN(g_reg_mapgens); i++) {
224 if (include_hidden || g_reg_mapgens[i].is_user_visible)
225 mgnames->push_back(g_reg_mapgens[i].name);
230 u32 Mapgen::getBlockSeed(v3s16 p, s32 seed)
239 u32 Mapgen::getBlockSeed2(v3s16 p, s32 seed)
241 u32 n = 1619 * p.X + 31337 * p.Y + 52591 * p.Z + 1013 * seed;
243 return (n * (n * n * 60493 + 19990303) + 1376312589);
247 // Returns Y one under area minimum if not found
248 s16 Mapgen::findGroundLevelFull(v2s16 p2d)
250 v3s16 em = vm->m_area.getExtent();
251 s16 y_nodes_max = vm->m_area.MaxEdge.Y;
252 s16 y_nodes_min = vm->m_area.MinEdge.Y;
253 u32 i = vm->m_area.index(p2d.X, y_nodes_max, p2d.Y);
256 for (y = y_nodes_max; y >= y_nodes_min; y--) {
257 MapNode &n = vm->m_data[i];
258 if (ndef->get(n).walkable)
261 vm->m_area.add_y(em, i, -1);
263 return (y >= y_nodes_min) ? y : y_nodes_min - 1;
267 // Returns -MAX_MAP_GENERATION_LIMIT if not found
268 s16 Mapgen::findGroundLevel(v2s16 p2d, s16 ymin, s16 ymax)
270 v3s16 em = vm->m_area.getExtent();
271 u32 i = vm->m_area.index(p2d.X, ymax, p2d.Y);
274 for (y = ymax; y >= ymin; y--) {
275 MapNode &n = vm->m_data[i];
276 if (ndef->get(n).walkable)
279 vm->m_area.add_y(em, i, -1);
281 return (y >= ymin) ? y : -MAX_MAP_GENERATION_LIMIT;
285 // Returns -MAX_MAP_GENERATION_LIMIT if not found or if ground is found first
286 s16 Mapgen::findLiquidSurface(v2s16 p2d, s16 ymin, s16 ymax)
288 v3s16 em = vm->m_area.getExtent();
289 u32 i = vm->m_area.index(p2d.X, ymax, p2d.Y);
292 for (y = ymax; y >= ymin; y--) {
293 MapNode &n = vm->m_data[i];
294 if (ndef->get(n).walkable)
295 return -MAX_MAP_GENERATION_LIMIT;
296 else if (ndef->get(n).isLiquid())
299 vm->m_area.add_y(em, i, -1);
301 return (y >= ymin) ? y : -MAX_MAP_GENERATION_LIMIT;
305 void Mapgen::updateHeightmap(v3s16 nmin, v3s16 nmax)
310 //TimeTaker t("Mapgen::updateHeightmap", NULL, PRECISION_MICRO);
312 for (s16 z = nmin.Z; z <= nmax.Z; z++) {
313 for (s16 x = nmin.X; x <= nmax.X; x++, index++) {
314 s16 y = findGroundLevel(v2s16(x, z), nmin.Y, nmax.Y);
316 heightmap[index] = y;
319 //printf("updateHeightmap: %dus\n", t.stop());
322 inline bool Mapgen::isLiquidHorizontallyFlowable(u32 vi, v3s16 em)
325 vm->m_area.add_x(em, vi_neg_x, -1);
326 if (vm->m_data[vi_neg_x].getContent() != CONTENT_IGNORE) {
327 const ContentFeatures &c_nx = ndef->get(vm->m_data[vi_neg_x]);
328 if (c_nx.floodable && !c_nx.isLiquid())
332 vm->m_area.add_x(em, vi_pos_x, +1);
333 if (vm->m_data[vi_pos_x].getContent() != CONTENT_IGNORE) {
334 const ContentFeatures &c_px = ndef->get(vm->m_data[vi_pos_x]);
335 if (c_px.floodable && !c_px.isLiquid())
339 vm->m_area.add_z(em, vi_neg_z, -1);
340 if (vm->m_data[vi_neg_z].getContent() != CONTENT_IGNORE) {
341 const ContentFeatures &c_nz = ndef->get(vm->m_data[vi_neg_z]);
342 if (c_nz.floodable && !c_nz.isLiquid())
346 vm->m_area.add_z(em, vi_pos_z, +1);
347 if (vm->m_data[vi_pos_z].getContent() != CONTENT_IGNORE) {
348 const ContentFeatures &c_pz = ndef->get(vm->m_data[vi_pos_z]);
349 if (c_pz.floodable && !c_pz.isLiquid())
355 void Mapgen::updateLiquid(UniqueQueue<v3s16> *trans_liquid, v3s16 nmin, v3s16 nmax)
357 bool isignored, isliquid, wasignored, wasliquid, waschecked, waspushed;
358 v3s16 em = vm->m_area.getExtent();
360 for (s16 z = nmin.Z + 1; z <= nmax.Z - 1; z++)
361 for (s16 x = nmin.X + 1; x <= nmax.X - 1; x++) {
367 u32 vi = vm->m_area.index(x, nmax.Y, z);
368 for (s16 y = nmax.Y; y >= nmin.Y; y--) {
369 isignored = vm->m_data[vi].getContent() == CONTENT_IGNORE;
370 isliquid = ndef->get(vm->m_data[vi]).isLiquid();
372 if (isignored || wasignored || isliquid == wasliquid) {
373 // Neither topmost node of liquid column nor topmost node below column
376 } else if (isliquid) {
377 // This is the topmost node in the column
378 bool ispushed = false;
379 if (isLiquidHorizontallyFlowable(vi, em)) {
380 trans_liquid->push_back(v3s16(x, y, z));
383 // Remember waschecked and waspushed to avoid repeated
384 // checks/pushes in case the column consists of only this node
386 waspushed = ispushed;
388 // This is the topmost node below a liquid column
390 vm->m_area.add_y(em, vi_above, 1);
391 if (!waspushed && (ndef->get(vm->m_data[vi]).floodable ||
392 (!waschecked && isLiquidHorizontallyFlowable(vi_above, em)))) {
393 // Push back the lowest node in the column which is one
394 // node above this one
395 trans_liquid->push_back(v3s16(x, y + 1, z));
399 wasliquid = isliquid;
400 wasignored = isignored;
401 vm->m_area.add_y(em, vi, -1);
407 void Mapgen::setLighting(u8 light, v3s16 nmin, v3s16 nmax)
409 ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG);
410 VoxelArea a(nmin, nmax);
412 for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) {
413 for (int y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) {
414 u32 i = vm->m_area.index(a.MinEdge.X, y, z);
415 for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++, i++)
416 vm->m_data[i].param1 = light;
422 void Mapgen::lightSpread(VoxelArea &a, v3s16 p, u8 light)
424 if (light <= 1 || !a.contains(p))
427 u32 vi = vm->m_area.index(p);
428 MapNode &n = vm->m_data[vi];
430 // Decay light in each of the banks separately
431 u8 light_day = light & 0x0F;
435 u8 light_night = light & 0xF0;
439 // Bail out only if we have no more light from either bank to propogate, or
440 // we hit a solid block that light cannot pass through.
441 if ((light_day <= (n.param1 & 0x0F) &&
442 light_night <= (n.param1 & 0xF0)) ||
443 !ndef->get(n).light_propagates)
446 // Since this recursive function only terminates when there is no light from
447 // either bank left, we need to take the max of both banks into account for
448 // the case where spreading has stopped for one light bank but not the other.
449 light = MYMAX(light_day, n.param1 & 0x0F) |
450 MYMAX(light_night, n.param1 & 0xF0);
454 lightSpread(a, p + v3s16(0, 0, 1), light);
455 lightSpread(a, p + v3s16(0, 1, 0), light);
456 lightSpread(a, p + v3s16(1, 0, 0), light);
457 lightSpread(a, p - v3s16(0, 0, 1), light);
458 lightSpread(a, p - v3s16(0, 1, 0), light);
459 lightSpread(a, p - v3s16(1, 0, 0), light);
463 void Mapgen::calcLighting(v3s16 nmin, v3s16 nmax, v3s16 full_nmin, v3s16 full_nmax,
464 bool propagate_shadow)
466 ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG);
467 //TimeTaker t("updateLighting");
469 propagateSunlight(nmin, nmax, propagate_shadow);
470 spreadLight(full_nmin, full_nmax);
472 //printf("updateLighting: %dms\n", t.stop());
476 void Mapgen::propagateSunlight(v3s16 nmin, v3s16 nmax, bool propagate_shadow)
478 //TimeTaker t("propagateSunlight");
479 VoxelArea a(nmin, nmax);
480 bool block_is_underground = (water_level >= nmax.Y);
481 v3s16 em = vm->m_area.getExtent();
483 // NOTE: Direct access to the low 4 bits of param1 is okay here because,
484 // by definition, sunlight will never be in the night lightbank.
486 for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) {
487 for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++) {
488 // see if we can get a light value from the overtop
489 u32 i = vm->m_area.index(x, a.MaxEdge.Y + 1, z);
490 if (vm->m_data[i].getContent() == CONTENT_IGNORE) {
491 if (block_is_underground)
493 } else if ((vm->m_data[i].param1 & 0x0F) != LIGHT_SUN &&
497 vm->m_area.add_y(em, i, -1);
499 for (int y = a.MaxEdge.Y; y >= a.MinEdge.Y; y--) {
500 MapNode &n = vm->m_data[i];
501 if (!ndef->get(n).sunlight_propagates)
503 n.param1 = LIGHT_SUN;
504 vm->m_area.add_y(em, i, -1);
508 //printf("propagateSunlight: %dms\n", t.stop());
512 void Mapgen::spreadLight(v3s16 nmin, v3s16 nmax)
514 //TimeTaker t("spreadLight");
515 VoxelArea a(nmin, nmax);
517 for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) {
518 for (int y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) {
519 u32 i = vm->m_area.index(a.MinEdge.X, y, z);
520 for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++, i++) {
521 MapNode &n = vm->m_data[i];
522 if (n.getContent() == CONTENT_IGNORE)
525 const ContentFeatures &cf = ndef->get(n);
526 if (!cf.light_propagates)
529 // TODO(hmmmmm): Abstract away direct param1 accesses with a
530 // wrapper, but something lighter than MapNode::get/setLight
532 u8 light_produced = cf.light_source;
534 n.param1 = light_produced | (light_produced << 4);
538 lightSpread(a, v3s16(x, y, z + 1), light);
539 lightSpread(a, v3s16(x, y + 1, z ), light);
540 lightSpread(a, v3s16(x + 1, y, z ), light);
541 lightSpread(a, v3s16(x, y, z - 1), light);
542 lightSpread(a, v3s16(x, y - 1, z ), light);
543 lightSpread(a, v3s16(x - 1, y, z ), light);
549 //printf("spreadLight: %dms\n", t.stop());
557 MapgenBasic::MapgenBasic(int mapgenid, MapgenParams *params, EmergeManager *emerge)
558 : Mapgen(mapgenid, params, emerge)
560 this->m_emerge = emerge;
561 this->m_bmgr = emerge->biomemgr;
563 //// Here, 'stride' refers to the number of elements needed to skip to index
564 //// an adjacent element for that coordinate in noise/height/biome maps
565 //// (*not* vmanip content map!)
567 // Note there is no X stride explicitly defined. Items adjacent in the X
568 // coordinate are assumed to be adjacent in memory as well (i.e. stride of 1).
570 // Number of elements to skip to get to the next Y coordinate
571 this->ystride = csize.X;
573 // Number of elements to skip to get to the next Z coordinate
574 this->zstride = csize.X * csize.Y;
576 // Z-stride value for maps oversized for 1-down overgeneration
577 this->zstride_1d = csize.X * (csize.Y + 1);
579 // Z-stride value for maps oversized for 1-up 1-down overgeneration
580 this->zstride_1u1d = csize.X * (csize.Y + 2);
582 //// Allocate heightmap
583 this->heightmap = new s16[csize.X * csize.Z];
585 //// Initialize biome generator
586 // TODO(hmmmm): should we have a way to disable biomemanager biomes?
587 biomegen = m_bmgr->createBiomeGen(BIOMEGEN_ORIGINAL, params->bparams, csize);
588 biomemap = biomegen->biomemap;
590 //// Look up some commonly used content
591 c_stone = ndef->getId("mapgen_stone");
592 c_desert_stone = ndef->getId("mapgen_desert_stone");
593 c_sandstone = ndef->getId("mapgen_sandstone");
594 c_water_source = ndef->getId("mapgen_water_source");
595 c_river_water_source = ndef->getId("mapgen_river_water_source");
597 // Fall back to more basic content if not defined
598 // river_water_source cannot fallback to water_source because river water
599 // needs to be non-renewable and have a short flow range.
600 if (c_desert_stone == CONTENT_IGNORE)
601 c_desert_stone = c_stone;
602 if (c_sandstone == CONTENT_IGNORE)
603 c_sandstone = c_stone;
605 //// Content used for dungeon generation
606 c_cobble = ndef->getId("mapgen_cobble");
607 c_mossycobble = ndef->getId("mapgen_mossycobble");
608 c_stair_cobble = ndef->getId("mapgen_stair_cobble");
609 c_stair_desert_stone = ndef->getId("mapgen_stair_desert_stone");
610 c_sandstonebrick = ndef->getId("mapgen_sandstonebrick");
611 c_stair_sandstone_block = ndef->getId("mapgen_stair_sandstone_block");
613 // Fall back to more basic content if not defined
614 if (c_mossycobble == CONTENT_IGNORE)
615 c_mossycobble = c_cobble;
616 if (c_stair_cobble == CONTENT_IGNORE)
617 c_stair_cobble = c_cobble;
618 if (c_stair_desert_stone == CONTENT_IGNORE)
619 c_stair_desert_stone = c_desert_stone;
620 if (c_sandstonebrick == CONTENT_IGNORE)
621 c_sandstonebrick = c_sandstone;
622 if (c_stair_sandstone_block == CONTENT_IGNORE)
623 c_stair_sandstone_block = c_sandstonebrick;
627 MapgenBasic::~MapgenBasic()
634 MgStoneType MapgenBasic::generateBiomes()
636 // can't generate biomes without a biome generator!
640 v3s16 em = vm->m_area.getExtent();
642 MgStoneType stone_type = MGSTONE_STONE;
644 noise_filler_depth->perlinMap2D(node_min.X, node_min.Z);
646 for (s16 z = node_min.Z; z <= node_max.Z; z++)
647 for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
651 u16 depth_water_top = 0;
652 u16 depth_riverbed = 0;
653 u32 vi = vm->m_area.index(x, node_max.Y, z);
655 // Check node at base of mapchunk above, either a node of a previously
656 // generated mapchunk or if not, a node of overgenerated base terrain.
657 content_t c_above = vm->m_data[vi + em.X].getContent();
658 bool air_above = c_above == CONTENT_AIR;
659 bool river_water_above = c_above == c_river_water_source;
660 bool water_above = c_above == c_water_source || river_water_above;
662 biomemap[index] = BIOME_NONE;
664 // If there is air or water above enable top/filler placement, otherwise force
665 // nplaced to stone level by setting a number exceeding any possible filler depth.
666 u16 nplaced = (air_above || water_above) ? 0 : U16_MAX;
668 for (s16 y = node_max.Y; y >= node_min.Y; y--) {
669 content_t c = vm->m_data[vi].getContent();
671 // Biome is recalculated each time an upper surface is detected while
672 // working down a column. The selected biome then remains in effect for
673 // all nodes below until the next surface and biome recalculation.
674 // Biome is recalculated:
675 // 1. At the surface of stone below air or water.
676 // 2. At the surface of water below air.
677 // 3. When stone or water is detected but biome has not yet been calculated.
678 bool is_stone_surface = (c == c_stone) &&
679 (air_above || water_above || !biome);
681 bool is_water_surface =
682 (c == c_water_source || c == c_river_water_source) &&
683 (air_above || !biome);
685 if (is_stone_surface || is_water_surface) {
686 biome = biomegen->getBiomeAtIndex(index, y);
688 if (biomemap[index] == BIOME_NONE && is_stone_surface)
689 biomemap[index] = biome->index;
691 depth_top = biome->depth_top;
692 base_filler = MYMAX(depth_top +
693 biome->depth_filler +
694 noise_filler_depth->result[index], 0.f);
695 depth_water_top = biome->depth_water_top;
696 depth_riverbed = biome->depth_riverbed;
698 // Detect stone type for dungeons during every biome calculation.
699 // This is more efficient than detecting per-node and will not
700 // miss any desert stone or sandstone biomes.
701 if (biome->c_stone == c_desert_stone)
702 stone_type = MGSTONE_DESERT_STONE;
703 else if (biome->c_stone == c_sandstone)
704 stone_type = MGSTONE_SANDSTONE;
708 content_t c_below = vm->m_data[vi - em.X].getContent();
710 // If the node below isn't solid, make this node stone, so that
711 // any top/filler nodes above are structurally supported.
712 // This is done by aborting the cycle of top/filler placement
713 // immediately by forcing nplaced to stone level.
714 if (c_below == CONTENT_AIR
715 || c_below == c_water_source
716 || c_below == c_river_water_source)
719 if (river_water_above) {
720 if (nplaced < depth_riverbed) {
721 vm->m_data[vi] = MapNode(biome->c_riverbed);
724 nplaced = U16_MAX; // Disable top/filler placement
725 river_water_above = false;
727 } else if (nplaced < depth_top) {
728 vm->m_data[vi] = MapNode(biome->c_top);
730 } else if (nplaced < base_filler) {
731 vm->m_data[vi] = MapNode(biome->c_filler);
734 vm->m_data[vi] = MapNode(biome->c_stone);
739 } else if (c == c_water_source) {
740 vm->m_data[vi] = MapNode((y > (s32)(water_level - depth_water_top))
741 ? biome->c_water_top : biome->c_water);
742 nplaced = 0; // Enable top/filler placement for next surface
745 } else if (c == c_river_water_source) {
746 vm->m_data[vi] = MapNode(biome->c_river_water);
747 nplaced = 0; // Enable riverbed placement for next surface
750 river_water_above = true;
751 } else if (c == CONTENT_AIR) {
752 nplaced = 0; // Enable top/filler placement for next surface
755 } else { // Possible various nodes overgenerated from neighbouring mapchunks
756 nplaced = U16_MAX; // Disable top/filler placement
761 vm->m_area.add_y(em, vi, -1);
769 void MapgenBasic::dustTopNodes()
771 if (node_max.Y < water_level)
774 v3s16 em = vm->m_area.getExtent();
777 for (s16 z = node_min.Z; z <= node_max.Z; z++)
778 for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
779 Biome *biome = (Biome *)m_bmgr->getRaw(biomemap[index]);
781 if (biome->c_dust == CONTENT_IGNORE)
784 u32 vi = vm->m_area.index(x, full_node_max.Y, z);
785 content_t c_full_max = vm->m_data[vi].getContent();
788 if (c_full_max == CONTENT_AIR) {
789 y_start = full_node_max.Y - 1;
790 } else if (c_full_max == CONTENT_IGNORE) {
791 vi = vm->m_area.index(x, node_max.Y + 1, z);
792 content_t c_max = vm->m_data[vi].getContent();
794 if (c_max == CONTENT_AIR)
795 y_start = node_max.Y;
802 vi = vm->m_area.index(x, y_start, z);
803 for (s16 y = y_start; y >= node_min.Y - 1; y--) {
804 if (vm->m_data[vi].getContent() != CONTENT_AIR)
807 vm->m_area.add_y(em, vi, -1);
810 content_t c = vm->m_data[vi].getContent();
811 if (!ndef->get(c).buildable_to && c != CONTENT_IGNORE && c != biome->c_dust) {
812 vm->m_area.add_y(em, vi, 1);
813 vm->m_data[vi] = MapNode(biome->c_dust);
819 void MapgenBasic::generateCaves(s16 max_stone_y, s16 large_cave_depth)
821 if (max_stone_y < node_min.Y)
824 CavesNoiseIntersection caves_noise(ndef, m_bmgr, csize,
825 &np_cave1, &np_cave2, seed, cave_width);
827 caves_noise.generateCaves(vm, node_min, node_max, biomemap);
829 if (node_max.Y > large_cave_depth)
832 PseudoRandom ps(blockseed + 21343);
833 u32 bruises_count = ps.range(0, 2);
834 for (u32 i = 0; i < bruises_count; i++) {
835 CavesRandomWalk cave(ndef, &gennotify, seed, water_level,
836 c_water_source, CONTENT_IGNORE);
838 cave.makeCave(vm, node_min, node_max, &ps, true, max_stone_y, heightmap);
843 void MapgenBasic::generateDungeons(s16 max_stone_y, MgStoneType stone_type)
845 if (max_stone_y < node_min.Y)
851 dp.c_water = c_water_source;
852 dp.c_river_water = c_river_water_source;
854 dp.only_in_ground = true;
855 dp.corridor_len_min = 1;
856 dp.corridor_len_max = 13;
859 dp.y_min = -MAX_MAP_GENERATION_LIMIT;
860 dp.y_max = MAX_MAP_GENERATION_LIMIT;
862 dp.np_density = nparams_dungeon_density;
863 dp.np_alt_wall = nparams_dungeon_alt_wall;
865 switch (stone_type) {
868 dp.c_wall = c_cobble;
869 dp.c_alt_wall = c_mossycobble;
870 dp.c_stair = c_stair_cobble;
872 dp.diagonal_dirs = false;
873 dp.holesize = v3s16(1, 2, 1);
874 dp.room_size_min = v3s16(4, 4, 4);
875 dp.room_size_max = v3s16(8, 6, 8);
876 dp.room_size_large_min = v3s16(8, 8, 8);
877 dp.room_size_large_max = v3s16(16, 16, 16);
878 dp.notifytype = GENNOTIFY_DUNGEON;
880 case MGSTONE_DESERT_STONE:
881 dp.c_wall = c_desert_stone;
882 dp.c_alt_wall = CONTENT_IGNORE;
883 dp.c_stair = c_stair_desert_stone;
885 dp.diagonal_dirs = true;
886 dp.holesize = v3s16(2, 3, 2);
887 dp.room_size_min = v3s16(6, 9, 6);
888 dp.room_size_max = v3s16(10, 11, 10);
889 dp.room_size_large_min = v3s16(10, 13, 10);
890 dp.room_size_large_max = v3s16(18, 21, 18);
891 dp.notifytype = GENNOTIFY_TEMPLE;
893 case MGSTONE_SANDSTONE:
894 dp.c_wall = c_sandstonebrick;
895 dp.c_alt_wall = CONTENT_IGNORE;
896 dp.c_stair = c_stair_sandstone_block;
898 dp.diagonal_dirs = false;
899 dp.holesize = v3s16(2, 2, 2);
900 dp.room_size_min = v3s16(6, 4, 6);
901 dp.room_size_max = v3s16(10, 6, 10);
902 dp.room_size_large_min = v3s16(10, 8, 10);
903 dp.room_size_large_max = v3s16(18, 16, 18);
904 dp.notifytype = GENNOTIFY_DUNGEON;
908 DungeonGen dgen(ndef, &gennotify, &dp);
909 dgen.generate(vm, blockseed, full_node_min, full_node_max);
914 //// GenerateNotifier
917 GenerateNotifier::GenerateNotifier()
923 GenerateNotifier::GenerateNotifier(u32 notify_on,
924 std::set<u32> *notify_on_deco_ids)
926 m_notify_on = notify_on;
927 m_notify_on_deco_ids = notify_on_deco_ids;
931 void GenerateNotifier::setNotifyOn(u32 notify_on)
933 m_notify_on = notify_on;
937 void GenerateNotifier::setNotifyOnDecoIds(std::set<u32> *notify_on_deco_ids)
939 m_notify_on_deco_ids = notify_on_deco_ids;
943 bool GenerateNotifier::addEvent(GenNotifyType type, v3s16 pos, u32 id)
945 if (!(m_notify_on & (1 << type)))
948 if (type == GENNOTIFY_DECORATION &&
949 m_notify_on_deco_ids->find(id) == m_notify_on_deco_ids->end())
956 m_notify_events.push_back(gne);
962 void GenerateNotifier::getEvents(
963 std::map<std::string, std::vector<v3s16> > &event_map,
966 std::list<GenNotifyEvent>::iterator it;
968 for (it = m_notify_events.begin(); it != m_notify_events.end(); ++it) {
969 GenNotifyEvent &gn = *it;
970 std::string name = (gn.type == GENNOTIFY_DECORATION) ?
971 "decoration#"+ itos(gn.id) :
972 flagdesc_gennotify[gn.type].name;
974 event_map[name].push_back(gn.pos);
978 m_notify_events.clear();
987 MapgenParams::~MapgenParams()
993 void MapgenParams::readParams(const Settings *settings)
995 std::string seed_str;
996 const char *seed_name = (settings == g_settings) ? "fixed_map_seed" : "seed";
998 if (settings->getNoEx(seed_name, seed_str)) {
999 if (!seed_str.empty())
1000 seed = read_seed(seed_str.c_str());
1002 myrand_bytes(&seed, sizeof(seed));
1005 std::string mg_name;
1006 if (settings->getNoEx("mg_name", mg_name)) {
1007 mgtype = Mapgen::getMapgenType(mg_name);
1008 if (mgtype == MAPGEN_INVALID)
1009 mgtype = MAPGEN_DEFAULT;
1012 settings->getS16NoEx("water_level", water_level);
1013 settings->getS16NoEx("mapgen_limit", mapgen_limit);
1014 settings->getS16NoEx("chunksize", chunksize);
1015 settings->getFlagStrNoEx("mg_flags", flags, flagdesc_mapgen);
1018 bparams = BiomeManager::createBiomeParams(BIOMEGEN_ORIGINAL);
1020 bparams->readParams(settings);
1021 bparams->seed = seed;
1026 void MapgenParams::writeParams(Settings *settings) const
1028 settings->set("mg_name", Mapgen::getMapgenName(mgtype));
1029 settings->setU64("seed", seed);
1030 settings->setS16("water_level", water_level);
1031 settings->setS16("mapgen_limit", mapgen_limit);
1032 settings->setS16("chunksize", chunksize);
1033 settings->setFlagStr("mg_flags", flags, flagdesc_mapgen, U32_MAX);
1036 bparams->writeParams(settings);