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.
30 #include "content_sao.h"
33 #include "voxelalgorithms.h"
38 #include "serialization.h"
39 #include "util/serialize.h"
40 #include "util/numeric.h"
43 #include "mapgen_carpathian.h"
44 #include "mapgen_flat.h"
45 #include "mapgen_fractal.h"
46 #include "mapgen_v5.h"
47 #include "mapgen_v6.h"
48 #include "mapgen_v7.h"
49 #include "mapgen_valleys.h"
50 #include "mapgen_singlenode.h"
52 #include "dungeongen.h"
54 FlagDesc flagdesc_mapgen[] = {
56 {"dungeons", MG_DUNGEONS},
58 {"decorations", MG_DECORATIONS},
59 {"biomes", MG_BIOMES},
63 FlagDesc flagdesc_gennotify[] = {
64 {"dungeon", 1 << GENNOTIFY_DUNGEON},
65 {"temple", 1 << GENNOTIFY_TEMPLE},
66 {"cave_begin", 1 << GENNOTIFY_CAVE_BEGIN},
67 {"cave_end", 1 << GENNOTIFY_CAVE_END},
68 {"large_cave_begin", 1 << GENNOTIFY_LARGECAVE_BEGIN},
69 {"large_cave_end", 1 << GENNOTIFY_LARGECAVE_END},
70 {"decoration", 1 << GENNOTIFY_DECORATION},
83 static MapgenDesc g_reg_mapgens[] = {
95 ARRLEN(g_reg_mapgens) == MAPGEN_INVALID,
96 registered_mapgens_is_wrong_size);
102 Mapgen::Mapgen(int mapgenid, MapgenParams *params, EmergeManager *emerge) :
103 gennotify(emerge->gen_notify_on, &emerge->gen_notify_on_deco_ids)
106 water_level = params->water_level;
107 mapgen_limit = params->mapgen_limit;
108 flags = params->flags;
109 csize = v3s16(1, 1, 1) * (params->chunksize * MAP_BLOCKSIZE);
112 We are losing half our entropy by doing this, but it is necessary to
113 preserve reverse compatibility. If the top half of our current 64 bit
114 seeds ever starts getting used, existing worlds will break due to a
115 different hash outcome and no way to differentiate between versions.
117 A solution could be to add a new bit to designate that the top half of
118 the seed value should be used, essentially a 1-bit version code, but
119 this would require increasing the total size of a seed to 9 bytes (yuck)
121 It's probably okay if this never gets fixed. 4.2 billion possibilities
122 ought to be enough for anyone.
124 seed = (s32)params->seed;
130 MapgenType Mapgen::getMapgenType(const std::string &mgname)
132 for (size_t i = 0; i != ARRLEN(g_reg_mapgens); i++) {
133 if (mgname == g_reg_mapgens[i].name)
134 return (MapgenType)i;
137 return MAPGEN_INVALID;
141 const char *Mapgen::getMapgenName(MapgenType mgtype)
143 size_t index = (size_t)mgtype;
144 if (index == MAPGEN_INVALID || index >= ARRLEN(g_reg_mapgens))
147 return g_reg_mapgens[index].name;
151 Mapgen *Mapgen::createMapgen(MapgenType mgtype, MapgenParams *params,
152 EmergeManager *emerge)
155 case MAPGEN_CARPATHIAN:
156 return new MapgenCarpathian((MapgenCarpathianParams *)params, emerge);
158 return new MapgenFlat((MapgenFlatParams *)params, emerge);
160 return new MapgenFractal((MapgenFractalParams *)params, emerge);
161 case MAPGEN_SINGLENODE:
162 return new MapgenSinglenode((MapgenSinglenodeParams *)params, emerge);
164 return new MapgenV5((MapgenV5Params *)params, emerge);
166 return new MapgenV6((MapgenV6Params *)params, emerge);
168 return new MapgenV7((MapgenV7Params *)params, emerge);
170 return new MapgenValleys((MapgenValleysParams *)params, emerge);
177 MapgenParams *Mapgen::createMapgenParams(MapgenType mgtype)
180 case MAPGEN_CARPATHIAN:
181 return new MapgenCarpathianParams;
183 return new MapgenFlatParams;
185 return new MapgenFractalParams;
186 case MAPGEN_SINGLENODE:
187 return new MapgenSinglenodeParams;
189 return new MapgenV5Params;
191 return new MapgenV6Params;
193 return new MapgenV7Params;
195 return new MapgenValleysParams;
202 void Mapgen::getMapgenNames(std::vector<const char *> *mgnames, bool include_hidden)
204 for (u32 i = 0; i != ARRLEN(g_reg_mapgens); i++) {
205 if (include_hidden || g_reg_mapgens[i].is_user_visible)
206 mgnames->push_back(g_reg_mapgens[i].name);
211 u32 Mapgen::getBlockSeed(v3s16 p, s32 seed)
220 u32 Mapgen::getBlockSeed2(v3s16 p, s32 seed)
222 u32 n = 1619 * p.X + 31337 * p.Y + 52591 * p.Z + 1013 * seed;
224 return (n * (n * n * 60493 + 19990303) + 1376312589);
228 // Returns Y one under area minimum if not found
229 s16 Mapgen::findGroundLevelFull(v2s16 p2d)
231 const v3s16 &em = vm->m_area.getExtent();
232 s16 y_nodes_max = vm->m_area.MaxEdge.Y;
233 s16 y_nodes_min = vm->m_area.MinEdge.Y;
234 u32 i = vm->m_area.index(p2d.X, y_nodes_max, p2d.Y);
237 for (y = y_nodes_max; y >= y_nodes_min; y--) {
238 MapNode &n = vm->m_data[i];
239 if (ndef->get(n).walkable)
242 VoxelArea::add_y(em, i, -1);
244 return (y >= y_nodes_min) ? y : y_nodes_min - 1;
248 // Returns -MAX_MAP_GENERATION_LIMIT if not found
249 s16 Mapgen::findGroundLevel(v2s16 p2d, s16 ymin, s16 ymax)
251 const v3s16 &em = vm->m_area.getExtent();
252 u32 i = vm->m_area.index(p2d.X, ymax, p2d.Y);
255 for (y = ymax; y >= ymin; y--) {
256 MapNode &n = vm->m_data[i];
257 if (ndef->get(n).walkable)
260 VoxelArea::add_y(em, i, -1);
262 return (y >= ymin) ? y : -MAX_MAP_GENERATION_LIMIT;
266 // Returns -MAX_MAP_GENERATION_LIMIT if not found or if ground is found first
267 s16 Mapgen::findLiquidSurface(v2s16 p2d, s16 ymin, s16 ymax)
269 const v3s16 &em = vm->m_area.getExtent();
270 u32 i = vm->m_area.index(p2d.X, ymax, p2d.Y);
273 for (y = ymax; y >= ymin; y--) {
274 MapNode &n = vm->m_data[i];
275 if (ndef->get(n).walkable)
276 return -MAX_MAP_GENERATION_LIMIT;
278 if (ndef->get(n).isLiquid())
281 VoxelArea::add_y(em, i, -1);
283 return (y >= ymin) ? y : -MAX_MAP_GENERATION_LIMIT;
287 void Mapgen::updateHeightmap(v3s16 nmin, v3s16 nmax)
292 //TimeTaker t("Mapgen::updateHeightmap", NULL, PRECISION_MICRO);
294 for (s16 z = nmin.Z; z <= nmax.Z; z++) {
295 for (s16 x = nmin.X; x <= nmax.X; x++, index++) {
296 s16 y = findGroundLevel(v2s16(x, z), nmin.Y, nmax.Y);
298 heightmap[index] = y;
304 void Mapgen::getSurfaces(v2s16 p2d, s16 ymin, s16 ymax,
305 std::vector<s16> &floors, std::vector<s16> &ceilings)
307 const v3s16 &em = vm->m_area.getExtent();
309 bool is_walkable = false;
310 u32 vi = vm->m_area.index(p2d.X, ymax, p2d.Y);
311 MapNode mn_max = vm->m_data[vi];
312 bool walkable_above = ndef->get(mn_max).walkable;
313 VoxelArea::add_y(em, vi, -1);
315 for (s16 y = ymax - 1; y >= ymin; y--) {
316 MapNode mn = vm->m_data[vi];
317 is_walkable = ndef->get(mn).walkable;
319 if (is_walkable && !walkable_above) {
321 } else if (!is_walkable && walkable_above) {
322 ceilings.push_back(y + 1);
325 VoxelArea::add_y(em, vi, -1);
326 walkable_above = is_walkable;
331 inline bool Mapgen::isLiquidHorizontallyFlowable(u32 vi, v3s16 em)
334 VoxelArea::add_x(em, vi_neg_x, -1);
335 if (vm->m_data[vi_neg_x].getContent() != CONTENT_IGNORE) {
336 const ContentFeatures &c_nx = ndef->get(vm->m_data[vi_neg_x]);
337 if (c_nx.floodable && !c_nx.isLiquid())
341 VoxelArea::add_x(em, vi_pos_x, +1);
342 if (vm->m_data[vi_pos_x].getContent() != CONTENT_IGNORE) {
343 const ContentFeatures &c_px = ndef->get(vm->m_data[vi_pos_x]);
344 if (c_px.floodable && !c_px.isLiquid())
348 VoxelArea::add_z(em, vi_neg_z, -1);
349 if (vm->m_data[vi_neg_z].getContent() != CONTENT_IGNORE) {
350 const ContentFeatures &c_nz = ndef->get(vm->m_data[vi_neg_z]);
351 if (c_nz.floodable && !c_nz.isLiquid())
355 VoxelArea::add_z(em, vi_pos_z, +1);
356 if (vm->m_data[vi_pos_z].getContent() != CONTENT_IGNORE) {
357 const ContentFeatures &c_pz = ndef->get(vm->m_data[vi_pos_z]);
358 if (c_pz.floodable && !c_pz.isLiquid())
364 void Mapgen::updateLiquid(UniqueQueue<v3s16> *trans_liquid, v3s16 nmin, v3s16 nmax)
366 bool isignored, isliquid, wasignored, wasliquid, waschecked, waspushed;
367 const v3s16 &em = vm->m_area.getExtent();
369 for (s16 z = nmin.Z + 1; z <= nmax.Z - 1; z++)
370 for (s16 x = nmin.X + 1; x <= nmax.X - 1; x++) {
376 u32 vi = vm->m_area.index(x, nmax.Y, z);
377 for (s16 y = nmax.Y; y >= nmin.Y; y--) {
378 isignored = vm->m_data[vi].getContent() == CONTENT_IGNORE;
379 isliquid = ndef->get(vm->m_data[vi]).isLiquid();
381 if (isignored || wasignored || isliquid == wasliquid) {
382 // Neither topmost node of liquid column nor topmost node below column
385 } else if (isliquid) {
386 // This is the topmost node in the column
387 bool ispushed = false;
388 if (isLiquidHorizontallyFlowable(vi, em)) {
389 trans_liquid->push_back(v3s16(x, y, z));
392 // Remember waschecked and waspushed to avoid repeated
393 // checks/pushes in case the column consists of only this node
395 waspushed = ispushed;
397 // This is the topmost node below a liquid column
399 VoxelArea::add_y(em, vi_above, 1);
400 if (!waspushed && (ndef->get(vm->m_data[vi]).floodable ||
401 (!waschecked && isLiquidHorizontallyFlowable(vi_above, em)))) {
402 // Push back the lowest node in the column which is one
403 // node above this one
404 trans_liquid->push_back(v3s16(x, y + 1, z));
408 wasliquid = isliquid;
409 wasignored = isignored;
410 VoxelArea::add_y(em, vi, -1);
416 void Mapgen::setLighting(u8 light, v3s16 nmin, v3s16 nmax)
418 ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG);
419 VoxelArea a(nmin, nmax);
421 for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) {
422 for (int y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) {
423 u32 i = vm->m_area.index(a.MinEdge.X, y, z);
424 for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++, i++)
425 vm->m_data[i].param1 = light;
431 void Mapgen::lightSpread(VoxelArea &a, v3s16 p, u8 light)
433 if (light <= 1 || !a.contains(p))
436 u32 vi = vm->m_area.index(p);
437 MapNode &n = vm->m_data[vi];
439 // Decay light in each of the banks separately
440 u8 light_day = light & 0x0F;
444 u8 light_night = light & 0xF0;
448 // Bail out only if we have no more light from either bank to propogate, or
449 // we hit a solid block that light cannot pass through.
450 if ((light_day <= (n.param1 & 0x0F) &&
451 light_night <= (n.param1 & 0xF0)) ||
452 !ndef->get(n).light_propagates)
455 // Since this recursive function only terminates when there is no light from
456 // either bank left, we need to take the max of both banks into account for
457 // the case where spreading has stopped for one light bank but not the other.
458 light = MYMAX(light_day, n.param1 & 0x0F) |
459 MYMAX(light_night, n.param1 & 0xF0);
463 lightSpread(a, p + v3s16(0, 0, 1), light);
464 lightSpread(a, p + v3s16(0, 1, 0), light);
465 lightSpread(a, p + v3s16(1, 0, 0), light);
466 lightSpread(a, p - v3s16(0, 0, 1), light);
467 lightSpread(a, p - v3s16(0, 1, 0), light);
468 lightSpread(a, p - v3s16(1, 0, 0), light);
472 void Mapgen::calcLighting(v3s16 nmin, v3s16 nmax, v3s16 full_nmin, v3s16 full_nmax,
473 bool propagate_shadow)
475 ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG);
476 //TimeTaker t("updateLighting");
478 propagateSunlight(nmin, nmax, propagate_shadow);
479 spreadLight(full_nmin, full_nmax);
481 //printf("updateLighting: %dms\n", t.stop());
485 void Mapgen::propagateSunlight(v3s16 nmin, v3s16 nmax, bool propagate_shadow)
487 //TimeTaker t("propagateSunlight");
488 VoxelArea a(nmin, nmax);
489 bool block_is_underground = (water_level >= nmax.Y);
490 const v3s16 &em = vm->m_area.getExtent();
492 // NOTE: Direct access to the low 4 bits of param1 is okay here because,
493 // by definition, sunlight will never be in the night lightbank.
495 for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) {
496 for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++) {
497 // see if we can get a light value from the overtop
498 u32 i = vm->m_area.index(x, a.MaxEdge.Y + 1, z);
499 if (vm->m_data[i].getContent() == CONTENT_IGNORE) {
500 if (block_is_underground)
502 } else if ((vm->m_data[i].param1 & 0x0F) != LIGHT_SUN &&
506 VoxelArea::add_y(em, i, -1);
508 for (int y = a.MaxEdge.Y; y >= a.MinEdge.Y; y--) {
509 MapNode &n = vm->m_data[i];
510 if (!ndef->get(n).sunlight_propagates)
512 n.param1 = LIGHT_SUN;
513 VoxelArea::add_y(em, i, -1);
517 //printf("propagateSunlight: %dms\n", t.stop());
521 void Mapgen::spreadLight(v3s16 nmin, v3s16 nmax)
523 //TimeTaker t("spreadLight");
524 VoxelArea a(nmin, nmax);
526 for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) {
527 for (int y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) {
528 u32 i = vm->m_area.index(a.MinEdge.X, y, z);
529 for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++, i++) {
530 MapNode &n = vm->m_data[i];
531 if (n.getContent() == CONTENT_IGNORE)
534 const ContentFeatures &cf = ndef->get(n);
535 if (!cf.light_propagates)
538 // TODO(hmmmmm): Abstract away direct param1 accesses with a
539 // wrapper, but something lighter than MapNode::get/setLight
541 u8 light_produced = cf.light_source;
543 n.param1 = light_produced | (light_produced << 4);
547 lightSpread(a, v3s16(x, y, z + 1), light);
548 lightSpread(a, v3s16(x, y + 1, z ), light);
549 lightSpread(a, v3s16(x + 1, y, z ), light);
550 lightSpread(a, v3s16(x, y, z - 1), light);
551 lightSpread(a, v3s16(x, y - 1, z ), light);
552 lightSpread(a, v3s16(x - 1, y, z ), light);
558 //printf("spreadLight: %dms\n", t.stop());
566 MapgenBasic::MapgenBasic(int mapgenid, MapgenParams *params, EmergeManager *emerge)
567 : Mapgen(mapgenid, params, emerge)
569 this->m_emerge = emerge;
570 this->m_bmgr = emerge->biomemgr;
572 //// Here, 'stride' refers to the number of elements needed to skip to index
573 //// an adjacent element for that coordinate in noise/height/biome maps
574 //// (*not* vmanip content map!)
576 // Note there is no X stride explicitly defined. Items adjacent in the X
577 // coordinate are assumed to be adjacent in memory as well (i.e. stride of 1).
579 // Number of elements to skip to get to the next Y coordinate
580 this->ystride = csize.X;
582 // Number of elements to skip to get to the next Z coordinate
583 this->zstride = csize.X * csize.Y;
585 // Z-stride value for maps oversized for 1-down overgeneration
586 this->zstride_1d = csize.X * (csize.Y + 1);
588 // Z-stride value for maps oversized for 1-up 1-down overgeneration
589 this->zstride_1u1d = csize.X * (csize.Y + 2);
591 //// Allocate heightmap
592 this->heightmap = new s16[csize.X * csize.Z];
594 //// Initialize biome generator
595 // TODO(hmmmm): should we have a way to disable biomemanager biomes?
596 biomegen = m_bmgr->createBiomeGen(BIOMEGEN_ORIGINAL, params->bparams, csize);
597 biomemap = biomegen->biomemap;
599 //// Look up some commonly used content
600 c_stone = ndef->getId("mapgen_stone");
601 c_desert_stone = ndef->getId("mapgen_desert_stone");
602 c_sandstone = ndef->getId("mapgen_sandstone");
603 c_water_source = ndef->getId("mapgen_water_source");
604 c_river_water_source = ndef->getId("mapgen_river_water_source");
605 c_lava_source = ndef->getId("mapgen_lava_source");
607 // Fall back to more basic content if not defined
608 // river_water_source cannot fallback to water_source because river water
609 // needs to be non-renewable and have a short flow range.
610 if (c_desert_stone == CONTENT_IGNORE)
611 c_desert_stone = c_stone;
612 if (c_sandstone == CONTENT_IGNORE)
613 c_sandstone = c_stone;
615 //// Content used for dungeon generation
616 c_cobble = ndef->getId("mapgen_cobble");
617 c_mossycobble = ndef->getId("mapgen_mossycobble");
618 c_stair_cobble = ndef->getId("mapgen_stair_cobble");
619 c_stair_desert_stone = ndef->getId("mapgen_stair_desert_stone");
620 c_sandstonebrick = ndef->getId("mapgen_sandstonebrick");
621 c_stair_sandstone_block = ndef->getId("mapgen_stair_sandstone_block");
623 // Fall back to more basic content if not defined
624 if (c_mossycobble == CONTENT_IGNORE)
625 c_mossycobble = c_cobble;
626 if (c_stair_cobble == CONTENT_IGNORE)
627 c_stair_cobble = c_cobble;
628 if (c_stair_desert_stone == CONTENT_IGNORE)
629 c_stair_desert_stone = c_desert_stone;
630 if (c_sandstonebrick == CONTENT_IGNORE)
631 c_sandstonebrick = c_sandstone;
632 if (c_stair_sandstone_block == CONTENT_IGNORE)
633 c_stair_sandstone_block = c_sandstonebrick;
637 MapgenBasic::~MapgenBasic()
644 void MapgenBasic::generateBiomes()
646 // can't generate biomes without a biome generator!
650 const v3s16 &em = vm->m_area.getExtent();
653 noise_filler_depth->perlinMap2D(node_min.X, node_min.Z);
655 for (s16 z = node_min.Z; z <= node_max.Z; z++)
656 for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
658 biome_t water_biome_index = 0;
661 u16 depth_water_top = 0;
662 u16 depth_riverbed = 0;
663 s16 biome_y_min = -MAX_MAP_GENERATION_LIMIT;
664 u32 vi = vm->m_area.index(x, node_max.Y, z);
666 // Check node at base of mapchunk above, either a node of a previously
667 // generated mapchunk or if not, a node of overgenerated base terrain.
668 content_t c_above = vm->m_data[vi + em.X].getContent();
669 bool air_above = c_above == CONTENT_AIR;
670 bool river_water_above = c_above == c_river_water_source;
671 bool water_above = c_above == c_water_source || river_water_above;
673 biomemap[index] = BIOME_NONE;
675 // If there is air or water above enable top/filler placement, otherwise force
676 // nplaced to stone level by setting a number exceeding any possible filler depth.
677 u16 nplaced = (air_above || water_above) ? 0 : U16_MAX;
679 for (s16 y = node_max.Y; y >= node_min.Y; y--) {
680 content_t c = vm->m_data[vi].getContent();
681 // Biome is (re)calculated:
682 // 1. At the surface of stone below air or water.
683 // 2. At the surface of water below air.
684 // 3. When stone or water is detected but biome has not yet been calculated.
685 // 4. When stone or water is detected just below a biome's lower limit.
686 bool is_stone_surface = (c == c_stone) &&
687 (air_above || water_above || !biome || y < biome_y_min); // 1, 3, 4
689 bool is_water_surface =
690 (c == c_water_source || c == c_river_water_source) &&
691 (air_above || !biome || y < biome_y_min); // 2, 3, 4
693 if (is_stone_surface || is_water_surface) {
694 // (Re)calculate biome
695 biome = biomegen->getBiomeAtIndex(index, v3s16(x, y, z));
697 // Add biome to biomemap at first stone surface detected
698 if (biomemap[index] == BIOME_NONE && is_stone_surface)
699 biomemap[index] = biome->index;
701 // Store biome of first water surface detected, as a fallback
702 // entry for the biomemap.
703 if (water_biome_index == 0 && is_water_surface)
704 water_biome_index = biome->index;
706 depth_top = biome->depth_top;
707 base_filler = MYMAX(depth_top +
708 biome->depth_filler +
709 noise_filler_depth->result[index], 0.0f);
710 depth_water_top = biome->depth_water_top;
711 depth_riverbed = biome->depth_riverbed;
712 biome_y_min = biome->min_pos.Y;
716 content_t c_below = vm->m_data[vi - em.X].getContent();
718 // If the node below isn't solid, make this node stone, so that
719 // any top/filler nodes above are structurally supported.
720 // This is done by aborting the cycle of top/filler placement
721 // immediately by forcing nplaced to stone level.
722 if (c_below == CONTENT_AIR
723 || c_below == c_water_source
724 || c_below == c_river_water_source)
727 if (river_water_above) {
728 if (nplaced < depth_riverbed) {
729 vm->m_data[vi] = MapNode(biome->c_riverbed);
732 nplaced = U16_MAX; // Disable top/filler placement
733 river_water_above = false;
735 } else if (nplaced < depth_top) {
736 vm->m_data[vi] = MapNode(biome->c_top);
738 } else if (nplaced < base_filler) {
739 vm->m_data[vi] = MapNode(biome->c_filler);
742 vm->m_data[vi] = MapNode(biome->c_stone);
743 nplaced = U16_MAX; // Disable top/filler placement
748 } else if (c == c_water_source) {
749 vm->m_data[vi] = MapNode((y > (s32)(water_level - depth_water_top))
750 ? biome->c_water_top : biome->c_water);
751 nplaced = 0; // Enable top/filler placement for next surface
754 } else if (c == c_river_water_source) {
755 vm->m_data[vi] = MapNode(biome->c_river_water);
756 nplaced = 0; // Enable riverbed placement for next surface
759 river_water_above = true;
760 } else if (c == CONTENT_AIR) {
761 nplaced = 0; // Enable top/filler placement for next surface
764 } else { // Possible various nodes overgenerated from neighbouring mapchunks
765 nplaced = U16_MAX; // Disable top/filler placement
770 VoxelArea::add_y(em, vi, -1);
772 // If no stone surface detected in mapchunk column and a water surface
773 // biome fallback exists, add it to the biomemap. This avoids water
774 // surface decorations failing in deep water.
775 if (biomemap[index] == BIOME_NONE && water_biome_index != 0)
776 biomemap[index] = water_biome_index;
781 void MapgenBasic::dustTopNodes()
783 if (node_max.Y < water_level)
786 const v3s16 &em = vm->m_area.getExtent();
789 for (s16 z = node_min.Z; z <= node_max.Z; z++)
790 for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
791 Biome *biome = (Biome *)m_bmgr->getRaw(biomemap[index]);
793 if (biome->c_dust == CONTENT_IGNORE)
796 // Check if mapchunk above has generated, if so, drop dust from 16 nodes
797 // above current mapchunk top, above decorations that will extend above
798 // the current mapchunk. If the mapchunk above has not generated, it
799 // will provide this required dust when it does.
800 u32 vi = vm->m_area.index(x, full_node_max.Y, z);
801 content_t c_full_max = vm->m_data[vi].getContent();
804 if (c_full_max == CONTENT_AIR) {
805 y_start = full_node_max.Y - 1;
806 } else if (c_full_max == CONTENT_IGNORE) {
807 vi = vm->m_area.index(x, node_max.Y + 1, z);
808 content_t c_max = vm->m_data[vi].getContent();
810 if (c_max == CONTENT_AIR)
811 y_start = node_max.Y;
818 vi = vm->m_area.index(x, y_start, z);
819 for (s16 y = y_start; y >= node_min.Y - 1; y--) {
820 if (vm->m_data[vi].getContent() != CONTENT_AIR)
823 VoxelArea::add_y(em, vi, -1);
826 content_t c = vm->m_data[vi].getContent();
827 NodeDrawType dtype = ndef->get(c).drawtype;
828 // Only place on cubic, walkable, non-dust nodes.
829 // Dust check needed due to avoid double layer of dust caused by
830 // dropping dust from 16 nodes above mapchunk top.
831 if ((dtype == NDT_NORMAL ||
832 dtype == NDT_ALLFACES ||
833 dtype == NDT_ALLFACES_OPTIONAL ||
834 dtype == NDT_GLASSLIKE ||
835 dtype == NDT_GLASSLIKE_FRAMED ||
836 dtype == NDT_GLASSLIKE_FRAMED_OPTIONAL) &&
837 ndef->get(c).walkable && c != biome->c_dust) {
838 VoxelArea::add_y(em, vi, 1);
839 vm->m_data[vi] = MapNode(biome->c_dust);
845 void MapgenBasic::generateCavesNoiseIntersection(s16 max_stone_y)
847 if (node_min.Y > max_stone_y)
850 CavesNoiseIntersection caves_noise(ndef, m_bmgr, csize,
851 &np_cave1, &np_cave2, seed, cave_width);
853 caves_noise.generateCaves(vm, node_min, node_max, biomemap);
857 void MapgenBasic::generateCavesRandomWalk(s16 max_stone_y, s16 large_cave_depth)
859 if (node_min.Y > max_stone_y || node_max.Y > large_cave_depth)
862 PseudoRandom ps(blockseed + 21343);
863 u32 bruises_count = ps.range(0, 2);
865 for (u32 i = 0; i < bruises_count; i++) {
866 CavesRandomWalk cave(ndef, &gennotify, seed, water_level,
867 c_water_source, c_lava_source, lava_depth, biomegen);
869 cave.makeCave(vm, node_min, node_max, &ps, true, max_stone_y,
875 bool MapgenBasic::generateCavernsNoise(s16 max_stone_y)
877 if (node_min.Y > max_stone_y || node_min.Y > cavern_limit)
880 CavernsNoise caverns_noise(ndef, csize, &np_cavern,
881 seed, cavern_limit, cavern_taper, cavern_threshold);
883 return caverns_noise.generateCaverns(vm, node_min, node_max);
887 void MapgenBasic::generateDungeons(s16 max_stone_y)
889 if (max_stone_y < node_min.Y)
892 // Get biome at mapchunk midpoint
893 v3s16 chunk_mid = node_min + (node_max - node_min) / v3s16(2, 2, 2);
894 Biome *biome = (Biome *)biomegen->getBiomeAtPoint(chunk_mid);
899 dp.only_in_ground = true;
900 dp.corridor_len_min = 1;
901 dp.corridor_len_max = 13;
905 dp.np_density = nparams_dungeon_density;
906 dp.np_alt_wall = nparams_dungeon_alt_wall;
908 // Biome-defined dungeon nodes
909 if (biome->c_dungeon != CONTENT_IGNORE) {
910 dp.c_wall = biome->c_dungeon;
911 // If 'node_dungeon_alt' is not defined by biome, it and dp.c_alt_wall
912 // become CONTENT_IGNORE which skips the alt wall node placement loop in
914 dp.c_alt_wall = biome->c_dungeon_alt;
915 // Stairs fall back to 'c_dungeon' if not defined by biome
916 dp.c_stair = (biome->c_dungeon_stair != CONTENT_IGNORE) ?
917 biome->c_dungeon_stair : biome->c_dungeon;
919 dp.diagonal_dirs = false;
920 dp.holesize = v3s16(2, 2, 2);
921 dp.room_size_min = v3s16(6, 4, 6);
922 dp.room_size_max = v3s16(10, 6, 10);
923 dp.room_size_large_min = v3s16(10, 8, 10);
924 dp.room_size_large_max = v3s16(18, 16, 18);
925 dp.notifytype = GENNOTIFY_DUNGEON;
927 // Otherwise classic behaviour
928 } else if (biome->c_stone == c_stone) {
929 dp.c_wall = c_cobble;
930 dp.c_alt_wall = c_mossycobble;
931 dp.c_stair = c_stair_cobble;
933 dp.diagonal_dirs = false;
934 dp.holesize = v3s16(1, 2, 1);
935 dp.room_size_min = v3s16(4, 4, 4);
936 dp.room_size_max = v3s16(8, 6, 8);
937 dp.room_size_large_min = v3s16(8, 8, 8);
938 dp.room_size_large_max = v3s16(16, 16, 16);
939 dp.notifytype = GENNOTIFY_DUNGEON;
941 } else if (biome->c_stone == c_desert_stone) {
942 dp.c_wall = c_desert_stone;
943 dp.c_alt_wall = CONTENT_IGNORE;
944 dp.c_stair = c_stair_desert_stone;
946 dp.diagonal_dirs = true;
947 dp.holesize = v3s16(2, 3, 2);
948 dp.room_size_min = v3s16(6, 9, 6);
949 dp.room_size_max = v3s16(10, 11, 10);
950 dp.room_size_large_min = v3s16(10, 13, 10);
951 dp.room_size_large_max = v3s16(18, 21, 18);
952 dp.notifytype = GENNOTIFY_TEMPLE;
954 } else if (biome->c_stone == c_sandstone) {
955 dp.c_wall = c_sandstonebrick;
956 dp.c_alt_wall = CONTENT_IGNORE;
957 dp.c_stair = c_stair_sandstone_block;
959 dp.diagonal_dirs = false;
960 dp.holesize = v3s16(2, 2, 2);
961 dp.room_size_min = v3s16(6, 4, 6);
962 dp.room_size_max = v3s16(10, 6, 10);
963 dp.room_size_large_min = v3s16(10, 8, 10);
964 dp.room_size_large_max = v3s16(18, 16, 18);
965 dp.notifytype = GENNOTIFY_DUNGEON;
967 // Fallback to using biome 'node_stone'
969 dp.c_wall = biome->c_stone;
970 dp.c_alt_wall = CONTENT_IGNORE;
971 dp.c_stair = biome->c_stone;
973 dp.diagonal_dirs = false;
974 dp.holesize = v3s16(2, 2, 2);
975 dp.room_size_min = v3s16(6, 4, 6);
976 dp.room_size_max = v3s16(10, 6, 10);
977 dp.room_size_large_min = v3s16(10, 8, 10);
978 dp.room_size_large_max = v3s16(18, 16, 18);
979 dp.notifytype = GENNOTIFY_DUNGEON;
982 DungeonGen dgen(ndef, &gennotify, &dp);
983 dgen.generate(vm, blockseed, full_node_min, full_node_max);
988 //// GenerateNotifier
991 GenerateNotifier::GenerateNotifier(u32 notify_on,
992 std::set<u32> *notify_on_deco_ids)
994 m_notify_on = notify_on;
995 m_notify_on_deco_ids = notify_on_deco_ids;
999 void GenerateNotifier::setNotifyOn(u32 notify_on)
1001 m_notify_on = notify_on;
1005 void GenerateNotifier::setNotifyOnDecoIds(std::set<u32> *notify_on_deco_ids)
1007 m_notify_on_deco_ids = notify_on_deco_ids;
1011 bool GenerateNotifier::addEvent(GenNotifyType type, v3s16 pos, u32 id)
1013 if (!(m_notify_on & (1 << type)))
1016 if (type == GENNOTIFY_DECORATION &&
1017 m_notify_on_deco_ids->find(id) == m_notify_on_deco_ids->end())
1024 m_notify_events.push_back(gne);
1030 void GenerateNotifier::getEvents(
1031 std::map<std::string, std::vector<v3s16> > &event_map)
1033 std::list<GenNotifyEvent>::iterator it;
1035 for (it = m_notify_events.begin(); it != m_notify_events.end(); ++it) {
1036 GenNotifyEvent &gn = *it;
1037 std::string name = (gn.type == GENNOTIFY_DECORATION) ?
1038 "decoration#"+ itos(gn.id) :
1039 flagdesc_gennotify[gn.type].name;
1041 event_map[name].push_back(gn.pos);
1046 void GenerateNotifier::clearEvents()
1048 m_notify_events.clear();
1057 MapgenParams::~MapgenParams()
1063 void MapgenParams::readParams(const Settings *settings)
1065 std::string seed_str;
1066 const char *seed_name = (settings == g_settings) ? "fixed_map_seed" : "seed";
1068 if (settings->getNoEx(seed_name, seed_str)) {
1069 if (!seed_str.empty())
1070 seed = read_seed(seed_str.c_str());
1072 myrand_bytes(&seed, sizeof(seed));
1075 std::string mg_name;
1076 if (settings->getNoEx("mg_name", mg_name)) {
1077 mgtype = Mapgen::getMapgenType(mg_name);
1078 if (mgtype == MAPGEN_INVALID)
1079 mgtype = MAPGEN_DEFAULT;
1082 settings->getS16NoEx("water_level", water_level);
1083 settings->getS16NoEx("mapgen_limit", mapgen_limit);
1084 settings->getS16NoEx("chunksize", chunksize);
1085 settings->getFlagStrNoEx("mg_flags", flags, flagdesc_mapgen);
1088 bparams = BiomeManager::createBiomeParams(BIOMEGEN_ORIGINAL);
1090 bparams->readParams(settings);
1091 bparams->seed = seed;
1096 void MapgenParams::writeParams(Settings *settings) const
1098 settings->set("mg_name", Mapgen::getMapgenName(mgtype));
1099 settings->setU64("seed", seed);
1100 settings->setS16("water_level", water_level);
1101 settings->setS16("mapgen_limit", mapgen_limit);
1102 settings->setS16("chunksize", chunksize);
1103 settings->setFlagStr("mg_flags", flags, flagdesc_mapgen, U32_MAX);
1106 bparams->writeParams(settings);
1110 // Calculate exact edges of the outermost mapchunks that are within the
1111 // set 'mapgen_limit'.
1112 void MapgenParams::calcMapgenEdges()
1114 // Central chunk offset, in blocks
1115 s16 ccoff_b = -chunksize / 2;
1116 // Chunksize, in nodes
1117 s32 csize_n = chunksize * MAP_BLOCKSIZE;
1118 // Minp/maxp of central chunk, in nodes
1119 s16 ccmin = ccoff_b * MAP_BLOCKSIZE;
1120 s16 ccmax = ccmin + csize_n - 1;
1121 // Fullminp/fullmaxp of central chunk, in nodes
1122 s16 ccfmin = ccmin - MAP_BLOCKSIZE;
1123 s16 ccfmax = ccmax + MAP_BLOCKSIZE;
1124 // Effective mapgen limit, in blocks
1125 // Uses same calculation as ServerMap::blockpos_over_mapgen_limit(v3s16 p)
1126 s16 mapgen_limit_b = rangelim(mapgen_limit,
1127 0, MAX_MAP_GENERATION_LIMIT) / MAP_BLOCKSIZE;
1128 // Effective mapgen limits, in nodes
1129 s16 mapgen_limit_min = -mapgen_limit_b * MAP_BLOCKSIZE;
1130 s16 mapgen_limit_max = (mapgen_limit_b + 1) * MAP_BLOCKSIZE - 1;
1131 // Number of complete chunks from central chunk fullminp/fullmaxp
1132 // to effective mapgen limits.
1133 s16 numcmin = MYMAX((ccfmin - mapgen_limit_min) / csize_n, 0);
1134 s16 numcmax = MYMAX((mapgen_limit_max - ccfmax) / csize_n, 0);
1135 // Mapgen edges, in nodes
1136 mapgen_edge_min = ccmin - numcmin * csize_n;
1137 mapgen_edge_max = ccmax + numcmax * csize_n;
1139 m_mapgen_edges_calculated = true;
1143 s32 MapgenParams::getSpawnRangeMax()
1145 if (!m_mapgen_edges_calculated)
1148 return MYMIN(-mapgen_edge_min, mapgen_edge_max);