3 Copyright (C) 2010-2018 celeron55, Perttu Ahola <celeron55@gmail.com>
4 Copyright (C) 2013-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
5 Copyright (C) 2015-2018 paramat
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License along
18 with this program; if not, write to the Free Software Foundation, Inc.,
19 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
31 #include "content_sao.h"
34 #include "voxelalgorithms.h"
39 #include "serialization.h"
40 #include "util/serialize.h"
41 #include "util/numeric.h"
42 #include "util/directiontables.h"
45 #include "mapgen_carpathian.h"
46 #include "mapgen_flat.h"
47 #include "mapgen_fractal.h"
48 #include "mapgen_v5.h"
49 #include "mapgen_v6.h"
50 #include "mapgen_v7.h"
51 #include "mapgen_valleys.h"
52 #include "mapgen_singlenode.h"
54 #include "dungeongen.h"
56 FlagDesc flagdesc_mapgen[] = {
58 {"dungeons", MG_DUNGEONS},
60 {"decorations", MG_DECORATIONS},
61 {"biomes", MG_BIOMES},
65 FlagDesc flagdesc_gennotify[] = {
66 {"dungeon", 1 << GENNOTIFY_DUNGEON},
67 {"temple", 1 << GENNOTIFY_TEMPLE},
68 {"cave_begin", 1 << GENNOTIFY_CAVE_BEGIN},
69 {"cave_end", 1 << GENNOTIFY_CAVE_END},
70 {"large_cave_begin", 1 << GENNOTIFY_LARGECAVE_BEGIN},
71 {"large_cave_end", 1 << GENNOTIFY_LARGECAVE_END},
72 {"decoration", 1 << GENNOTIFY_DECORATION},
85 // Order used here defines the order of appearence in mainmenu.
86 // v6 always last to discourage selection.
87 // Special mapgens flat, fractal, singlenode, next to last. Of these, singlenode
88 // last to discourage selection.
89 // Of the remaining, v5 last due to age, v7 first due to being the default.
90 // The order of 'enum MapgenType' in mapgen.h must match this order.
91 static MapgenDesc g_reg_mapgens[] = {
103 ARRLEN(g_reg_mapgens) == MAPGEN_INVALID,
104 registered_mapgens_is_wrong_size);
110 Mapgen::Mapgen(int mapgenid, MapgenParams *params, EmergeManager *emerge) :
111 gennotify(emerge->gen_notify_on, &emerge->gen_notify_on_deco_ids)
114 water_level = params->water_level;
115 mapgen_limit = params->mapgen_limit;
116 flags = params->flags;
117 csize = v3s16(1, 1, 1) * (params->chunksize * MAP_BLOCKSIZE);
120 We are losing half our entropy by doing this, but it is necessary to
121 preserve reverse compatibility. If the top half of our current 64 bit
122 seeds ever starts getting used, existing worlds will break due to a
123 different hash outcome and no way to differentiate between versions.
125 A solution could be to add a new bit to designate that the top half of
126 the seed value should be used, essentially a 1-bit version code, but
127 this would require increasing the total size of a seed to 9 bytes (yuck)
129 It's probably okay if this never gets fixed. 4.2 billion possibilities
130 ought to be enough for anyone.
132 seed = (s32)params->seed;
138 MapgenType Mapgen::getMapgenType(const std::string &mgname)
140 for (size_t i = 0; i != ARRLEN(g_reg_mapgens); i++) {
141 if (mgname == g_reg_mapgens[i].name)
142 return (MapgenType)i;
145 return MAPGEN_INVALID;
149 const char *Mapgen::getMapgenName(MapgenType mgtype)
151 size_t index = (size_t)mgtype;
152 if (index == MAPGEN_INVALID || index >= ARRLEN(g_reg_mapgens))
155 return g_reg_mapgens[index].name;
159 Mapgen *Mapgen::createMapgen(MapgenType mgtype, MapgenParams *params,
160 EmergeManager *emerge)
163 case MAPGEN_CARPATHIAN:
164 return new MapgenCarpathian((MapgenCarpathianParams *)params, emerge);
166 return new MapgenFlat((MapgenFlatParams *)params, emerge);
168 return new MapgenFractal((MapgenFractalParams *)params, emerge);
169 case MAPGEN_SINGLENODE:
170 return new MapgenSinglenode((MapgenSinglenodeParams *)params, emerge);
172 return new MapgenV5((MapgenV5Params *)params, emerge);
174 return new MapgenV6((MapgenV6Params *)params, emerge);
176 return new MapgenV7((MapgenV7Params *)params, emerge);
178 return new MapgenValleys((MapgenValleysParams *)params, emerge);
185 MapgenParams *Mapgen::createMapgenParams(MapgenType mgtype)
188 case MAPGEN_CARPATHIAN:
189 return new MapgenCarpathianParams;
191 return new MapgenFlatParams;
193 return new MapgenFractalParams;
194 case MAPGEN_SINGLENODE:
195 return new MapgenSinglenodeParams;
197 return new MapgenV5Params;
199 return new MapgenV6Params;
201 return new MapgenV7Params;
203 return new MapgenValleysParams;
210 void Mapgen::getMapgenNames(std::vector<const char *> *mgnames, bool include_hidden)
212 for (u32 i = 0; i != ARRLEN(g_reg_mapgens); i++) {
213 if (include_hidden || g_reg_mapgens[i].is_user_visible)
214 mgnames->push_back(g_reg_mapgens[i].name);
218 void Mapgen::setDefaultSettings(Settings *settings)
220 settings->setDefault("mg_flags", flagdesc_mapgen,
221 MG_CAVES | MG_DUNGEONS | MG_LIGHT | MG_DECORATIONS | MG_BIOMES);
223 for (int i = 0; i < (int)MAPGEN_INVALID; ++i) {
224 MapgenParams *params = createMapgenParams((MapgenType)i);
225 params->setDefaultSettings(settings);
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 const 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 VoxelArea::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 const 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 VoxelArea::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 const 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;
297 if (ndef->get(n).isLiquid())
300 VoxelArea::add_y(em, i, -1);
302 return (y >= ymin) ? y : -MAX_MAP_GENERATION_LIMIT;
306 void Mapgen::updateHeightmap(v3s16 nmin, v3s16 nmax)
311 //TimeTaker t("Mapgen::updateHeightmap", NULL, PRECISION_MICRO);
313 for (s16 z = nmin.Z; z <= nmax.Z; z++) {
314 for (s16 x = nmin.X; x <= nmax.X; x++, index++) {
315 s16 y = findGroundLevel(v2s16(x, z), nmin.Y, nmax.Y);
317 heightmap[index] = y;
323 void Mapgen::getSurfaces(v2s16 p2d, s16 ymin, s16 ymax,
324 std::vector<s16> &floors, std::vector<s16> &ceilings)
326 const v3s16 &em = vm->m_area.getExtent();
328 bool is_walkable = false;
329 u32 vi = vm->m_area.index(p2d.X, ymax, p2d.Y);
330 MapNode mn_max = vm->m_data[vi];
331 bool walkable_above = ndef->get(mn_max).walkable;
332 VoxelArea::add_y(em, vi, -1);
334 for (s16 y = ymax - 1; y >= ymin; y--) {
335 MapNode mn = vm->m_data[vi];
336 is_walkable = ndef->get(mn).walkable;
338 if (is_walkable && !walkable_above) {
340 } else if (!is_walkable && walkable_above) {
341 ceilings.push_back(y + 1);
344 VoxelArea::add_y(em, vi, -1);
345 walkable_above = is_walkable;
350 inline bool Mapgen::isLiquidHorizontallyFlowable(u32 vi, v3s16 em)
353 VoxelArea::add_x(em, vi_neg_x, -1);
354 if (vm->m_data[vi_neg_x].getContent() != CONTENT_IGNORE) {
355 const ContentFeatures &c_nx = ndef->get(vm->m_data[vi_neg_x]);
356 if (c_nx.floodable && !c_nx.isLiquid())
360 VoxelArea::add_x(em, vi_pos_x, +1);
361 if (vm->m_data[vi_pos_x].getContent() != CONTENT_IGNORE) {
362 const ContentFeatures &c_px = ndef->get(vm->m_data[vi_pos_x]);
363 if (c_px.floodable && !c_px.isLiquid())
367 VoxelArea::add_z(em, vi_neg_z, -1);
368 if (vm->m_data[vi_neg_z].getContent() != CONTENT_IGNORE) {
369 const ContentFeatures &c_nz = ndef->get(vm->m_data[vi_neg_z]);
370 if (c_nz.floodable && !c_nz.isLiquid())
374 VoxelArea::add_z(em, vi_pos_z, +1);
375 if (vm->m_data[vi_pos_z].getContent() != CONTENT_IGNORE) {
376 const ContentFeatures &c_pz = ndef->get(vm->m_data[vi_pos_z]);
377 if (c_pz.floodable && !c_pz.isLiquid())
383 void Mapgen::updateLiquid(UniqueQueue<v3s16> *trans_liquid, v3s16 nmin, v3s16 nmax)
385 bool isignored, isliquid, wasignored, wasliquid, waschecked, waspushed;
386 const v3s16 &em = vm->m_area.getExtent();
388 for (s16 z = nmin.Z + 1; z <= nmax.Z - 1; z++)
389 for (s16 x = nmin.X + 1; x <= nmax.X - 1; x++) {
395 u32 vi = vm->m_area.index(x, nmax.Y, z);
396 for (s16 y = nmax.Y; y >= nmin.Y; y--) {
397 isignored = vm->m_data[vi].getContent() == CONTENT_IGNORE;
398 isliquid = ndef->get(vm->m_data[vi]).isLiquid();
400 if (isignored || wasignored || isliquid == wasliquid) {
401 // Neither topmost node of liquid column nor topmost node below column
404 } else if (isliquid) {
405 // This is the topmost node in the column
406 bool ispushed = false;
407 if (isLiquidHorizontallyFlowable(vi, em)) {
408 trans_liquid->push_back(v3s16(x, y, z));
411 // Remember waschecked and waspushed to avoid repeated
412 // checks/pushes in case the column consists of only this node
414 waspushed = ispushed;
416 // This is the topmost node below a liquid column
418 VoxelArea::add_y(em, vi_above, 1);
419 if (!waspushed && (ndef->get(vm->m_data[vi]).floodable ||
420 (!waschecked && isLiquidHorizontallyFlowable(vi_above, em)))) {
421 // Push back the lowest node in the column which is one
422 // node above this one
423 trans_liquid->push_back(v3s16(x, y + 1, z));
427 wasliquid = isliquid;
428 wasignored = isignored;
429 VoxelArea::add_y(em, vi, -1);
435 void Mapgen::setLighting(u8 light, v3s16 nmin, v3s16 nmax)
437 ScopeProfiler sp(g_profiler, "EmergeThread: update lighting", SPT_AVG);
438 VoxelArea a(nmin, nmax);
440 for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) {
441 for (int y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) {
442 u32 i = vm->m_area.index(a.MinEdge.X, y, z);
443 for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++, i++)
444 vm->m_data[i].param1 = light;
450 void Mapgen::lightSpread(VoxelArea &a, std::queue<std::pair<v3s16, u8>> &queue,
451 const v3s16 &p, u8 light)
453 if (light <= 1 || !a.contains(p))
456 u32 vi = vm->m_area.index(p);
457 MapNode &n = vm->m_data[vi];
459 // Decay light in each of the banks separately
460 u8 light_day = light & 0x0F;
464 u8 light_night = light & 0xF0;
468 // Bail out only if we have no more light from either bank to propogate, or
469 // we hit a solid block that light cannot pass through.
470 if ((light_day <= (n.param1 & 0x0F) &&
471 light_night <= (n.param1 & 0xF0)) ||
472 !ndef->get(n).light_propagates)
475 // Since this recursive function only terminates when there is no light from
476 // either bank left, we need to take the max of both banks into account for
477 // the case where spreading has stopped for one light bank but not the other.
478 light = MYMAX(light_day, n.param1 & 0x0F) |
479 MYMAX(light_night, n.param1 & 0xF0);
484 queue.emplace(p, light);
488 void Mapgen::calcLighting(v3s16 nmin, v3s16 nmax, v3s16 full_nmin, v3s16 full_nmax,
489 bool propagate_shadow)
491 ScopeProfiler sp(g_profiler, "EmergeThread: update lighting", SPT_AVG);
492 //TimeTaker t("updateLighting");
494 propagateSunlight(nmin, nmax, propagate_shadow);
495 spreadLight(full_nmin, full_nmax);
497 //printf("updateLighting: %dms\n", t.stop());
501 void Mapgen::propagateSunlight(v3s16 nmin, v3s16 nmax, bool propagate_shadow)
503 //TimeTaker t("propagateSunlight");
504 VoxelArea a(nmin, nmax);
505 bool block_is_underground = (water_level >= nmax.Y);
506 const v3s16 &em = vm->m_area.getExtent();
508 // NOTE: Direct access to the low 4 bits of param1 is okay here because,
509 // by definition, sunlight will never be in the night lightbank.
511 for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) {
512 for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++) {
513 // see if we can get a light value from the overtop
514 u32 i = vm->m_area.index(x, a.MaxEdge.Y + 1, z);
515 if (vm->m_data[i].getContent() == CONTENT_IGNORE) {
516 if (block_is_underground)
518 } else if ((vm->m_data[i].param1 & 0x0F) != LIGHT_SUN &&
522 VoxelArea::add_y(em, i, -1);
524 for (int y = a.MaxEdge.Y; y >= a.MinEdge.Y; y--) {
525 MapNode &n = vm->m_data[i];
526 if (!ndef->get(n).sunlight_propagates)
528 n.param1 = LIGHT_SUN;
529 VoxelArea::add_y(em, i, -1);
533 //printf("propagateSunlight: %dms\n", t.stop());
537 void Mapgen::spreadLight(const v3s16 &nmin, const v3s16 &nmax)
539 //TimeTaker t("spreadLight");
540 std::queue<std::pair<v3s16, u8>> queue;
541 VoxelArea a(nmin, nmax);
543 for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) {
544 for (int y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) {
545 u32 i = vm->m_area.index(a.MinEdge.X, y, z);
546 for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++, i++) {
547 MapNode &n = vm->m_data[i];
548 if (n.getContent() == CONTENT_IGNORE)
551 const ContentFeatures &cf = ndef->get(n);
552 if (!cf.light_propagates)
555 // TODO(hmmmmm): Abstract away direct param1 accesses with a
556 // wrapper, but something lighter than MapNode::get/setLight
558 u8 light_produced = cf.light_source;
560 n.param1 = light_produced | (light_produced << 4);
564 const v3s16 p(x, y, z);
565 // spread to all 6 neighbor nodes
566 for (const auto &dir : g_6dirs)
567 lightSpread(a, queue, p + dir, light);
573 while (!queue.empty()) {
574 const auto &i = queue.front();
575 // spread to all 6 neighbor nodes
576 for (const auto &dir : g_6dirs)
577 lightSpread(a, queue, i.first + dir, i.second);
581 //printf("spreadLight: %lums\n", t.stop());
589 MapgenBasic::MapgenBasic(int mapgenid, MapgenParams *params, EmergeManager *emerge)
590 : Mapgen(mapgenid, params, emerge)
592 this->m_emerge = emerge;
593 this->m_bmgr = emerge->biomemgr;
595 //// Here, 'stride' refers to the number of elements needed to skip to index
596 //// an adjacent element for that coordinate in noise/height/biome maps
597 //// (*not* vmanip content map!)
599 // Note there is no X stride explicitly defined. Items adjacent in the X
600 // coordinate are assumed to be adjacent in memory as well (i.e. stride of 1).
602 // Number of elements to skip to get to the next Y coordinate
603 this->ystride = csize.X;
605 // Number of elements to skip to get to the next Z coordinate
606 this->zstride = csize.X * csize.Y;
608 // Z-stride value for maps oversized for 1-down overgeneration
609 this->zstride_1d = csize.X * (csize.Y + 1);
611 // Z-stride value for maps oversized for 1-up 1-down overgeneration
612 this->zstride_1u1d = csize.X * (csize.Y + 2);
614 //// Allocate heightmap
615 this->heightmap = new s16[csize.X * csize.Z];
617 //// Initialize biome generator
618 biomegen = m_bmgr->createBiomeGen(BIOMEGEN_ORIGINAL, params->bparams, csize);
619 biomemap = biomegen->biomemap;
621 //// Look up some commonly used content
622 c_stone = ndef->getId("mapgen_stone");
623 c_water_source = ndef->getId("mapgen_water_source");
624 c_river_water_source = ndef->getId("mapgen_river_water_source");
625 c_lava_source = ndef->getId("mapgen_lava_source");
626 c_cobble = ndef->getId("mapgen_cobble");
628 // Fall back to more basic content if not defined.
629 // Lava falls back to water as both are suitable as cave liquids.
630 if (c_lava_source == CONTENT_IGNORE)
631 c_lava_source = c_water_source;
633 if (c_stone == CONTENT_IGNORE)
634 errorstream << "Mapgen: Mapgen alias 'mapgen_stone' is invalid!" << std::endl;
635 if (c_water_source == CONTENT_IGNORE)
636 errorstream << "Mapgen: Mapgen alias 'mapgen_water_source' is invalid!" << std::endl;
637 if (c_river_water_source == CONTENT_IGNORE)
638 warningstream << "Mapgen: Mapgen alias 'mapgen_river_water_source' is invalid!" << std::endl;
642 MapgenBasic::~MapgenBasic()
649 void MapgenBasic::generateBiomes()
651 // can't generate biomes without a biome generator!
655 const v3s16 &em = vm->m_area.getExtent();
658 noise_filler_depth->perlinMap2D(node_min.X, node_min.Z);
660 for (s16 z = node_min.Z; z <= node_max.Z; z++)
661 for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
663 biome_t water_biome_index = 0;
666 u16 depth_water_top = 0;
667 u16 depth_riverbed = 0;
668 s16 biome_y_min = -MAX_MAP_GENERATION_LIMIT;
669 u32 vi = vm->m_area.index(x, node_max.Y, z);
671 // Check node at base of mapchunk above, either a node of a previously
672 // generated mapchunk or if not, a node of overgenerated base terrain.
673 content_t c_above = vm->m_data[vi + em.X].getContent();
674 bool air_above = c_above == CONTENT_AIR;
675 bool river_water_above = c_above == c_river_water_source;
676 bool water_above = c_above == c_water_source || river_water_above;
678 biomemap[index] = BIOME_NONE;
680 // If there is air or water above enable top/filler placement, otherwise force
681 // nplaced to stone level by setting a number exceeding any possible filler depth.
682 u16 nplaced = (air_above || water_above) ? 0 : U16_MAX;
684 for (s16 y = node_max.Y; y >= node_min.Y; y--) {
685 content_t c = vm->m_data[vi].getContent();
686 // Biome is (re)calculated:
687 // 1. At the surface of stone below air or water.
688 // 2. At the surface of water below air.
689 // 3. When stone or water is detected but biome has not yet been calculated.
690 // 4. When stone or water is detected just below a biome's lower limit.
691 bool is_stone_surface = (c == c_stone) &&
692 (air_above || water_above || !biome || y < biome_y_min); // 1, 3, 4
694 bool is_water_surface =
695 (c == c_water_source || c == c_river_water_source) &&
696 (air_above || !biome || y < biome_y_min); // 2, 3, 4
698 if (is_stone_surface || is_water_surface) {
699 // (Re)calculate biome
700 biome = biomegen->getBiomeAtIndex(index, v3s16(x, y, z));
702 // Add biome to biomemap at first stone surface detected
703 if (biomemap[index] == BIOME_NONE && is_stone_surface)
704 biomemap[index] = biome->index;
706 // Store biome of first water surface detected, as a fallback
707 // entry for the biomemap.
708 if (water_biome_index == 0 && is_water_surface)
709 water_biome_index = biome->index;
711 depth_top = biome->depth_top;
712 base_filler = MYMAX(depth_top +
713 biome->depth_filler +
714 noise_filler_depth->result[index], 0.0f);
715 depth_water_top = biome->depth_water_top;
716 depth_riverbed = biome->depth_riverbed;
717 biome_y_min = biome->min_pos.Y;
721 content_t c_below = vm->m_data[vi - em.X].getContent();
723 // If the node below isn't solid, make this node stone, so that
724 // any top/filler nodes above are structurally supported.
725 // This is done by aborting the cycle of top/filler placement
726 // immediately by forcing nplaced to stone level.
727 if (c_below == CONTENT_AIR
728 || c_below == c_water_source
729 || c_below == c_river_water_source)
732 if (river_water_above) {
733 if (nplaced < depth_riverbed) {
734 vm->m_data[vi] = MapNode(biome->c_riverbed);
737 nplaced = U16_MAX; // Disable top/filler placement
738 river_water_above = false;
740 } else if (nplaced < depth_top) {
741 vm->m_data[vi] = MapNode(biome->c_top);
743 } else if (nplaced < base_filler) {
744 vm->m_data[vi] = MapNode(biome->c_filler);
747 vm->m_data[vi] = MapNode(biome->c_stone);
748 nplaced = U16_MAX; // Disable top/filler placement
753 } else if (c == c_water_source) {
754 vm->m_data[vi] = MapNode((y > (s32)(water_level - depth_water_top))
755 ? biome->c_water_top : biome->c_water);
756 nplaced = 0; // Enable top/filler placement for next surface
759 } else if (c == c_river_water_source) {
760 vm->m_data[vi] = MapNode(biome->c_river_water);
761 nplaced = 0; // Enable riverbed placement for next surface
764 river_water_above = true;
765 } else if (c == CONTENT_AIR) {
766 nplaced = 0; // Enable top/filler placement for next surface
769 } else { // Possible various nodes overgenerated from neighbouring mapchunks
770 nplaced = U16_MAX; // Disable top/filler placement
775 VoxelArea::add_y(em, vi, -1);
777 // If no stone surface detected in mapchunk column and a water surface
778 // biome fallback exists, add it to the biomemap. This avoids water
779 // surface decorations failing in deep water.
780 if (biomemap[index] == BIOME_NONE && water_biome_index != 0)
781 biomemap[index] = water_biome_index;
786 void MapgenBasic::dustTopNodes()
788 if (node_max.Y < water_level)
791 const v3s16 &em = vm->m_area.getExtent();
794 for (s16 z = node_min.Z; z <= node_max.Z; z++)
795 for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
796 Biome *biome = (Biome *)m_bmgr->getRaw(biomemap[index]);
798 if (biome->c_dust == CONTENT_IGNORE)
801 // Check if mapchunk above has generated, if so, drop dust from 16 nodes
802 // above current mapchunk top, above decorations that will extend above
803 // the current mapchunk. If the mapchunk above has not generated, it
804 // will provide this required dust when it does.
805 u32 vi = vm->m_area.index(x, full_node_max.Y, z);
806 content_t c_full_max = vm->m_data[vi].getContent();
809 if (c_full_max == CONTENT_AIR) {
810 y_start = full_node_max.Y - 1;
811 } else if (c_full_max == CONTENT_IGNORE) {
812 vi = vm->m_area.index(x, node_max.Y + 1, z);
813 content_t c_max = vm->m_data[vi].getContent();
815 if (c_max == CONTENT_AIR)
816 y_start = node_max.Y;
823 vi = vm->m_area.index(x, y_start, z);
824 for (s16 y = y_start; y >= node_min.Y - 1; y--) {
825 if (vm->m_data[vi].getContent() != CONTENT_AIR)
828 VoxelArea::add_y(em, vi, -1);
831 content_t c = vm->m_data[vi].getContent();
832 NodeDrawType dtype = ndef->get(c).drawtype;
833 // Only place on cubic, walkable, non-dust nodes.
834 // Dust check needed due to avoid double layer of dust caused by
835 // dropping dust from 16 nodes above mapchunk top.
836 if ((dtype == NDT_NORMAL ||
837 dtype == NDT_ALLFACES ||
838 dtype == NDT_ALLFACES_OPTIONAL ||
839 dtype == NDT_GLASSLIKE ||
840 dtype == NDT_GLASSLIKE_FRAMED ||
841 dtype == NDT_GLASSLIKE_FRAMED_OPTIONAL) &&
842 ndef->get(c).walkable && c != biome->c_dust) {
843 VoxelArea::add_y(em, vi, 1);
844 vm->m_data[vi] = MapNode(biome->c_dust);
850 void MapgenBasic::generateCavesNoiseIntersection(s16 max_stone_y)
852 // cave_width >= 10 is used to disable generation and avoid the intensive
853 // 3D noise calculations. Tunnels already have zero width when cave_width > 1.
854 if (node_min.Y > max_stone_y || cave_width >= 10.0f)
857 CavesNoiseIntersection caves_noise(ndef, m_bmgr, csize,
858 &np_cave1, &np_cave2, seed, cave_width);
860 caves_noise.generateCaves(vm, node_min, node_max, biomemap);
864 void MapgenBasic::generateCavesRandomWalk(s16 max_stone_y, s16 large_cave_ymax)
866 if (node_min.Y > max_stone_y)
869 PseudoRandom ps(blockseed + 21343);
870 // Small randomwalk caves
871 u32 num_small_caves = ps.range(small_cave_num_min, small_cave_num_max);
873 for (u32 i = 0; i < num_small_caves; i++) {
874 CavesRandomWalk cave(ndef, &gennotify, seed, water_level,
875 c_water_source, c_lava_source, large_cave_flooded, biomegen);
876 cave.makeCave(vm, node_min, node_max, &ps, false, max_stone_y, heightmap);
879 if (node_max.Y > large_cave_ymax)
882 // Large randomwalk caves below 'large_cave_ymax'.
883 // 'large_cave_ymax' can differ from the 'large_cave_depth' mapgen parameter,
884 // it is set to world base to disable large caves in or near caverns.
885 u32 num_large_caves = ps.range(large_cave_num_min, large_cave_num_max);
887 for (u32 i = 0; i < num_large_caves; i++) {
888 CavesRandomWalk cave(ndef, &gennotify, seed, water_level,
889 c_water_source, c_lava_source, large_cave_flooded, biomegen);
890 cave.makeCave(vm, node_min, node_max, &ps, true, max_stone_y, heightmap);
895 bool MapgenBasic::generateCavernsNoise(s16 max_stone_y)
897 if (node_min.Y > max_stone_y || node_min.Y > cavern_limit)
900 CavernsNoise caverns_noise(ndef, csize, &np_cavern,
901 seed, cavern_limit, cavern_taper, cavern_threshold);
903 return caverns_noise.generateCaverns(vm, node_min, node_max);
907 void MapgenBasic::generateDungeons(s16 max_stone_y)
909 if (node_min.Y > max_stone_y || node_min.Y > dungeon_ymax ||
910 node_max.Y < dungeon_ymin)
913 u16 num_dungeons = std::fmax(std::floor(
914 NoisePerlin3D(&np_dungeons, node_min.X, node_min.Y, node_min.Z, seed)), 0.0f);
915 if (num_dungeons == 0)
918 PseudoRandom ps(blockseed + 70033);
923 NoiseParams(-0.4, 1.0, v3f(40.0, 40.0, 40.0), 32474, 6, 1.1, 2.0);
926 dp.only_in_ground = true;
927 dp.num_dungeons = num_dungeons;
928 dp.notifytype = GENNOTIFY_DUNGEON;
929 dp.num_rooms = ps.range(2, 16);
930 dp.room_size_min = v3s16(5, 5, 5);
931 dp.room_size_max = v3s16(12, 6, 12);
932 dp.room_size_large_min = v3s16(12, 6, 12);
933 dp.room_size_large_max = v3s16(16, 16, 16);
934 dp.large_room_chance = (ps.range(1, 4) == 1) ? 8 : 0;
935 dp.diagonal_dirs = ps.range(1, 8) == 1;
936 // Diagonal corridors must have 'hole' width >=2 to be passable
937 u8 holewidth = (dp.diagonal_dirs) ? 2 : ps.range(1, 2);
938 dp.holesize = v3s16(holewidth, 3, holewidth);
939 dp.corridor_len_min = 1;
940 dp.corridor_len_max = 13;
942 // Get biome at mapchunk midpoint
943 v3s16 chunk_mid = node_min + (node_max - node_min) / v3s16(2, 2, 2);
944 Biome *biome = (Biome *)biomegen->getBiomeAtPoint(chunk_mid);
946 // Use biome-defined dungeon nodes if defined
947 if (biome->c_dungeon != CONTENT_IGNORE) {
948 dp.c_wall = biome->c_dungeon;
949 // If 'node_dungeon_alt' is not defined by biome, it and dp.c_alt_wall
950 // become CONTENT_IGNORE which skips the alt wall node placement loop in
952 dp.c_alt_wall = biome->c_dungeon_alt;
953 // Stairs fall back to 'c_dungeon' if not defined by biome
954 dp.c_stair = (biome->c_dungeon_stair != CONTENT_IGNORE) ?
955 biome->c_dungeon_stair : biome->c_dungeon;
956 // Fallback to using cobble mapgen alias if defined
957 } else if (c_cobble != CONTENT_IGNORE) {
958 dp.c_wall = c_cobble;
959 dp.c_alt_wall = CONTENT_IGNORE;
960 dp.c_stair = c_cobble;
961 // Fallback to using biome-defined stone
963 dp.c_wall = biome->c_stone;
964 dp.c_alt_wall = CONTENT_IGNORE;
965 dp.c_stair = biome->c_stone;
968 DungeonGen dgen(ndef, &gennotify, &dp);
969 dgen.generate(vm, blockseed, full_node_min, full_node_max);
974 //// GenerateNotifier
977 GenerateNotifier::GenerateNotifier(u32 notify_on,
978 std::set<u32> *notify_on_deco_ids)
980 m_notify_on = notify_on;
981 m_notify_on_deco_ids = notify_on_deco_ids;
985 void GenerateNotifier::setNotifyOn(u32 notify_on)
987 m_notify_on = notify_on;
991 void GenerateNotifier::setNotifyOnDecoIds(std::set<u32> *notify_on_deco_ids)
993 m_notify_on_deco_ids = notify_on_deco_ids;
997 bool GenerateNotifier::addEvent(GenNotifyType type, v3s16 pos, u32 id)
999 if (!(m_notify_on & (1 << type)))
1002 if (type == GENNOTIFY_DECORATION &&
1003 m_notify_on_deco_ids->find(id) == m_notify_on_deco_ids->end())
1010 m_notify_events.push_back(gne);
1016 void GenerateNotifier::getEvents(
1017 std::map<std::string, std::vector<v3s16> > &event_map)
1019 std::list<GenNotifyEvent>::iterator it;
1021 for (it = m_notify_events.begin(); it != m_notify_events.end(); ++it) {
1022 GenNotifyEvent &gn = *it;
1023 std::string name = (gn.type == GENNOTIFY_DECORATION) ?
1024 "decoration#"+ itos(gn.id) :
1025 flagdesc_gennotify[gn.type].name;
1027 event_map[name].push_back(gn.pos);
1032 void GenerateNotifier::clearEvents()
1034 m_notify_events.clear();
1043 MapgenParams::~MapgenParams()
1049 void MapgenParams::readParams(const Settings *settings)
1051 std::string seed_str;
1052 const char *seed_name = (settings == g_settings) ? "fixed_map_seed" : "seed";
1054 if (settings->getNoEx(seed_name, seed_str)) {
1055 if (!seed_str.empty())
1056 seed = read_seed(seed_str.c_str());
1058 myrand_bytes(&seed, sizeof(seed));
1061 std::string mg_name;
1062 if (settings->getNoEx("mg_name", mg_name)) {
1063 mgtype = Mapgen::getMapgenType(mg_name);
1064 if (mgtype == MAPGEN_INVALID)
1065 mgtype = MAPGEN_DEFAULT;
1068 settings->getS16NoEx("water_level", water_level);
1069 settings->getS16NoEx("mapgen_limit", mapgen_limit);
1070 settings->getS16NoEx("chunksize", chunksize);
1071 settings->getFlagStrNoEx("mg_flags", flags, flagdesc_mapgen);
1074 bparams = BiomeManager::createBiomeParams(BIOMEGEN_ORIGINAL);
1076 bparams->readParams(settings);
1077 bparams->seed = seed;
1082 void MapgenParams::writeParams(Settings *settings) const
1084 settings->set("mg_name", Mapgen::getMapgenName(mgtype));
1085 settings->setU64("seed", seed);
1086 settings->setS16("water_level", water_level);
1087 settings->setS16("mapgen_limit", mapgen_limit);
1088 settings->setS16("chunksize", chunksize);
1089 settings->setFlagStr("mg_flags", flags, flagdesc_mapgen);
1092 bparams->writeParams(settings);
1096 // Calculate exact edges of the outermost mapchunks that are within the
1097 // set 'mapgen_limit'.
1098 void MapgenParams::calcMapgenEdges()
1100 // Central chunk offset, in blocks
1101 s16 ccoff_b = -chunksize / 2;
1102 // Chunksize, in nodes
1103 s32 csize_n = chunksize * MAP_BLOCKSIZE;
1104 // Minp/maxp of central chunk, in nodes
1105 s16 ccmin = ccoff_b * MAP_BLOCKSIZE;
1106 s16 ccmax = ccmin + csize_n - 1;
1107 // Fullminp/fullmaxp of central chunk, in nodes
1108 s16 ccfmin = ccmin - MAP_BLOCKSIZE;
1109 s16 ccfmax = ccmax + MAP_BLOCKSIZE;
1110 // Effective mapgen limit, in blocks
1111 // Uses same calculation as ServerMap::blockpos_over_mapgen_limit(v3s16 p)
1112 s16 mapgen_limit_b = rangelim(mapgen_limit,
1113 0, MAX_MAP_GENERATION_LIMIT) / MAP_BLOCKSIZE;
1114 // Effective mapgen limits, in nodes
1115 s16 mapgen_limit_min = -mapgen_limit_b * MAP_BLOCKSIZE;
1116 s16 mapgen_limit_max = (mapgen_limit_b + 1) * MAP_BLOCKSIZE - 1;
1117 // Number of complete chunks from central chunk fullminp/fullmaxp
1118 // to effective mapgen limits.
1119 s16 numcmin = MYMAX((ccfmin - mapgen_limit_min) / csize_n, 0);
1120 s16 numcmax = MYMAX((mapgen_limit_max - ccfmax) / csize_n, 0);
1121 // Mapgen edges, in nodes
1122 mapgen_edge_min = ccmin - numcmin * csize_n;
1123 mapgen_edge_max = ccmax + numcmax * csize_n;
1125 m_mapgen_edges_calculated = true;
1129 s32 MapgenParams::getSpawnRangeMax()
1131 if (!m_mapgen_edges_calculated)
1134 return MYMIN(-mapgen_edge_min, mapgen_edge_max);