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"
44 #include "mapgen_carpathian.h"
45 #include "mapgen_flat.h"
46 #include "mapgen_fractal.h"
47 #include "mapgen_v5.h"
48 #include "mapgen_v6.h"
49 #include "mapgen_v7.h"
50 #include "mapgen_valleys.h"
51 #include "mapgen_singlenode.h"
53 #include "dungeongen.h"
55 FlagDesc flagdesc_mapgen[] = {
57 {"dungeons", MG_DUNGEONS},
59 {"decorations", MG_DECORATIONS},
60 {"biomes", MG_BIOMES},
64 FlagDesc flagdesc_gennotify[] = {
65 {"dungeon", 1 << GENNOTIFY_DUNGEON},
66 {"temple", 1 << GENNOTIFY_TEMPLE},
67 {"cave_begin", 1 << GENNOTIFY_CAVE_BEGIN},
68 {"cave_end", 1 << GENNOTIFY_CAVE_END},
69 {"large_cave_begin", 1 << GENNOTIFY_LARGECAVE_BEGIN},
70 {"large_cave_end", 1 << GENNOTIFY_LARGECAVE_END},
71 {"decoration", 1 << GENNOTIFY_DECORATION},
84 // Order used here defines the order of appearence in mainmenu.
85 // v6 always last to discourage selection.
86 // Special mapgens flat, fractal, singlenode, next to last. Of these, singlenode
87 // last to discourage selection.
88 // Of the remaining, v5 last due to age, v7 first due to being the default.
89 // The order of 'enum MapgenType' in mapgen.h must match this order.
90 static MapgenDesc g_reg_mapgens[] = {
102 ARRLEN(g_reg_mapgens) == MAPGEN_INVALID,
103 registered_mapgens_is_wrong_size);
109 Mapgen::Mapgen(int mapgenid, MapgenParams *params, EmergeManager *emerge) :
110 gennotify(emerge->gen_notify_on, &emerge->gen_notify_on_deco_ids)
113 water_level = params->water_level;
114 mapgen_limit = params->mapgen_limit;
115 flags = params->flags;
116 csize = v3s16(1, 1, 1) * (params->chunksize * MAP_BLOCKSIZE);
119 We are losing half our entropy by doing this, but it is necessary to
120 preserve reverse compatibility. If the top half of our current 64 bit
121 seeds ever starts getting used, existing worlds will break due to a
122 different hash outcome and no way to differentiate between versions.
124 A solution could be to add a new bit to designate that the top half of
125 the seed value should be used, essentially a 1-bit version code, but
126 this would require increasing the total size of a seed to 9 bytes (yuck)
128 It's probably okay if this never gets fixed. 4.2 billion possibilities
129 ought to be enough for anyone.
131 seed = (s32)params->seed;
137 MapgenType Mapgen::getMapgenType(const std::string &mgname)
139 for (size_t i = 0; i != ARRLEN(g_reg_mapgens); i++) {
140 if (mgname == g_reg_mapgens[i].name)
141 return (MapgenType)i;
144 return MAPGEN_INVALID;
148 const char *Mapgen::getMapgenName(MapgenType mgtype)
150 size_t index = (size_t)mgtype;
151 if (index == MAPGEN_INVALID || index >= ARRLEN(g_reg_mapgens))
154 return g_reg_mapgens[index].name;
158 Mapgen *Mapgen::createMapgen(MapgenType mgtype, MapgenParams *params,
159 EmergeManager *emerge)
162 case MAPGEN_CARPATHIAN:
163 return new MapgenCarpathian((MapgenCarpathianParams *)params, emerge);
165 return new MapgenFlat((MapgenFlatParams *)params, emerge);
167 return new MapgenFractal((MapgenFractalParams *)params, emerge);
168 case MAPGEN_SINGLENODE:
169 return new MapgenSinglenode((MapgenSinglenodeParams *)params, emerge);
171 return new MapgenV5((MapgenV5Params *)params, emerge);
173 return new MapgenV6((MapgenV6Params *)params, emerge);
175 return new MapgenV7((MapgenV7Params *)params, emerge);
177 return new MapgenValleys((MapgenValleysParams *)params, emerge);
184 MapgenParams *Mapgen::createMapgenParams(MapgenType mgtype)
187 case MAPGEN_CARPATHIAN:
188 return new MapgenCarpathianParams;
190 return new MapgenFlatParams;
192 return new MapgenFractalParams;
193 case MAPGEN_SINGLENODE:
194 return new MapgenSinglenodeParams;
196 return new MapgenV5Params;
198 return new MapgenV6Params;
200 return new MapgenV7Params;
202 return new MapgenValleysParams;
209 void Mapgen::getMapgenNames(std::vector<const char *> *mgnames, bool include_hidden)
211 for (u32 i = 0; i != ARRLEN(g_reg_mapgens); i++) {
212 if (include_hidden || g_reg_mapgens[i].is_user_visible)
213 mgnames->push_back(g_reg_mapgens[i].name);
218 u32 Mapgen::getBlockSeed(v3s16 p, s32 seed)
227 u32 Mapgen::getBlockSeed2(v3s16 p, s32 seed)
229 u32 n = 1619 * p.X + 31337 * p.Y + 52591 * p.Z + 1013 * seed;
231 return (n * (n * n * 60493 + 19990303) + 1376312589);
235 // Returns Y one under area minimum if not found
236 s16 Mapgen::findGroundLevelFull(v2s16 p2d)
238 const v3s16 &em = vm->m_area.getExtent();
239 s16 y_nodes_max = vm->m_area.MaxEdge.Y;
240 s16 y_nodes_min = vm->m_area.MinEdge.Y;
241 u32 i = vm->m_area.index(p2d.X, y_nodes_max, p2d.Y);
244 for (y = y_nodes_max; y >= y_nodes_min; y--) {
245 MapNode &n = vm->m_data[i];
246 if (ndef->get(n).walkable)
249 VoxelArea::add_y(em, i, -1);
251 return (y >= y_nodes_min) ? y : y_nodes_min - 1;
255 // Returns -MAX_MAP_GENERATION_LIMIT if not found
256 s16 Mapgen::findGroundLevel(v2s16 p2d, s16 ymin, s16 ymax)
258 const v3s16 &em = vm->m_area.getExtent();
259 u32 i = vm->m_area.index(p2d.X, ymax, p2d.Y);
262 for (y = ymax; y >= ymin; y--) {
263 MapNode &n = vm->m_data[i];
264 if (ndef->get(n).walkable)
267 VoxelArea::add_y(em, i, -1);
269 return (y >= ymin) ? y : -MAX_MAP_GENERATION_LIMIT;
273 // Returns -MAX_MAP_GENERATION_LIMIT if not found or if ground is found first
274 s16 Mapgen::findLiquidSurface(v2s16 p2d, s16 ymin, s16 ymax)
276 const v3s16 &em = vm->m_area.getExtent();
277 u32 i = vm->m_area.index(p2d.X, ymax, p2d.Y);
280 for (y = ymax; y >= ymin; y--) {
281 MapNode &n = vm->m_data[i];
282 if (ndef->get(n).walkable)
283 return -MAX_MAP_GENERATION_LIMIT;
285 if (ndef->get(n).isLiquid())
288 VoxelArea::add_y(em, i, -1);
290 return (y >= ymin) ? y : -MAX_MAP_GENERATION_LIMIT;
294 void Mapgen::updateHeightmap(v3s16 nmin, v3s16 nmax)
299 //TimeTaker t("Mapgen::updateHeightmap", NULL, PRECISION_MICRO);
301 for (s16 z = nmin.Z; z <= nmax.Z; z++) {
302 for (s16 x = nmin.X; x <= nmax.X; x++, index++) {
303 s16 y = findGroundLevel(v2s16(x, z), nmin.Y, nmax.Y);
305 heightmap[index] = y;
311 void Mapgen::getSurfaces(v2s16 p2d, s16 ymin, s16 ymax,
312 std::vector<s16> &floors, std::vector<s16> &ceilings)
314 const v3s16 &em = vm->m_area.getExtent();
316 bool is_walkable = false;
317 u32 vi = vm->m_area.index(p2d.X, ymax, p2d.Y);
318 MapNode mn_max = vm->m_data[vi];
319 bool walkable_above = ndef->get(mn_max).walkable;
320 VoxelArea::add_y(em, vi, -1);
322 for (s16 y = ymax - 1; y >= ymin; y--) {
323 MapNode mn = vm->m_data[vi];
324 is_walkable = ndef->get(mn).walkable;
326 if (is_walkable && !walkable_above) {
328 } else if (!is_walkable && walkable_above) {
329 ceilings.push_back(y + 1);
332 VoxelArea::add_y(em, vi, -1);
333 walkable_above = is_walkable;
338 inline bool Mapgen::isLiquidHorizontallyFlowable(u32 vi, v3s16 em)
341 VoxelArea::add_x(em, vi_neg_x, -1);
342 if (vm->m_data[vi_neg_x].getContent() != CONTENT_IGNORE) {
343 const ContentFeatures &c_nx = ndef->get(vm->m_data[vi_neg_x]);
344 if (c_nx.floodable && !c_nx.isLiquid())
348 VoxelArea::add_x(em, vi_pos_x, +1);
349 if (vm->m_data[vi_pos_x].getContent() != CONTENT_IGNORE) {
350 const ContentFeatures &c_px = ndef->get(vm->m_data[vi_pos_x]);
351 if (c_px.floodable && !c_px.isLiquid())
355 VoxelArea::add_z(em, vi_neg_z, -1);
356 if (vm->m_data[vi_neg_z].getContent() != CONTENT_IGNORE) {
357 const ContentFeatures &c_nz = ndef->get(vm->m_data[vi_neg_z]);
358 if (c_nz.floodable && !c_nz.isLiquid())
362 VoxelArea::add_z(em, vi_pos_z, +1);
363 if (vm->m_data[vi_pos_z].getContent() != CONTENT_IGNORE) {
364 const ContentFeatures &c_pz = ndef->get(vm->m_data[vi_pos_z]);
365 if (c_pz.floodable && !c_pz.isLiquid())
371 void Mapgen::updateLiquid(UniqueQueue<v3s16> *trans_liquid, v3s16 nmin, v3s16 nmax)
373 bool isignored, isliquid, wasignored, wasliquid, waschecked, waspushed;
374 const v3s16 &em = vm->m_area.getExtent();
376 for (s16 z = nmin.Z + 1; z <= nmax.Z - 1; z++)
377 for (s16 x = nmin.X + 1; x <= nmax.X - 1; x++) {
383 u32 vi = vm->m_area.index(x, nmax.Y, z);
384 for (s16 y = nmax.Y; y >= nmin.Y; y--) {
385 isignored = vm->m_data[vi].getContent() == CONTENT_IGNORE;
386 isliquid = ndef->get(vm->m_data[vi]).isLiquid();
388 if (isignored || wasignored || isliquid == wasliquid) {
389 // Neither topmost node of liquid column nor topmost node below column
392 } else if (isliquid) {
393 // This is the topmost node in the column
394 bool ispushed = false;
395 if (isLiquidHorizontallyFlowable(vi, em)) {
396 trans_liquid->push_back(v3s16(x, y, z));
399 // Remember waschecked and waspushed to avoid repeated
400 // checks/pushes in case the column consists of only this node
402 waspushed = ispushed;
404 // This is the topmost node below a liquid column
406 VoxelArea::add_y(em, vi_above, 1);
407 if (!waspushed && (ndef->get(vm->m_data[vi]).floodable ||
408 (!waschecked && isLiquidHorizontallyFlowable(vi_above, em)))) {
409 // Push back the lowest node in the column which is one
410 // node above this one
411 trans_liquid->push_back(v3s16(x, y + 1, z));
415 wasliquid = isliquid;
416 wasignored = isignored;
417 VoxelArea::add_y(em, vi, -1);
423 void Mapgen::setLighting(u8 light, v3s16 nmin, v3s16 nmax)
425 ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG);
426 VoxelArea a(nmin, nmax);
428 for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) {
429 for (int y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) {
430 u32 i = vm->m_area.index(a.MinEdge.X, y, z);
431 for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++, i++)
432 vm->m_data[i].param1 = light;
438 void Mapgen::lightSpread(VoxelArea &a, v3s16 p, u8 light)
440 if (light <= 1 || !a.contains(p))
443 u32 vi = vm->m_area.index(p);
444 MapNode &n = vm->m_data[vi];
446 // Decay light in each of the banks separately
447 u8 light_day = light & 0x0F;
451 u8 light_night = light & 0xF0;
455 // Bail out only if we have no more light from either bank to propogate, or
456 // we hit a solid block that light cannot pass through.
457 if ((light_day <= (n.param1 & 0x0F) &&
458 light_night <= (n.param1 & 0xF0)) ||
459 !ndef->get(n).light_propagates)
462 // Since this recursive function only terminates when there is no light from
463 // either bank left, we need to take the max of both banks into account for
464 // the case where spreading has stopped for one light bank but not the other.
465 light = MYMAX(light_day, n.param1 & 0x0F) |
466 MYMAX(light_night, n.param1 & 0xF0);
470 lightSpread(a, p + v3s16(0, 0, 1), light);
471 lightSpread(a, p + v3s16(0, 1, 0), light);
472 lightSpread(a, p + v3s16(1, 0, 0), light);
473 lightSpread(a, p - v3s16(0, 0, 1), light);
474 lightSpread(a, p - v3s16(0, 1, 0), light);
475 lightSpread(a, p - v3s16(1, 0, 0), light);
479 void Mapgen::calcLighting(v3s16 nmin, v3s16 nmax, v3s16 full_nmin, v3s16 full_nmax,
480 bool propagate_shadow)
482 ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG);
483 //TimeTaker t("updateLighting");
485 propagateSunlight(nmin, nmax, propagate_shadow);
486 spreadLight(full_nmin, full_nmax);
488 //printf("updateLighting: %dms\n", t.stop());
492 void Mapgen::propagateSunlight(v3s16 nmin, v3s16 nmax, bool propagate_shadow)
494 //TimeTaker t("propagateSunlight");
495 VoxelArea a(nmin, nmax);
496 bool block_is_underground = (water_level >= nmax.Y);
497 const v3s16 &em = vm->m_area.getExtent();
499 // NOTE: Direct access to the low 4 bits of param1 is okay here because,
500 // by definition, sunlight will never be in the night lightbank.
502 for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) {
503 for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++) {
504 // see if we can get a light value from the overtop
505 u32 i = vm->m_area.index(x, a.MaxEdge.Y + 1, z);
506 if (vm->m_data[i].getContent() == CONTENT_IGNORE) {
507 if (block_is_underground)
509 } else if ((vm->m_data[i].param1 & 0x0F) != LIGHT_SUN &&
513 VoxelArea::add_y(em, i, -1);
515 for (int y = a.MaxEdge.Y; y >= a.MinEdge.Y; y--) {
516 MapNode &n = vm->m_data[i];
517 if (!ndef->get(n).sunlight_propagates)
519 n.param1 = LIGHT_SUN;
520 VoxelArea::add_y(em, i, -1);
524 //printf("propagateSunlight: %dms\n", t.stop());
528 void Mapgen::spreadLight(v3s16 nmin, v3s16 nmax)
530 //TimeTaker t("spreadLight");
531 VoxelArea a(nmin, nmax);
533 for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) {
534 for (int y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) {
535 u32 i = vm->m_area.index(a.MinEdge.X, y, z);
536 for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++, i++) {
537 MapNode &n = vm->m_data[i];
538 if (n.getContent() == CONTENT_IGNORE)
541 const ContentFeatures &cf = ndef->get(n);
542 if (!cf.light_propagates)
545 // TODO(hmmmmm): Abstract away direct param1 accesses with a
546 // wrapper, but something lighter than MapNode::get/setLight
548 u8 light_produced = cf.light_source;
550 n.param1 = light_produced | (light_produced << 4);
554 lightSpread(a, v3s16(x, y, z + 1), light);
555 lightSpread(a, v3s16(x, y + 1, z ), light);
556 lightSpread(a, v3s16(x + 1, y, z ), light);
557 lightSpread(a, v3s16(x, y, z - 1), light);
558 lightSpread(a, v3s16(x, y - 1, z ), light);
559 lightSpread(a, v3s16(x - 1, y, z ), light);
565 //printf("spreadLight: %dms\n", t.stop());
573 MapgenBasic::MapgenBasic(int mapgenid, MapgenParams *params, EmergeManager *emerge)
574 : Mapgen(mapgenid, params, emerge)
576 this->m_emerge = emerge;
577 this->m_bmgr = emerge->biomemgr;
579 //// Here, 'stride' refers to the number of elements needed to skip to index
580 //// an adjacent element for that coordinate in noise/height/biome maps
581 //// (*not* vmanip content map!)
583 // Note there is no X stride explicitly defined. Items adjacent in the X
584 // coordinate are assumed to be adjacent in memory as well (i.e. stride of 1).
586 // Number of elements to skip to get to the next Y coordinate
587 this->ystride = csize.X;
589 // Number of elements to skip to get to the next Z coordinate
590 this->zstride = csize.X * csize.Y;
592 // Z-stride value for maps oversized for 1-down overgeneration
593 this->zstride_1d = csize.X * (csize.Y + 1);
595 // Z-stride value for maps oversized for 1-up 1-down overgeneration
596 this->zstride_1u1d = csize.X * (csize.Y + 2);
598 //// Allocate heightmap
599 this->heightmap = new s16[csize.X * csize.Z];
601 //// Initialize biome generator
602 biomegen = m_bmgr->createBiomeGen(BIOMEGEN_ORIGINAL, params->bparams, csize);
603 biomemap = biomegen->biomemap;
605 //// Look up some commonly used content
606 c_stone = ndef->getId("mapgen_stone");
607 c_water_source = ndef->getId("mapgen_water_source");
608 c_river_water_source = ndef->getId("mapgen_river_water_source");
609 c_lava_source = ndef->getId("mapgen_lava_source");
610 c_cobble = ndef->getId("mapgen_cobble");
612 // Fall back to more basic content if not defined.
613 // Lava falls back to water as both are suitable as cave liquids.
614 if (c_lava_source == CONTENT_IGNORE)
615 c_lava_source = c_water_source;
619 MapgenBasic::~MapgenBasic()
626 void MapgenBasic::generateBiomes()
628 // can't generate biomes without a biome generator!
632 const v3s16 &em = vm->m_area.getExtent();
635 noise_filler_depth->perlinMap2D(node_min.X, node_min.Z);
637 for (s16 z = node_min.Z; z <= node_max.Z; z++)
638 for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
640 biome_t water_biome_index = 0;
643 u16 depth_water_top = 0;
644 u16 depth_riverbed = 0;
645 s16 biome_y_min = -MAX_MAP_GENERATION_LIMIT;
646 u32 vi = vm->m_area.index(x, node_max.Y, z);
648 // Check node at base of mapchunk above, either a node of a previously
649 // generated mapchunk or if not, a node of overgenerated base terrain.
650 content_t c_above = vm->m_data[vi + em.X].getContent();
651 bool air_above = c_above == CONTENT_AIR;
652 bool river_water_above = c_above == c_river_water_source;
653 bool water_above = c_above == c_water_source || river_water_above;
655 biomemap[index] = BIOME_NONE;
657 // If there is air or water above enable top/filler placement, otherwise force
658 // nplaced to stone level by setting a number exceeding any possible filler depth.
659 u16 nplaced = (air_above || water_above) ? 0 : U16_MAX;
661 for (s16 y = node_max.Y; y >= node_min.Y; y--) {
662 content_t c = vm->m_data[vi].getContent();
663 // Biome is (re)calculated:
664 // 1. At the surface of stone below air or water.
665 // 2. At the surface of water below air.
666 // 3. When stone or water is detected but biome has not yet been calculated.
667 // 4. When stone or water is detected just below a biome's lower limit.
668 bool is_stone_surface = (c == c_stone) &&
669 (air_above || water_above || !biome || y < biome_y_min); // 1, 3, 4
671 bool is_water_surface =
672 (c == c_water_source || c == c_river_water_source) &&
673 (air_above || !biome || y < biome_y_min); // 2, 3, 4
675 if (is_stone_surface || is_water_surface) {
676 // (Re)calculate biome
677 biome = biomegen->getBiomeAtIndex(index, v3s16(x, y, z));
679 // Add biome to biomemap at first stone surface detected
680 if (biomemap[index] == BIOME_NONE && is_stone_surface)
681 biomemap[index] = biome->index;
683 // Store biome of first water surface detected, as a fallback
684 // entry for the biomemap.
685 if (water_biome_index == 0 && is_water_surface)
686 water_biome_index = biome->index;
688 depth_top = biome->depth_top;
689 base_filler = MYMAX(depth_top +
690 biome->depth_filler +
691 noise_filler_depth->result[index], 0.0f);
692 depth_water_top = biome->depth_water_top;
693 depth_riverbed = biome->depth_riverbed;
694 biome_y_min = biome->min_pos.Y;
698 content_t c_below = vm->m_data[vi - em.X].getContent();
700 // If the node below isn't solid, make this node stone, so that
701 // any top/filler nodes above are structurally supported.
702 // This is done by aborting the cycle of top/filler placement
703 // immediately by forcing nplaced to stone level.
704 if (c_below == CONTENT_AIR
705 || c_below == c_water_source
706 || c_below == c_river_water_source)
709 if (river_water_above) {
710 if (nplaced < depth_riverbed) {
711 vm->m_data[vi] = MapNode(biome->c_riverbed);
714 nplaced = U16_MAX; // Disable top/filler placement
715 river_water_above = false;
717 } else if (nplaced < depth_top) {
718 vm->m_data[vi] = MapNode(biome->c_top);
720 } else if (nplaced < base_filler) {
721 vm->m_data[vi] = MapNode(biome->c_filler);
724 vm->m_data[vi] = MapNode(biome->c_stone);
725 nplaced = U16_MAX; // Disable top/filler placement
730 } else if (c == c_water_source) {
731 vm->m_data[vi] = MapNode((y > (s32)(water_level - depth_water_top))
732 ? biome->c_water_top : biome->c_water);
733 nplaced = 0; // Enable top/filler placement for next surface
736 } else if (c == c_river_water_source) {
737 vm->m_data[vi] = MapNode(biome->c_river_water);
738 nplaced = 0; // Enable riverbed placement for next surface
741 river_water_above = true;
742 } else if (c == CONTENT_AIR) {
743 nplaced = 0; // Enable top/filler placement for next surface
746 } else { // Possible various nodes overgenerated from neighbouring mapchunks
747 nplaced = U16_MAX; // Disable top/filler placement
752 VoxelArea::add_y(em, vi, -1);
754 // If no stone surface detected in mapchunk column and a water surface
755 // biome fallback exists, add it to the biomemap. This avoids water
756 // surface decorations failing in deep water.
757 if (biomemap[index] == BIOME_NONE && water_biome_index != 0)
758 biomemap[index] = water_biome_index;
763 void MapgenBasic::dustTopNodes()
765 if (node_max.Y < water_level)
768 const v3s16 &em = vm->m_area.getExtent();
771 for (s16 z = node_min.Z; z <= node_max.Z; z++)
772 for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
773 Biome *biome = (Biome *)m_bmgr->getRaw(biomemap[index]);
775 if (biome->c_dust == CONTENT_IGNORE)
778 // Check if mapchunk above has generated, if so, drop dust from 16 nodes
779 // above current mapchunk top, above decorations that will extend above
780 // the current mapchunk. If the mapchunk above has not generated, it
781 // will provide this required dust when it does.
782 u32 vi = vm->m_area.index(x, full_node_max.Y, z);
783 content_t c_full_max = vm->m_data[vi].getContent();
786 if (c_full_max == CONTENT_AIR) {
787 y_start = full_node_max.Y - 1;
788 } else if (c_full_max == CONTENT_IGNORE) {
789 vi = vm->m_area.index(x, node_max.Y + 1, z);
790 content_t c_max = vm->m_data[vi].getContent();
792 if (c_max == CONTENT_AIR)
793 y_start = node_max.Y;
800 vi = vm->m_area.index(x, y_start, z);
801 for (s16 y = y_start; y >= node_min.Y - 1; y--) {
802 if (vm->m_data[vi].getContent() != CONTENT_AIR)
805 VoxelArea::add_y(em, vi, -1);
808 content_t c = vm->m_data[vi].getContent();
809 NodeDrawType dtype = ndef->get(c).drawtype;
810 // Only place on cubic, walkable, non-dust nodes.
811 // Dust check needed due to avoid double layer of dust caused by
812 // dropping dust from 16 nodes above mapchunk top.
813 if ((dtype == NDT_NORMAL ||
814 dtype == NDT_ALLFACES ||
815 dtype == NDT_ALLFACES_OPTIONAL ||
816 dtype == NDT_GLASSLIKE ||
817 dtype == NDT_GLASSLIKE_FRAMED ||
818 dtype == NDT_GLASSLIKE_FRAMED_OPTIONAL) &&
819 ndef->get(c).walkable && c != biome->c_dust) {
820 VoxelArea::add_y(em, vi, 1);
821 vm->m_data[vi] = MapNode(biome->c_dust);
827 void MapgenBasic::generateCavesNoiseIntersection(s16 max_stone_y)
829 if (node_min.Y > max_stone_y)
832 CavesNoiseIntersection caves_noise(ndef, m_bmgr, csize,
833 &np_cave1, &np_cave2, seed, cave_width);
835 caves_noise.generateCaves(vm, node_min, node_max, biomemap);
839 void MapgenBasic::generateCavesRandomWalk(s16 max_stone_y, s16 large_cave_depth)
841 if (node_min.Y > max_stone_y || node_max.Y > large_cave_depth)
844 PseudoRandom ps(blockseed + 21343);
845 u32 bruises_count = ps.range(0, 2);
847 for (u32 i = 0; i < bruises_count; i++) {
848 CavesRandomWalk cave(ndef, &gennotify, seed, water_level,
849 c_water_source, c_lava_source, lava_depth, biomegen);
851 cave.makeCave(vm, node_min, node_max, &ps, true, max_stone_y,
857 bool MapgenBasic::generateCavernsNoise(s16 max_stone_y)
859 if (node_min.Y > max_stone_y || node_min.Y > cavern_limit)
862 CavernsNoise caverns_noise(ndef, csize, &np_cavern,
863 seed, cavern_limit, cavern_taper, cavern_threshold);
865 return caverns_noise.generateCaverns(vm, node_min, node_max);
869 void MapgenBasic::generateDungeons(s16 max_stone_y)
871 if (max_stone_y < node_min.Y)
874 u16 num_dungeons = std::fmax(std::floor(
875 NoisePerlin3D(&np_dungeons, node_min.X, node_min.Y, node_min.Z, seed)), 0.0f);
876 if (num_dungeons == 0)
879 PseudoRandom ps(blockseed + 70033);
884 NoiseParams(-0.4, 1.0, v3f(40.0, 40.0, 40.0), 32474, 6, 1.1, 2.0);
887 dp.num_dungeons = num_dungeons;
888 dp.only_in_ground = true;
889 dp.num_rooms = ps.range(2, 16);
890 dp.room_size_min = v3s16(6, 5, 6);
891 dp.room_size_max = v3s16(10, 6, 10);
892 dp.room_size_large_min = v3s16(10, 8, 10);
893 dp.room_size_large_max = v3s16(18, 16, 18);
894 dp.large_room_chance = (ps.range(1, 4) == 1) ? 1 : 0;
895 dp.holesize = v3s16(2, 3, 2);
896 dp.corridor_len_min = 1;
897 dp.corridor_len_max = 13;
898 dp.diagonal_dirs = ps.range(1, 12) == 1;
899 dp.notifytype = GENNOTIFY_DUNGEON;
901 // Get biome at mapchunk midpoint
902 v3s16 chunk_mid = node_min + (node_max - node_min) / v3s16(2, 2, 2);
903 Biome *biome = (Biome *)biomegen->getBiomeAtPoint(chunk_mid);
905 // Use biome-defined dungeon nodes if defined
906 if (biome->c_dungeon != CONTENT_IGNORE) {
907 dp.c_wall = biome->c_dungeon;
908 // If 'node_dungeon_alt' is not defined by biome, it and dp.c_alt_wall
909 // become CONTENT_IGNORE which skips the alt wall node placement loop in
911 dp.c_alt_wall = biome->c_dungeon_alt;
912 // Stairs fall back to 'c_dungeon' if not defined by biome
913 dp.c_stair = (biome->c_dungeon_stair != CONTENT_IGNORE) ?
914 biome->c_dungeon_stair : biome->c_dungeon;
915 // Fallback to using cobble mapgen alias if defined
916 } else if (c_cobble != CONTENT_IGNORE) {
917 dp.c_wall = c_cobble;
918 dp.c_alt_wall = CONTENT_IGNORE;
919 dp.c_stair = c_cobble;
920 // Fallback to using biome-defined stone
922 dp.c_wall = biome->c_stone;
923 dp.c_alt_wall = CONTENT_IGNORE;
924 dp.c_stair = biome->c_stone;
927 DungeonGen dgen(ndef, &gennotify, &dp);
928 dgen.generate(vm, blockseed, full_node_min, full_node_max);
933 //// GenerateNotifier
936 GenerateNotifier::GenerateNotifier(u32 notify_on,
937 std::set<u32> *notify_on_deco_ids)
939 m_notify_on = notify_on;
940 m_notify_on_deco_ids = notify_on_deco_ids;
944 void GenerateNotifier::setNotifyOn(u32 notify_on)
946 m_notify_on = notify_on;
950 void GenerateNotifier::setNotifyOnDecoIds(std::set<u32> *notify_on_deco_ids)
952 m_notify_on_deco_ids = notify_on_deco_ids;
956 bool GenerateNotifier::addEvent(GenNotifyType type, v3s16 pos, u32 id)
958 if (!(m_notify_on & (1 << type)))
961 if (type == GENNOTIFY_DECORATION &&
962 m_notify_on_deco_ids->find(id) == m_notify_on_deco_ids->end())
969 m_notify_events.push_back(gne);
975 void GenerateNotifier::getEvents(
976 std::map<std::string, std::vector<v3s16> > &event_map)
978 std::list<GenNotifyEvent>::iterator it;
980 for (it = m_notify_events.begin(); it != m_notify_events.end(); ++it) {
981 GenNotifyEvent &gn = *it;
982 std::string name = (gn.type == GENNOTIFY_DECORATION) ?
983 "decoration#"+ itos(gn.id) :
984 flagdesc_gennotify[gn.type].name;
986 event_map[name].push_back(gn.pos);
991 void GenerateNotifier::clearEvents()
993 m_notify_events.clear();
1002 MapgenParams::~MapgenParams()
1008 void MapgenParams::readParams(const Settings *settings)
1010 std::string seed_str;
1011 const char *seed_name = (settings == g_settings) ? "fixed_map_seed" : "seed";
1013 if (settings->getNoEx(seed_name, seed_str)) {
1014 if (!seed_str.empty())
1015 seed = read_seed(seed_str.c_str());
1017 myrand_bytes(&seed, sizeof(seed));
1020 std::string mg_name;
1021 if (settings->getNoEx("mg_name", mg_name)) {
1022 mgtype = Mapgen::getMapgenType(mg_name);
1023 if (mgtype == MAPGEN_INVALID)
1024 mgtype = MAPGEN_DEFAULT;
1027 settings->getS16NoEx("water_level", water_level);
1028 settings->getS16NoEx("mapgen_limit", mapgen_limit);
1029 settings->getS16NoEx("chunksize", chunksize);
1030 settings->getFlagStrNoEx("mg_flags", flags, flagdesc_mapgen);
1033 bparams = BiomeManager::createBiomeParams(BIOMEGEN_ORIGINAL);
1035 bparams->readParams(settings);
1036 bparams->seed = seed;
1041 void MapgenParams::writeParams(Settings *settings) const
1043 settings->set("mg_name", Mapgen::getMapgenName(mgtype));
1044 settings->setU64("seed", seed);
1045 settings->setS16("water_level", water_level);
1046 settings->setS16("mapgen_limit", mapgen_limit);
1047 settings->setS16("chunksize", chunksize);
1048 settings->setFlagStr("mg_flags", flags, flagdesc_mapgen, U32_MAX);
1051 bparams->writeParams(settings);
1055 // Calculate exact edges of the outermost mapchunks that are within the
1056 // set 'mapgen_limit'.
1057 void MapgenParams::calcMapgenEdges()
1059 // Central chunk offset, in blocks
1060 s16 ccoff_b = -chunksize / 2;
1061 // Chunksize, in nodes
1062 s32 csize_n = chunksize * MAP_BLOCKSIZE;
1063 // Minp/maxp of central chunk, in nodes
1064 s16 ccmin = ccoff_b * MAP_BLOCKSIZE;
1065 s16 ccmax = ccmin + csize_n - 1;
1066 // Fullminp/fullmaxp of central chunk, in nodes
1067 s16 ccfmin = ccmin - MAP_BLOCKSIZE;
1068 s16 ccfmax = ccmax + MAP_BLOCKSIZE;
1069 // Effective mapgen limit, in blocks
1070 // Uses same calculation as ServerMap::blockpos_over_mapgen_limit(v3s16 p)
1071 s16 mapgen_limit_b = rangelim(mapgen_limit,
1072 0, MAX_MAP_GENERATION_LIMIT) / MAP_BLOCKSIZE;
1073 // Effective mapgen limits, in nodes
1074 s16 mapgen_limit_min = -mapgen_limit_b * MAP_BLOCKSIZE;
1075 s16 mapgen_limit_max = (mapgen_limit_b + 1) * MAP_BLOCKSIZE - 1;
1076 // Number of complete chunks from central chunk fullminp/fullmaxp
1077 // to effective mapgen limits.
1078 s16 numcmin = MYMAX((ccfmin - mapgen_limit_min) / csize_n, 0);
1079 s16 numcmax = MYMAX((mapgen_limit_max - ccfmax) / csize_n, 0);
1080 // Mapgen edges, in nodes
1081 mapgen_edge_min = ccmin - numcmin * csize_n;
1082 mapgen_edge_max = ccmax + numcmax * csize_n;
1084 m_mapgen_edges_calculated = true;
1088 s32 MapgenParams::getSpawnRangeMax()
1090 if (!m_mapgen_edges_calculated)
1093 return MYMIN(-mapgen_edge_min, mapgen_edge_max);