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},
62 FlagDesc flagdesc_gennotify[] = {
63 {"dungeon", 1 << GENNOTIFY_DUNGEON},
64 {"temple", 1 << GENNOTIFY_TEMPLE},
65 {"cave_begin", 1 << GENNOTIFY_CAVE_BEGIN},
66 {"cave_end", 1 << GENNOTIFY_CAVE_END},
67 {"large_cave_begin", 1 << GENNOTIFY_LARGECAVE_BEGIN},
68 {"large_cave_end", 1 << GENNOTIFY_LARGECAVE_END},
69 {"decoration", 1 << GENNOTIFY_DECORATION},
82 static MapgenDesc g_reg_mapgens[] = {
94 ARRLEN(g_reg_mapgens) == MAPGEN_INVALID,
95 registered_mapgens_is_wrong_size);
101 Mapgen::Mapgen(int mapgenid, MapgenParams *params, EmergeManager *emerge) :
102 gennotify(emerge->gen_notify_on, &emerge->gen_notify_on_deco_ids)
105 water_level = params->water_level;
106 mapgen_limit = params->mapgen_limit;
107 flags = params->flags;
108 csize = v3s16(1, 1, 1) * (params->chunksize * MAP_BLOCKSIZE);
111 We are losing half our entropy by doing this, but it is necessary to
112 preserve reverse compatibility. If the top half of our current 64 bit
113 seeds ever starts getting used, existing worlds will break due to a
114 different hash outcome and no way to differentiate between versions.
116 A solution could be to add a new bit to designate that the top half of
117 the seed value should be used, essentially a 1-bit version code, but
118 this would require increasing the total size of a seed to 9 bytes (yuck)
120 It's probably okay if this never gets fixed. 4.2 billion possibilities
121 ought to be enough for anyone.
123 seed = (s32)params->seed;
129 MapgenType Mapgen::getMapgenType(const std::string &mgname)
131 for (size_t i = 0; i != ARRLEN(g_reg_mapgens); i++) {
132 if (mgname == g_reg_mapgens[i].name)
133 return (MapgenType)i;
136 return MAPGEN_INVALID;
140 const char *Mapgen::getMapgenName(MapgenType mgtype)
142 size_t index = (size_t)mgtype;
143 if (index == MAPGEN_INVALID || index >= ARRLEN(g_reg_mapgens))
146 return g_reg_mapgens[index].name;
150 Mapgen *Mapgen::createMapgen(MapgenType mgtype, int mgid,
151 MapgenParams *params, EmergeManager *emerge)
154 case MAPGEN_CARPATHIAN:
155 return new MapgenCarpathian(mgid, (MapgenCarpathianParams *)params, emerge);
157 return new MapgenFlat(mgid, (MapgenFlatParams *)params, emerge);
159 return new MapgenFractal(mgid, (MapgenFractalParams *)params, emerge);
160 case MAPGEN_SINGLENODE:
161 return new MapgenSinglenode(mgid, (MapgenSinglenodeParams *)params, emerge);
163 return new MapgenV5(mgid, (MapgenV5Params *)params, emerge);
165 return new MapgenV6(mgid, (MapgenV6Params *)params, emerge);
167 return new MapgenV7(mgid, (MapgenV7Params *)params, emerge);
169 return new MapgenValleys(mgid, (MapgenValleysParams *)params, emerge);
176 MapgenParams *Mapgen::createMapgenParams(MapgenType mgtype)
179 case MAPGEN_CARPATHIAN:
180 return new MapgenCarpathianParams;
182 return new MapgenFlatParams;
184 return new MapgenFractalParams;
185 case MAPGEN_SINGLENODE:
186 return new MapgenSinglenodeParams;
188 return new MapgenV5Params;
190 return new MapgenV6Params;
192 return new MapgenV7Params;
194 return new MapgenValleysParams;
201 void Mapgen::getMapgenNames(std::vector<const char *> *mgnames, bool include_hidden)
203 for (u32 i = 0; i != ARRLEN(g_reg_mapgens); i++) {
204 if (include_hidden || g_reg_mapgens[i].is_user_visible)
205 mgnames->push_back(g_reg_mapgens[i].name);
210 u32 Mapgen::getBlockSeed(v3s16 p, s32 seed)
219 u32 Mapgen::getBlockSeed2(v3s16 p, s32 seed)
221 u32 n = 1619 * p.X + 31337 * p.Y + 52591 * p.Z + 1013 * seed;
223 return (n * (n * n * 60493 + 19990303) + 1376312589);
227 // Returns Y one under area minimum if not found
228 s16 Mapgen::findGroundLevelFull(v2s16 p2d)
230 const v3s16 &em = vm->m_area.getExtent();
231 s16 y_nodes_max = vm->m_area.MaxEdge.Y;
232 s16 y_nodes_min = vm->m_area.MinEdge.Y;
233 u32 i = vm->m_area.index(p2d.X, y_nodes_max, p2d.Y);
236 for (y = y_nodes_max; y >= y_nodes_min; y--) {
237 MapNode &n = vm->m_data[i];
238 if (ndef->get(n).walkable)
241 VoxelArea::add_y(em, i, -1);
243 return (y >= y_nodes_min) ? y : y_nodes_min - 1;
247 // Returns -MAX_MAP_GENERATION_LIMIT if not found
248 s16 Mapgen::findGroundLevel(v2s16 p2d, s16 ymin, s16 ymax)
250 const v3s16 &em = vm->m_area.getExtent();
251 u32 i = vm->m_area.index(p2d.X, ymax, p2d.Y);
254 for (y = ymax; y >= ymin; y--) {
255 MapNode &n = vm->m_data[i];
256 if (ndef->get(n).walkable)
259 VoxelArea::add_y(em, i, -1);
261 return (y >= ymin) ? y : -MAX_MAP_GENERATION_LIMIT;
265 // Returns -MAX_MAP_GENERATION_LIMIT if not found or if ground is found first
266 s16 Mapgen::findLiquidSurface(v2s16 p2d, s16 ymin, s16 ymax)
268 const v3s16 &em = vm->m_area.getExtent();
269 u32 i = vm->m_area.index(p2d.X, ymax, p2d.Y);
272 for (y = ymax; y >= ymin; y--) {
273 MapNode &n = vm->m_data[i];
274 if (ndef->get(n).walkable)
275 return -MAX_MAP_GENERATION_LIMIT;
277 if (ndef->get(n).isLiquid())
280 VoxelArea::add_y(em, i, -1);
282 return (y >= ymin) ? y : -MAX_MAP_GENERATION_LIMIT;
286 void Mapgen::updateHeightmap(v3s16 nmin, v3s16 nmax)
291 //TimeTaker t("Mapgen::updateHeightmap", NULL, PRECISION_MICRO);
293 for (s16 z = nmin.Z; z <= nmax.Z; z++) {
294 for (s16 x = nmin.X; x <= nmax.X; x++, index++) {
295 s16 y = findGroundLevel(v2s16(x, z), nmin.Y, nmax.Y);
297 heightmap[index] = y;
303 void Mapgen::getSurfaces(v2s16 p2d, s16 ymin, s16 ymax,
304 std::vector<s16> &floors, std::vector<s16> &ceilings)
306 const v3s16 &em = vm->m_area.getExtent();
308 bool is_walkable = false;
309 u32 vi = vm->m_area.index(p2d.X, ymax, p2d.Y);
310 MapNode mn_max = vm->m_data[vi];
311 bool walkable_above = ndef->get(mn_max).walkable;
312 VoxelArea::add_y(em, vi, -1);
314 for (s16 y = ymax - 1; y >= ymin; y--) {
315 MapNode mn = vm->m_data[vi];
316 is_walkable = ndef->get(mn).walkable;
318 if (is_walkable && !walkable_above) {
320 } else if (!is_walkable && walkable_above) {
321 ceilings.push_back(y + 1);
324 VoxelArea::add_y(em, vi, -1);
325 walkable_above = is_walkable;
330 inline bool Mapgen::isLiquidHorizontallyFlowable(u32 vi, v3s16 em)
333 VoxelArea::add_x(em, vi_neg_x, -1);
334 if (vm->m_data[vi_neg_x].getContent() != CONTENT_IGNORE) {
335 const ContentFeatures &c_nx = ndef->get(vm->m_data[vi_neg_x]);
336 if (c_nx.floodable && !c_nx.isLiquid())
340 VoxelArea::add_x(em, vi_pos_x, +1);
341 if (vm->m_data[vi_pos_x].getContent() != CONTENT_IGNORE) {
342 const ContentFeatures &c_px = ndef->get(vm->m_data[vi_pos_x]);
343 if (c_px.floodable && !c_px.isLiquid())
347 VoxelArea::add_z(em, vi_neg_z, -1);
348 if (vm->m_data[vi_neg_z].getContent() != CONTENT_IGNORE) {
349 const ContentFeatures &c_nz = ndef->get(vm->m_data[vi_neg_z]);
350 if (c_nz.floodable && !c_nz.isLiquid())
354 VoxelArea::add_z(em, vi_pos_z, +1);
355 if (vm->m_data[vi_pos_z].getContent() != CONTENT_IGNORE) {
356 const ContentFeatures &c_pz = ndef->get(vm->m_data[vi_pos_z]);
357 if (c_pz.floodable && !c_pz.isLiquid())
363 void Mapgen::updateLiquid(UniqueQueue<v3s16> *trans_liquid, v3s16 nmin, v3s16 nmax)
365 bool isignored, isliquid, wasignored, wasliquid, waschecked, waspushed;
366 const v3s16 &em = vm->m_area.getExtent();
368 for (s16 z = nmin.Z + 1; z <= nmax.Z - 1; z++)
369 for (s16 x = nmin.X + 1; x <= nmax.X - 1; x++) {
375 u32 vi = vm->m_area.index(x, nmax.Y, z);
376 for (s16 y = nmax.Y; y >= nmin.Y; y--) {
377 isignored = vm->m_data[vi].getContent() == CONTENT_IGNORE;
378 isliquid = ndef->get(vm->m_data[vi]).isLiquid();
380 if (isignored || wasignored || isliquid == wasliquid) {
381 // Neither topmost node of liquid column nor topmost node below column
384 } else if (isliquid) {
385 // This is the topmost node in the column
386 bool ispushed = false;
387 if (isLiquidHorizontallyFlowable(vi, em)) {
388 trans_liquid->push_back(v3s16(x, y, z));
391 // Remember waschecked and waspushed to avoid repeated
392 // checks/pushes in case the column consists of only this node
394 waspushed = ispushed;
396 // This is the topmost node below a liquid column
398 VoxelArea::add_y(em, vi_above, 1);
399 if (!waspushed && (ndef->get(vm->m_data[vi]).floodable ||
400 (!waschecked && isLiquidHorizontallyFlowable(vi_above, em)))) {
401 // Push back the lowest node in the column which is one
402 // node above this one
403 trans_liquid->push_back(v3s16(x, y + 1, z));
407 wasliquid = isliquid;
408 wasignored = isignored;
409 VoxelArea::add_y(em, vi, -1);
415 void Mapgen::setLighting(u8 light, v3s16 nmin, v3s16 nmax)
417 ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG);
418 VoxelArea a(nmin, nmax);
420 for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) {
421 for (int y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) {
422 u32 i = vm->m_area.index(a.MinEdge.X, y, z);
423 for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++, i++)
424 vm->m_data[i].param1 = light;
430 void Mapgen::lightSpread(VoxelArea &a, v3s16 p, u8 light)
432 if (light <= 1 || !a.contains(p))
435 u32 vi = vm->m_area.index(p);
436 MapNode &n = vm->m_data[vi];
438 // Decay light in each of the banks separately
439 u8 light_day = light & 0x0F;
443 u8 light_night = light & 0xF0;
447 // Bail out only if we have no more light from either bank to propogate, or
448 // we hit a solid block that light cannot pass through.
449 if ((light_day <= (n.param1 & 0x0F) &&
450 light_night <= (n.param1 & 0xF0)) ||
451 !ndef->get(n).light_propagates)
454 // Since this recursive function only terminates when there is no light from
455 // either bank left, we need to take the max of both banks into account for
456 // the case where spreading has stopped for one light bank but not the other.
457 light = MYMAX(light_day, n.param1 & 0x0F) |
458 MYMAX(light_night, n.param1 & 0xF0);
462 lightSpread(a, p + v3s16(0, 0, 1), light);
463 lightSpread(a, p + v3s16(0, 1, 0), light);
464 lightSpread(a, p + v3s16(1, 0, 0), light);
465 lightSpread(a, p - v3s16(0, 0, 1), light);
466 lightSpread(a, p - v3s16(0, 1, 0), light);
467 lightSpread(a, p - v3s16(1, 0, 0), light);
471 void Mapgen::calcLighting(v3s16 nmin, v3s16 nmax, v3s16 full_nmin, v3s16 full_nmax,
472 bool propagate_shadow)
474 ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG);
475 //TimeTaker t("updateLighting");
477 propagateSunlight(nmin, nmax, propagate_shadow);
478 spreadLight(full_nmin, full_nmax);
480 //printf("updateLighting: %dms\n", t.stop());
484 void Mapgen::propagateSunlight(v3s16 nmin, v3s16 nmax, bool propagate_shadow)
486 //TimeTaker t("propagateSunlight");
487 VoxelArea a(nmin, nmax);
488 bool block_is_underground = (water_level >= nmax.Y);
489 const v3s16 &em = vm->m_area.getExtent();
491 // NOTE: Direct access to the low 4 bits of param1 is okay here because,
492 // by definition, sunlight will never be in the night lightbank.
494 for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) {
495 for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++) {
496 // see if we can get a light value from the overtop
497 u32 i = vm->m_area.index(x, a.MaxEdge.Y + 1, z);
498 if (vm->m_data[i].getContent() == CONTENT_IGNORE) {
499 if (block_is_underground)
501 } else if ((vm->m_data[i].param1 & 0x0F) != LIGHT_SUN &&
505 VoxelArea::add_y(em, i, -1);
507 for (int y = a.MaxEdge.Y; y >= a.MinEdge.Y; y--) {
508 MapNode &n = vm->m_data[i];
509 if (!ndef->get(n).sunlight_propagates)
511 n.param1 = LIGHT_SUN;
512 VoxelArea::add_y(em, i, -1);
516 //printf("propagateSunlight: %dms\n", t.stop());
520 void Mapgen::spreadLight(v3s16 nmin, v3s16 nmax)
522 //TimeTaker t("spreadLight");
523 VoxelArea a(nmin, nmax);
525 for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) {
526 for (int y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) {
527 u32 i = vm->m_area.index(a.MinEdge.X, y, z);
528 for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++, i++) {
529 MapNode &n = vm->m_data[i];
530 if (n.getContent() == CONTENT_IGNORE)
533 const ContentFeatures &cf = ndef->get(n);
534 if (!cf.light_propagates)
537 // TODO(hmmmmm): Abstract away direct param1 accesses with a
538 // wrapper, but something lighter than MapNode::get/setLight
540 u8 light_produced = cf.light_source;
542 n.param1 = light_produced | (light_produced << 4);
546 lightSpread(a, v3s16(x, y, z + 1), light);
547 lightSpread(a, v3s16(x, y + 1, z ), light);
548 lightSpread(a, v3s16(x + 1, y, z ), light);
549 lightSpread(a, v3s16(x, y, z - 1), light);
550 lightSpread(a, v3s16(x, y - 1, z ), light);
551 lightSpread(a, v3s16(x - 1, y, z ), light);
557 //printf("spreadLight: %dms\n", t.stop());
565 MapgenBasic::MapgenBasic(int mapgenid, MapgenParams *params, EmergeManager *emerge)
566 : Mapgen(mapgenid, params, emerge)
568 this->m_emerge = emerge;
569 this->m_bmgr = emerge->biomemgr;
571 //// Here, 'stride' refers to the number of elements needed to skip to index
572 //// an adjacent element for that coordinate in noise/height/biome maps
573 //// (*not* vmanip content map!)
575 // Note there is no X stride explicitly defined. Items adjacent in the X
576 // coordinate are assumed to be adjacent in memory as well (i.e. stride of 1).
578 // Number of elements to skip to get to the next Y coordinate
579 this->ystride = csize.X;
581 // Number of elements to skip to get to the next Z coordinate
582 this->zstride = csize.X * csize.Y;
584 // Z-stride value for maps oversized for 1-down overgeneration
585 this->zstride_1d = csize.X * (csize.Y + 1);
587 // Z-stride value for maps oversized for 1-up 1-down overgeneration
588 this->zstride_1u1d = csize.X * (csize.Y + 2);
590 //// Allocate heightmap
591 this->heightmap = new s16[csize.X * csize.Z];
593 //// Initialize biome generator
594 // TODO(hmmmm): should we have a way to disable biomemanager biomes?
595 biomegen = m_bmgr->createBiomeGen(BIOMEGEN_ORIGINAL, params->bparams, csize);
596 biomemap = biomegen->biomemap;
598 //// Look up some commonly used content
599 c_stone = ndef->getId("mapgen_stone");
600 c_desert_stone = ndef->getId("mapgen_desert_stone");
601 c_sandstone = ndef->getId("mapgen_sandstone");
602 c_water_source = ndef->getId("mapgen_water_source");
603 c_river_water_source = ndef->getId("mapgen_river_water_source");
604 c_lava_source = ndef->getId("mapgen_lava_source");
606 // Fall back to more basic content if not defined
607 // river_water_source cannot fallback to water_source because river water
608 // needs to be non-renewable and have a short flow range.
609 if (c_desert_stone == CONTENT_IGNORE)
610 c_desert_stone = c_stone;
611 if (c_sandstone == CONTENT_IGNORE)
612 c_sandstone = c_stone;
614 //// Content used for dungeon generation
615 c_cobble = ndef->getId("mapgen_cobble");
616 c_mossycobble = ndef->getId("mapgen_mossycobble");
617 c_stair_cobble = ndef->getId("mapgen_stair_cobble");
618 c_stair_desert_stone = ndef->getId("mapgen_stair_desert_stone");
619 c_sandstonebrick = ndef->getId("mapgen_sandstonebrick");
620 c_stair_sandstone_block = ndef->getId("mapgen_stair_sandstone_block");
622 // Fall back to more basic content if not defined
623 if (c_mossycobble == CONTENT_IGNORE)
624 c_mossycobble = c_cobble;
625 if (c_stair_cobble == CONTENT_IGNORE)
626 c_stair_cobble = c_cobble;
627 if (c_stair_desert_stone == CONTENT_IGNORE)
628 c_stair_desert_stone = c_desert_stone;
629 if (c_sandstonebrick == CONTENT_IGNORE)
630 c_sandstonebrick = c_sandstone;
631 if (c_stair_sandstone_block == CONTENT_IGNORE)
632 c_stair_sandstone_block = c_sandstonebrick;
636 MapgenBasic::~MapgenBasic()
643 void MapgenBasic::generateBiomes()
645 // can't generate biomes without a biome generator!
649 const v3s16 &em = vm->m_area.getExtent();
652 noise_filler_depth->perlinMap2D(node_min.X, node_min.Z);
654 for (s16 z = node_min.Z; z <= node_max.Z; z++)
655 for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
659 u16 depth_water_top = 0;
660 u16 depth_riverbed = 0;
661 s16 biome_y_min = -MAX_MAP_GENERATION_LIMIT;
662 u32 vi = vm->m_area.index(x, node_max.Y, z);
664 // Check node at base of mapchunk above, either a node of a previously
665 // generated mapchunk or if not, a node of overgenerated base terrain.
666 content_t c_above = vm->m_data[vi + em.X].getContent();
667 bool air_above = c_above == CONTENT_AIR;
668 bool river_water_above = c_above == c_river_water_source;
669 bool water_above = c_above == c_water_source || river_water_above;
671 biomemap[index] = BIOME_NONE;
673 // If there is air or water above enable top/filler placement, otherwise force
674 // nplaced to stone level by setting a number exceeding any possible filler depth.
675 u16 nplaced = (air_above || water_above) ? 0 : U16_MAX;
677 for (s16 y = node_max.Y; y >= node_min.Y; y--) {
678 content_t c = vm->m_data[vi].getContent();
679 // Biome is (re)calculated:
680 // 1. At the surface of stone below air or water.
681 // 2. At the surface of water below air.
682 // 3. When stone or water is detected but biome has not yet been calculated.
683 // 4. When stone or water is detected just below a biome's lower limit.
684 bool is_stone_surface = (c == c_stone) &&
685 (air_above || water_above || !biome || y < biome_y_min); // 1, 3, 4
687 bool is_water_surface =
688 (c == c_water_source || c == c_river_water_source) &&
689 (air_above || !biome || y < biome_y_min); // 2, 3, 4
691 if (is_stone_surface || is_water_surface) {
692 // (Re)calculate biome
693 biome = biomegen->getBiomeAtIndex(index, v3s16(x, y, z));
695 // Add biome to biomemap at first stone surface detected
696 if (biomemap[index] == BIOME_NONE && is_stone_surface)
697 biomemap[index] = biome->index;
699 depth_top = biome->depth_top;
700 base_filler = MYMAX(depth_top +
701 biome->depth_filler +
702 noise_filler_depth->result[index], 0.0f);
703 depth_water_top = biome->depth_water_top;
704 depth_riverbed = biome->depth_riverbed;
705 biome_y_min = biome->min_pos.Y;
709 content_t c_below = vm->m_data[vi - em.X].getContent();
711 // If the node below isn't solid, make this node stone, so that
712 // any top/filler nodes above are structurally supported.
713 // This is done by aborting the cycle of top/filler placement
714 // immediately by forcing nplaced to stone level.
715 if (c_below == CONTENT_AIR
716 || c_below == c_water_source
717 || c_below == c_river_water_source)
720 if (river_water_above) {
721 if (nplaced < depth_riverbed) {
722 vm->m_data[vi] = MapNode(biome->c_riverbed);
725 nplaced = U16_MAX; // Disable top/filler placement
726 river_water_above = false;
728 } else if (nplaced < depth_top) {
729 vm->m_data[vi] = MapNode(biome->c_top);
731 } else if (nplaced < base_filler) {
732 vm->m_data[vi] = MapNode(biome->c_filler);
735 vm->m_data[vi] = MapNode(biome->c_stone);
736 nplaced = U16_MAX; // Disable top/filler placement
741 } else if (c == c_water_source) {
742 vm->m_data[vi] = MapNode((y > (s32)(water_level - depth_water_top))
743 ? biome->c_water_top : biome->c_water);
744 nplaced = 0; // Enable top/filler placement for next surface
747 } else if (c == c_river_water_source) {
748 vm->m_data[vi] = MapNode(biome->c_river_water);
749 nplaced = 0; // Enable riverbed placement for next surface
752 river_water_above = true;
753 } else if (c == CONTENT_AIR) {
754 nplaced = 0; // Enable top/filler placement for next surface
757 } else { // Possible various nodes overgenerated from neighbouring mapchunks
758 nplaced = U16_MAX; // Disable top/filler placement
763 VoxelArea::add_y(em, vi, -1);
765 // If no stone surface was detected in this mapchunk column the biomemap
766 // will be empty for this (x, z) position. Add the currently active
767 // biome to the biomemap, or if biome is NULL calculate it for this
768 // position and add it.
769 if (biomemap[index] == BIOME_NONE) {
771 biome = biomegen->getBiomeAtIndex(
772 index, v3s16(x, node_min.Y, z));
773 biomemap[index] = biome->index;
779 void MapgenBasic::dustTopNodes()
781 if (node_max.Y < water_level)
784 const v3s16 &em = vm->m_area.getExtent();
787 for (s16 z = node_min.Z; z <= node_max.Z; z++)
788 for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
789 Biome *biome = (Biome *)m_bmgr->getRaw(biomemap[index]);
791 if (biome->c_dust == CONTENT_IGNORE)
794 u32 vi = vm->m_area.index(x, full_node_max.Y, z);
795 content_t c_full_max = vm->m_data[vi].getContent();
798 if (c_full_max == CONTENT_AIR) {
799 y_start = full_node_max.Y - 1;
800 } else if (c_full_max == CONTENT_IGNORE) {
801 vi = vm->m_area.index(x, node_max.Y + 1, z);
802 content_t c_max = vm->m_data[vi].getContent();
804 if (c_max == CONTENT_AIR)
805 y_start = node_max.Y;
812 vi = vm->m_area.index(x, y_start, z);
813 for (s16 y = y_start; y >= node_min.Y - 1; y--) {
814 if (vm->m_data[vi].getContent() != CONTENT_AIR)
817 VoxelArea::add_y(em, vi, -1);
820 content_t c = vm->m_data[vi].getContent();
821 NodeDrawType dtype = ndef->get(c).drawtype;
822 // Only place on walkable cubic non-liquid nodes
823 // Dust check needed due to vertical overgeneration
824 if ((dtype == NDT_NORMAL ||
825 dtype == NDT_ALLFACES_OPTIONAL ||
826 dtype == NDT_GLASSLIKE_FRAMED_OPTIONAL ||
827 dtype == NDT_GLASSLIKE ||
828 dtype == NDT_GLASSLIKE_FRAMED ||
829 dtype == NDT_ALLFACES) &&
830 ndef->get(c).walkable && c != biome->c_dust) {
831 VoxelArea::add_y(em, vi, 1);
832 vm->m_data[vi] = MapNode(biome->c_dust);
838 void MapgenBasic::generateCavesNoiseIntersection(s16 max_stone_y)
840 if (node_min.Y > max_stone_y)
843 CavesNoiseIntersection caves_noise(ndef, m_bmgr, csize,
844 &np_cave1, &np_cave2, seed, cave_width);
846 caves_noise.generateCaves(vm, node_min, node_max, biomemap);
850 void MapgenBasic::generateCavesRandomWalk(s16 max_stone_y, s16 large_cave_depth)
852 if (node_min.Y > max_stone_y || node_max.Y > large_cave_depth)
855 PseudoRandom ps(blockseed + 21343);
856 u32 bruises_count = ps.range(0, 2);
858 for (u32 i = 0; i < bruises_count; i++) {
859 CavesRandomWalk cave(ndef, &gennotify, seed, water_level,
860 c_water_source, c_lava_source, lava_depth, biomegen);
862 cave.makeCave(vm, node_min, node_max, &ps, true, max_stone_y,
868 bool MapgenBasic::generateCavernsNoise(s16 max_stone_y)
870 if (node_min.Y > max_stone_y || node_min.Y > cavern_limit)
873 CavernsNoise caverns_noise(ndef, csize, &np_cavern,
874 seed, cavern_limit, cavern_taper, cavern_threshold);
876 return caverns_noise.generateCaverns(vm, node_min, node_max);
880 void MapgenBasic::generateDungeons(s16 max_stone_y)
882 if (max_stone_y < node_min.Y)
885 // Get biome at mapchunk midpoint
886 v3s16 chunk_mid = node_min + (node_max - node_min) / v3s16(2, 2, 2);
887 Biome *biome = (Biome *)biomegen->getBiomeAtPoint(chunk_mid);
892 dp.only_in_ground = true;
893 dp.corridor_len_min = 1;
894 dp.corridor_len_max = 13;
898 dp.np_density = nparams_dungeon_density;
899 dp.np_alt_wall = nparams_dungeon_alt_wall;
901 // Biome-defined dungeon nodes
902 if (biome->c_dungeon != CONTENT_IGNORE) {
903 dp.c_wall = biome->c_dungeon;
904 // If 'node_dungeon_alt' is not defined by biome, it and dp.c_alt_wall
905 // become CONTENT_IGNORE which skips the alt wall node placement loop in
907 dp.c_alt_wall = biome->c_dungeon_alt;
908 // Stairs fall back to 'c_dungeon' if not defined by biome
909 dp.c_stair = (biome->c_dungeon_stair != CONTENT_IGNORE) ?
910 biome->c_dungeon_stair : biome->c_dungeon;
912 dp.diagonal_dirs = false;
913 dp.holesize = v3s16(2, 2, 2);
914 dp.room_size_min = v3s16(6, 4, 6);
915 dp.room_size_max = v3s16(10, 6, 10);
916 dp.room_size_large_min = v3s16(10, 8, 10);
917 dp.room_size_large_max = v3s16(18, 16, 18);
918 dp.notifytype = GENNOTIFY_DUNGEON;
920 // Otherwise classic behaviour
921 } else if (biome->c_stone == c_stone) {
922 dp.c_wall = c_cobble;
923 dp.c_alt_wall = c_mossycobble;
924 dp.c_stair = c_stair_cobble;
926 dp.diagonal_dirs = false;
927 dp.holesize = v3s16(1, 2, 1);
928 dp.room_size_min = v3s16(4, 4, 4);
929 dp.room_size_max = v3s16(8, 6, 8);
930 dp.room_size_large_min = v3s16(8, 8, 8);
931 dp.room_size_large_max = v3s16(16, 16, 16);
932 dp.notifytype = GENNOTIFY_DUNGEON;
934 } else if (biome->c_stone == c_desert_stone) {
935 dp.c_wall = c_desert_stone;
936 dp.c_alt_wall = CONTENT_IGNORE;
937 dp.c_stair = c_stair_desert_stone;
939 dp.diagonal_dirs = true;
940 dp.holesize = v3s16(2, 3, 2);
941 dp.room_size_min = v3s16(6, 9, 6);
942 dp.room_size_max = v3s16(10, 11, 10);
943 dp.room_size_large_min = v3s16(10, 13, 10);
944 dp.room_size_large_max = v3s16(18, 21, 18);
945 dp.notifytype = GENNOTIFY_TEMPLE;
947 } else if (biome->c_stone == c_sandstone) {
948 dp.c_wall = c_sandstonebrick;
949 dp.c_alt_wall = CONTENT_IGNORE;
950 dp.c_stair = c_stair_sandstone_block;
952 dp.diagonal_dirs = false;
953 dp.holesize = v3s16(2, 2, 2);
954 dp.room_size_min = v3s16(6, 4, 6);
955 dp.room_size_max = v3s16(10, 6, 10);
956 dp.room_size_large_min = v3s16(10, 8, 10);
957 dp.room_size_large_max = v3s16(18, 16, 18);
958 dp.notifytype = GENNOTIFY_DUNGEON;
960 // Fallback to using biome 'node_stone'
962 dp.c_wall = biome->c_stone;
963 dp.c_alt_wall = CONTENT_IGNORE;
964 dp.c_stair = biome->c_stone;
966 dp.diagonal_dirs = false;
967 dp.holesize = v3s16(2, 2, 2);
968 dp.room_size_min = v3s16(6, 4, 6);
969 dp.room_size_max = v3s16(10, 6, 10);
970 dp.room_size_large_min = v3s16(10, 8, 10);
971 dp.room_size_large_max = v3s16(18, 16, 18);
972 dp.notifytype = GENNOTIFY_DUNGEON;
975 DungeonGen dgen(ndef, &gennotify, &dp);
976 dgen.generate(vm, blockseed, full_node_min, full_node_max);
981 //// GenerateNotifier
984 GenerateNotifier::GenerateNotifier(u32 notify_on,
985 std::set<u32> *notify_on_deco_ids)
987 m_notify_on = notify_on;
988 m_notify_on_deco_ids = notify_on_deco_ids;
992 void GenerateNotifier::setNotifyOn(u32 notify_on)
994 m_notify_on = notify_on;
998 void GenerateNotifier::setNotifyOnDecoIds(std::set<u32> *notify_on_deco_ids)
1000 m_notify_on_deco_ids = notify_on_deco_ids;
1004 bool GenerateNotifier::addEvent(GenNotifyType type, v3s16 pos, u32 id)
1006 if (!(m_notify_on & (1 << type)))
1009 if (type == GENNOTIFY_DECORATION &&
1010 m_notify_on_deco_ids->find(id) == m_notify_on_deco_ids->end())
1017 m_notify_events.push_back(gne);
1023 void GenerateNotifier::getEvents(
1024 std::map<std::string, std::vector<v3s16> > &event_map)
1026 std::list<GenNotifyEvent>::iterator it;
1028 for (it = m_notify_events.begin(); it != m_notify_events.end(); ++it) {
1029 GenNotifyEvent &gn = *it;
1030 std::string name = (gn.type == GENNOTIFY_DECORATION) ?
1031 "decoration#"+ itos(gn.id) :
1032 flagdesc_gennotify[gn.type].name;
1034 event_map[name].push_back(gn.pos);
1039 void GenerateNotifier::clearEvents()
1041 m_notify_events.clear();
1050 MapgenParams::~MapgenParams()
1056 void MapgenParams::readParams(const Settings *settings)
1058 std::string seed_str;
1059 const char *seed_name = (settings == g_settings) ? "fixed_map_seed" : "seed";
1061 if (settings->getNoEx(seed_name, seed_str)) {
1062 if (!seed_str.empty())
1063 seed = read_seed(seed_str.c_str());
1065 myrand_bytes(&seed, sizeof(seed));
1068 std::string mg_name;
1069 if (settings->getNoEx("mg_name", mg_name)) {
1070 mgtype = Mapgen::getMapgenType(mg_name);
1071 if (mgtype == MAPGEN_INVALID)
1072 mgtype = MAPGEN_DEFAULT;
1075 settings->getS16NoEx("water_level", water_level);
1076 settings->getS16NoEx("mapgen_limit", mapgen_limit);
1077 settings->getS16NoEx("chunksize", chunksize);
1078 settings->getFlagStrNoEx("mg_flags", flags, flagdesc_mapgen);
1081 bparams = BiomeManager::createBiomeParams(BIOMEGEN_ORIGINAL);
1083 bparams->readParams(settings);
1084 bparams->seed = seed;
1089 void MapgenParams::writeParams(Settings *settings) const
1091 settings->set("mg_name", Mapgen::getMapgenName(mgtype));
1092 settings->setU64("seed", seed);
1093 settings->setS16("water_level", water_level);
1094 settings->setS16("mapgen_limit", mapgen_limit);
1095 settings->setS16("chunksize", chunksize);
1096 settings->setFlagStr("mg_flags", flags, flagdesc_mapgen, U32_MAX);
1099 bparams->writeParams(settings);
1103 // Calculate exact edges of the outermost mapchunks that are within the
1104 // set 'mapgen_limit'.
1105 void MapgenParams::calcMapgenEdges()
1107 // Central chunk offset, in blocks
1108 s16 ccoff_b = -chunksize / 2;
1109 // Chunksize, in nodes
1110 s32 csize_n = chunksize * MAP_BLOCKSIZE;
1111 // Minp/maxp of central chunk, in nodes
1112 s16 ccmin = ccoff_b * MAP_BLOCKSIZE;
1113 s16 ccmax = ccmin + csize_n - 1;
1114 // Fullminp/fullmaxp of central chunk, in nodes
1115 s16 ccfmin = ccmin - MAP_BLOCKSIZE;
1116 s16 ccfmax = ccmax + MAP_BLOCKSIZE;
1117 // Effective mapgen limit, in blocks
1118 // Uses same calculation as ServerMap::blockpos_over_mapgen_limit(v3s16 p)
1119 s16 mapgen_limit_b = rangelim(mapgen_limit,
1120 0, MAX_MAP_GENERATION_LIMIT) / MAP_BLOCKSIZE;
1121 // Effective mapgen limits, in nodes
1122 s16 mapgen_limit_min = -mapgen_limit_b * MAP_BLOCKSIZE;
1123 s16 mapgen_limit_max = (mapgen_limit_b + 1) * MAP_BLOCKSIZE - 1;
1124 // Number of complete chunks from central chunk fullminp/fullmaxp
1125 // to effective mapgen limits.
1126 s16 numcmin = MYMAX((ccfmin - mapgen_limit_min) / csize_n, 0);
1127 s16 numcmax = MYMAX((mapgen_limit_max - ccfmax) / csize_n, 0);
1128 // Mapgen edges, in nodes
1129 mapgen_edge_min = ccmin - numcmin * csize_n;
1130 mapgen_edge_max = ccmax + numcmax * csize_n;
1132 m_mapgen_edges_calculated = true;
1136 s32 MapgenParams::getSpawnRangeMax()
1138 if (!m_mapgen_edges_calculated)
1141 return MYMIN(-mapgen_edge_min, mapgen_edge_max);