3 Copyright (C) 2010-2015 celeron55, Perttu Ahola <celeron55@gmail.com>
4 Copyright (C) 2013-2016 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
5 Copyright (C) 2015-2017 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_flat.h"
44 #include "mapgen_fractal.h"
45 #include "mapgen_v5.h"
46 #include "mapgen_v6.h"
47 #include "mapgen_v7.h"
48 #include "mapgen_valleys.h"
49 #include "mapgen_singlenode.h"
51 #include "dungeongen.h"
53 FlagDesc flagdesc_mapgen[] = {
55 {"dungeons", MG_DUNGEONS},
57 {"decorations", MG_DECORATIONS},
61 FlagDesc flagdesc_gennotify[] = {
62 {"dungeon", 1 << GENNOTIFY_DUNGEON},
63 {"temple", 1 << GENNOTIFY_TEMPLE},
64 {"cave_begin", 1 << GENNOTIFY_CAVE_BEGIN},
65 {"cave_end", 1 << GENNOTIFY_CAVE_END},
66 {"large_cave_begin", 1 << GENNOTIFY_LARGECAVE_BEGIN},
67 {"large_cave_end", 1 << GENNOTIFY_LARGECAVE_END},
68 {"decoration", 1 << GENNOTIFY_DECORATION},
81 static MapgenDesc g_reg_mapgens[] = {
92 ARRLEN(g_reg_mapgens) == MAPGEN_INVALID,
93 registered_mapgens_is_wrong_size);
116 Mapgen::Mapgen(int mapgenid, MapgenParams *params, EmergeManager *emerge) :
117 gennotify(emerge->gen_notify_on, &emerge->gen_notify_on_deco_ids)
121 water_level = params->water_level;
122 mapgen_limit = params->mapgen_limit;
123 flags = params->flags;
124 csize = v3s16(1, 1, 1) * (params->chunksize * MAP_BLOCKSIZE);
127 We are losing half our entropy by doing this, but it is necessary to
128 preserve reverse compatibility. If the top half of our current 64 bit
129 seeds ever starts getting used, existing worlds will break due to a
130 different hash outcome and no way to differentiate between versions.
132 A solution could be to add a new bit to designate that the top half of
133 the seed value should be used, essentially a 1-bit version code, but
134 this would require increasing the total size of a seed to 9 bytes (yuck)
136 It's probably okay if this never gets fixed. 4.2 billion possibilities
137 ought to be enough for anyone.
139 seed = (s32)params->seed;
154 MapgenType Mapgen::getMapgenType(const std::string &mgname)
156 for (size_t i = 0; i != ARRLEN(g_reg_mapgens); i++) {
157 if (mgname == g_reg_mapgens[i].name)
158 return (MapgenType)i;
161 return MAPGEN_INVALID;
165 const char *Mapgen::getMapgenName(MapgenType mgtype)
167 size_t index = (size_t)mgtype;
168 if (index == MAPGEN_INVALID || index >= ARRLEN(g_reg_mapgens))
171 return g_reg_mapgens[index].name;
175 Mapgen *Mapgen::createMapgen(MapgenType mgtype, int mgid,
176 MapgenParams *params, EmergeManager *emerge)
180 return new MapgenFlat(mgid, (MapgenFlatParams *)params, emerge);
182 return new MapgenFractal(mgid, (MapgenFractalParams *)params, emerge);
183 case MAPGEN_SINGLENODE:
184 return new MapgenSinglenode(mgid, (MapgenSinglenodeParams *)params, emerge);
186 return new MapgenV5(mgid, (MapgenV5Params *)params, emerge);
188 return new MapgenV6(mgid, (MapgenV6Params *)params, emerge);
190 return new MapgenV7(mgid, (MapgenV7Params *)params, emerge);
192 return new MapgenValleys(mgid, (MapgenValleysParams *)params, emerge);
199 MapgenParams *Mapgen::createMapgenParams(MapgenType mgtype)
203 return new MapgenFlatParams;
205 return new MapgenFractalParams;
206 case MAPGEN_SINGLENODE:
207 return new MapgenSinglenodeParams;
209 return new MapgenV5Params;
211 return new MapgenV6Params;
213 return new MapgenV7Params;
215 return new MapgenValleysParams;
222 void Mapgen::getMapgenNames(std::vector<const char *> *mgnames, bool include_hidden)
224 for (u32 i = 0; i != ARRLEN(g_reg_mapgens); i++) {
225 if (include_hidden || g_reg_mapgens[i].is_user_visible)
226 mgnames->push_back(g_reg_mapgens[i].name);
231 u32 Mapgen::getBlockSeed(v3s16 p, s32 seed)
240 u32 Mapgen::getBlockSeed2(v3s16 p, s32 seed)
242 u32 n = 1619 * p.X + 31337 * p.Y + 52591 * p.Z + 1013 * seed;
244 return (n * (n * n * 60493 + 19990303) + 1376312589);
248 // Returns Y one under area minimum if not found
249 s16 Mapgen::findGroundLevelFull(v2s16 p2d)
251 v3s16 em = vm->m_area.getExtent();
252 s16 y_nodes_max = vm->m_area.MaxEdge.Y;
253 s16 y_nodes_min = vm->m_area.MinEdge.Y;
254 u32 i = vm->m_area.index(p2d.X, y_nodes_max, p2d.Y);
257 for (y = y_nodes_max; y >= y_nodes_min; y--) {
258 MapNode &n = vm->m_data[i];
259 if (ndef->get(n).walkable)
262 vm->m_area.add_y(em, i, -1);
264 return (y >= y_nodes_min) ? y : y_nodes_min - 1;
268 // Returns -MAX_MAP_GENERATION_LIMIT if not found
269 s16 Mapgen::findGroundLevel(v2s16 p2d, s16 ymin, s16 ymax)
271 v3s16 em = vm->m_area.getExtent();
272 u32 i = vm->m_area.index(p2d.X, ymax, p2d.Y);
275 for (y = ymax; y >= ymin; y--) {
276 MapNode &n = vm->m_data[i];
277 if (ndef->get(n).walkable)
280 vm->m_area.add_y(em, i, -1);
282 return (y >= ymin) ? y : -MAX_MAP_GENERATION_LIMIT;
286 // Returns -MAX_MAP_GENERATION_LIMIT if not found or if ground is found first
287 s16 Mapgen::findLiquidSurface(v2s16 p2d, s16 ymin, s16 ymax)
289 v3s16 em = vm->m_area.getExtent();
290 u32 i = vm->m_area.index(p2d.X, ymax, p2d.Y);
293 for (y = ymax; y >= ymin; y--) {
294 MapNode &n = vm->m_data[i];
295 if (ndef->get(n).walkable)
296 return -MAX_MAP_GENERATION_LIMIT;
297 else if (ndef->get(n).isLiquid())
300 vm->m_area.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;
322 inline bool Mapgen::isLiquidHorizontallyFlowable(u32 vi, v3s16 em)
325 vm->m_area.add_x(em, vi_neg_x, -1);
326 if (vm->m_data[vi_neg_x].getContent() != CONTENT_IGNORE) {
327 const ContentFeatures &c_nx = ndef->get(vm->m_data[vi_neg_x]);
328 if (c_nx.floodable && !c_nx.isLiquid())
332 vm->m_area.add_x(em, vi_pos_x, +1);
333 if (vm->m_data[vi_pos_x].getContent() != CONTENT_IGNORE) {
334 const ContentFeatures &c_px = ndef->get(vm->m_data[vi_pos_x]);
335 if (c_px.floodable && !c_px.isLiquid())
339 vm->m_area.add_z(em, vi_neg_z, -1);
340 if (vm->m_data[vi_neg_z].getContent() != CONTENT_IGNORE) {
341 const ContentFeatures &c_nz = ndef->get(vm->m_data[vi_neg_z]);
342 if (c_nz.floodable && !c_nz.isLiquid())
346 vm->m_area.add_z(em, vi_pos_z, +1);
347 if (vm->m_data[vi_pos_z].getContent() != CONTENT_IGNORE) {
348 const ContentFeatures &c_pz = ndef->get(vm->m_data[vi_pos_z]);
349 if (c_pz.floodable && !c_pz.isLiquid())
355 void Mapgen::updateLiquid(UniqueQueue<v3s16> *trans_liquid, v3s16 nmin, v3s16 nmax)
357 bool isignored, isliquid, wasignored, wasliquid, waschecked, waspushed;
358 v3s16 em = vm->m_area.getExtent();
360 for (s16 z = nmin.Z + 1; z <= nmax.Z - 1; z++)
361 for (s16 x = nmin.X + 1; x <= nmax.X - 1; x++) {
367 u32 vi = vm->m_area.index(x, nmax.Y, z);
368 for (s16 y = nmax.Y; y >= nmin.Y; y--) {
369 isignored = vm->m_data[vi].getContent() == CONTENT_IGNORE;
370 isliquid = ndef->get(vm->m_data[vi]).isLiquid();
372 if (isignored || wasignored || isliquid == wasliquid) {
373 // Neither topmost node of liquid column nor topmost node below column
376 } else if (isliquid) {
377 // This is the topmost node in the column
378 bool ispushed = false;
379 if (isLiquidHorizontallyFlowable(vi, em)) {
380 trans_liquid->push_back(v3s16(x, y, z));
383 // Remember waschecked and waspushed to avoid repeated
384 // checks/pushes in case the column consists of only this node
386 waspushed = ispushed;
388 // This is the topmost node below a liquid column
390 vm->m_area.add_y(em, vi_above, 1);
391 if (!waspushed && (ndef->get(vm->m_data[vi]).floodable ||
392 (!waschecked && isLiquidHorizontallyFlowable(vi_above, em)))) {
393 // Push back the lowest node in the column which is one
394 // node above this one
395 trans_liquid->push_back(v3s16(x, y + 1, z));
399 wasliquid = isliquid;
400 wasignored = isignored;
401 vm->m_area.add_y(em, vi, -1);
407 void Mapgen::setLighting(u8 light, v3s16 nmin, v3s16 nmax)
409 ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG);
410 VoxelArea a(nmin, nmax);
412 for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) {
413 for (int y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) {
414 u32 i = vm->m_area.index(a.MinEdge.X, y, z);
415 for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++, i++)
416 vm->m_data[i].param1 = light;
422 void Mapgen::lightSpread(VoxelArea &a, v3s16 p, u8 light)
424 if (light <= 1 || !a.contains(p))
427 u32 vi = vm->m_area.index(p);
428 MapNode &n = vm->m_data[vi];
430 // Decay light in each of the banks separately
431 u8 light_day = light & 0x0F;
435 u8 light_night = light & 0xF0;
439 // Bail out only if we have no more light from either bank to propogate, or
440 // we hit a solid block that light cannot pass through.
441 if ((light_day <= (n.param1 & 0x0F) &&
442 light_night <= (n.param1 & 0xF0)) ||
443 !ndef->get(n).light_propagates)
446 // Since this recursive function only terminates when there is no light from
447 // either bank left, we need to take the max of both banks into account for
448 // the case where spreading has stopped for one light bank but not the other.
449 light = MYMAX(light_day, n.param1 & 0x0F) |
450 MYMAX(light_night, n.param1 & 0xF0);
454 lightSpread(a, p + v3s16(0, 0, 1), light);
455 lightSpread(a, p + v3s16(0, 1, 0), light);
456 lightSpread(a, p + v3s16(1, 0, 0), light);
457 lightSpread(a, p - v3s16(0, 0, 1), light);
458 lightSpread(a, p - v3s16(0, 1, 0), light);
459 lightSpread(a, p - v3s16(1, 0, 0), light);
463 void Mapgen::calcLighting(v3s16 nmin, v3s16 nmax, v3s16 full_nmin, v3s16 full_nmax,
464 bool propagate_shadow)
466 ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG);
467 //TimeTaker t("updateLighting");
469 propagateSunlight(nmin, nmax, propagate_shadow);
470 spreadLight(full_nmin, full_nmax);
472 //printf("updateLighting: %dms\n", t.stop());
476 void Mapgen::propagateSunlight(v3s16 nmin, v3s16 nmax, bool propagate_shadow)
478 //TimeTaker t("propagateSunlight");
479 VoxelArea a(nmin, nmax);
480 bool block_is_underground = (water_level >= nmax.Y);
481 v3s16 em = vm->m_area.getExtent();
483 // NOTE: Direct access to the low 4 bits of param1 is okay here because,
484 // by definition, sunlight will never be in the night lightbank.
486 for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) {
487 for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++) {
488 // see if we can get a light value from the overtop
489 u32 i = vm->m_area.index(x, a.MaxEdge.Y + 1, z);
490 if (vm->m_data[i].getContent() == CONTENT_IGNORE) {
491 if (block_is_underground)
493 } else if ((vm->m_data[i].param1 & 0x0F) != LIGHT_SUN &&
497 vm->m_area.add_y(em, i, -1);
499 for (int y = a.MaxEdge.Y; y >= a.MinEdge.Y; y--) {
500 MapNode &n = vm->m_data[i];
501 if (!ndef->get(n).sunlight_propagates)
503 n.param1 = LIGHT_SUN;
504 vm->m_area.add_y(em, i, -1);
508 //printf("propagateSunlight: %dms\n", t.stop());
512 void Mapgen::spreadLight(v3s16 nmin, v3s16 nmax)
514 //TimeTaker t("spreadLight");
515 VoxelArea a(nmin, nmax);
517 for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) {
518 for (int y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) {
519 u32 i = vm->m_area.index(a.MinEdge.X, y, z);
520 for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++, i++) {
521 MapNode &n = vm->m_data[i];
522 if (n.getContent() == CONTENT_IGNORE)
525 const ContentFeatures &cf = ndef->get(n);
526 if (!cf.light_propagates)
529 // TODO(hmmmmm): Abstract away direct param1 accesses with a
530 // wrapper, but something lighter than MapNode::get/setLight
532 u8 light_produced = cf.light_source;
534 n.param1 = light_produced | (light_produced << 4);
538 lightSpread(a, v3s16(x, y, z + 1), light);
539 lightSpread(a, v3s16(x, y + 1, z ), light);
540 lightSpread(a, v3s16(x + 1, y, z ), light);
541 lightSpread(a, v3s16(x, y, z - 1), light);
542 lightSpread(a, v3s16(x, y - 1, z ), light);
543 lightSpread(a, v3s16(x - 1, y, z ), light);
549 //printf("spreadLight: %dms\n", t.stop());
557 MapgenBasic::MapgenBasic(int mapgenid, MapgenParams *params, EmergeManager *emerge)
558 : Mapgen(mapgenid, params, emerge)
560 this->m_emerge = emerge;
561 this->m_bmgr = emerge->biomemgr;
563 //// Here, 'stride' refers to the number of elements needed to skip to index
564 //// an adjacent element for that coordinate in noise/height/biome maps
565 //// (*not* vmanip content map!)
567 // Note there is no X stride explicitly defined. Items adjacent in the X
568 // coordinate are assumed to be adjacent in memory as well (i.e. stride of 1).
570 // Number of elements to skip to get to the next Y coordinate
571 this->ystride = csize.X;
573 // Number of elements to skip to get to the next Z coordinate
574 this->zstride = csize.X * csize.Y;
576 // Z-stride value for maps oversized for 1-down overgeneration
577 this->zstride_1d = csize.X * (csize.Y + 1);
579 // Z-stride value for maps oversized for 1-up 1-down overgeneration
580 this->zstride_1u1d = csize.X * (csize.Y + 2);
582 //// Allocate heightmap
583 this->heightmap = new s16[csize.X * csize.Z];
585 //// Initialize biome generator
586 // TODO(hmmmm): should we have a way to disable biomemanager biomes?
587 biomegen = m_bmgr->createBiomeGen(BIOMEGEN_ORIGINAL, params->bparams, csize);
588 biomemap = biomegen->biomemap;
590 //// Look up some commonly used content
591 c_stone = ndef->getId("mapgen_stone");
592 c_desert_stone = ndef->getId("mapgen_desert_stone");
593 c_sandstone = ndef->getId("mapgen_sandstone");
594 c_water_source = ndef->getId("mapgen_water_source");
595 c_river_water_source = ndef->getId("mapgen_river_water_source");
596 c_lava_source = ndef->getId("mapgen_lava_source");
598 // Fall back to more basic content if not defined
599 // river_water_source cannot fallback to water_source because river water
600 // needs to be non-renewable and have a short flow range.
601 if (c_desert_stone == CONTENT_IGNORE)
602 c_desert_stone = c_stone;
603 if (c_sandstone == CONTENT_IGNORE)
604 c_sandstone = c_stone;
606 //// Content used for dungeon generation
607 c_cobble = ndef->getId("mapgen_cobble");
608 c_mossycobble = ndef->getId("mapgen_mossycobble");
609 c_stair_cobble = ndef->getId("mapgen_stair_cobble");
610 c_stair_desert_stone = ndef->getId("mapgen_stair_desert_stone");
611 c_sandstonebrick = ndef->getId("mapgen_sandstonebrick");
612 c_stair_sandstone_block = ndef->getId("mapgen_stair_sandstone_block");
614 // Fall back to more basic content if not defined
615 if (c_mossycobble == CONTENT_IGNORE)
616 c_mossycobble = c_cobble;
617 if (c_stair_cobble == CONTENT_IGNORE)
618 c_stair_cobble = c_cobble;
619 if (c_stair_desert_stone == CONTENT_IGNORE)
620 c_stair_desert_stone = c_desert_stone;
621 if (c_sandstonebrick == CONTENT_IGNORE)
622 c_sandstonebrick = c_sandstone;
623 if (c_stair_sandstone_block == CONTENT_IGNORE)
624 c_stair_sandstone_block = c_sandstonebrick;
628 MapgenBasic::~MapgenBasic()
635 MgStoneType MapgenBasic::generateBiomes()
637 // can't generate biomes without a biome generator!
641 v3s16 em = vm->m_area.getExtent();
643 MgStoneType stone_type = MGSTONE_STONE;
645 noise_filler_depth->perlinMap2D(node_min.X, node_min.Z);
647 for (s16 z = node_min.Z; z <= node_max.Z; z++)
648 for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
652 u16 depth_water_top = 0;
653 u16 depth_riverbed = 0;
654 u32 vi = vm->m_area.index(x, node_max.Y, z);
656 // Check node at base of mapchunk above, either a node of a previously
657 // generated mapchunk or if not, a node of overgenerated base terrain.
658 content_t c_above = vm->m_data[vi + em.X].getContent();
659 bool air_above = c_above == CONTENT_AIR;
660 bool river_water_above = c_above == c_river_water_source;
661 bool water_above = c_above == c_water_source || river_water_above;
663 biomemap[index] = BIOME_NONE;
665 // If there is air or water above enable top/filler placement, otherwise force
666 // nplaced to stone level by setting a number exceeding any possible filler depth.
667 u16 nplaced = (air_above || water_above) ? 0 : U16_MAX;
669 for (s16 y = node_max.Y; y >= node_min.Y; y--) {
670 content_t c = vm->m_data[vi].getContent();
672 // Biome is recalculated each time an upper surface is detected while
673 // working down a column. The selected biome then remains in effect for
674 // all nodes below until the next surface and biome recalculation.
675 // Biome is recalculated:
676 // 1. At the surface of stone below air or water.
677 // 2. At the surface of water below air.
678 // 3. When stone or water is detected but biome has not yet been calculated.
679 bool is_stone_surface = (c == c_stone) &&
680 (air_above || water_above || !biome);
682 bool is_water_surface =
683 (c == c_water_source || c == c_river_water_source) &&
684 (air_above || !biome);
686 if (is_stone_surface || is_water_surface) {
687 biome = biomegen->getBiomeAtIndex(index, y);
689 if (biomemap[index] == BIOME_NONE && is_stone_surface)
690 biomemap[index] = biome->index;
692 depth_top = biome->depth_top;
693 base_filler = MYMAX(depth_top +
694 biome->depth_filler +
695 noise_filler_depth->result[index], 0.f);
696 depth_water_top = biome->depth_water_top;
697 depth_riverbed = biome->depth_riverbed;
699 // Detect stone type for dungeons during every biome calculation.
700 // This is more efficient than detecting per-node and will not
701 // miss any desert stone or sandstone biomes.
702 if (biome->c_stone == c_desert_stone)
703 stone_type = MGSTONE_DESERT_STONE;
704 else if (biome->c_stone == c_sandstone)
705 stone_type = MGSTONE_SANDSTONE;
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);
740 } else if (c == c_water_source) {
741 vm->m_data[vi] = MapNode((y > (s32)(water_level - depth_water_top))
742 ? biome->c_water_top : biome->c_water);
743 nplaced = 0; // Enable top/filler placement for next surface
746 } else if (c == c_river_water_source) {
747 vm->m_data[vi] = MapNode(biome->c_river_water);
748 nplaced = 0; // Enable riverbed placement for next surface
751 river_water_above = true;
752 } else if (c == CONTENT_AIR) {
753 nplaced = 0; // Enable top/filler placement for next surface
756 } else { // Possible various nodes overgenerated from neighbouring mapchunks
757 nplaced = U16_MAX; // Disable top/filler placement
762 vm->m_area.add_y(em, vi, -1);
770 void MapgenBasic::dustTopNodes()
772 if (node_max.Y < water_level)
775 v3s16 em = vm->m_area.getExtent();
778 for (s16 z = node_min.Z; z <= node_max.Z; z++)
779 for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
780 Biome *biome = (Biome *)m_bmgr->getRaw(biomemap[index]);
782 if (biome->c_dust == CONTENT_IGNORE)
785 u32 vi = vm->m_area.index(x, full_node_max.Y, z);
786 content_t c_full_max = vm->m_data[vi].getContent();
789 if (c_full_max == CONTENT_AIR) {
790 y_start = full_node_max.Y - 1;
791 } else if (c_full_max == CONTENT_IGNORE) {
792 vi = vm->m_area.index(x, node_max.Y + 1, z);
793 content_t c_max = vm->m_data[vi].getContent();
795 if (c_max == CONTENT_AIR)
796 y_start = node_max.Y;
803 vi = vm->m_area.index(x, y_start, z);
804 for (s16 y = y_start; y >= node_min.Y - 1; y--) {
805 if (vm->m_data[vi].getContent() != CONTENT_AIR)
808 vm->m_area.add_y(em, vi, -1);
811 content_t c = vm->m_data[vi].getContent();
812 if (!ndef->get(c).buildable_to && c != CONTENT_IGNORE && c != biome->c_dust) {
813 vm->m_area.add_y(em, vi, 1);
814 vm->m_data[vi] = MapNode(biome->c_dust);
820 void MapgenBasic::generateCaves(s16 max_stone_y, s16 large_cave_depth)
822 if (max_stone_y < node_min.Y)
825 CavesNoiseIntersection caves_noise(ndef, m_bmgr, csize,
826 &np_cave1, &np_cave2, seed, cave_width);
828 caves_noise.generateCaves(vm, node_min, node_max, biomemap);
830 if (node_max.Y > large_cave_depth)
833 PseudoRandom ps(blockseed + 21343);
834 u32 bruises_count = ps.range(0, 2);
835 for (u32 i = 0; i < bruises_count; i++) {
836 CavesRandomWalk cave(ndef, &gennotify, seed, water_level,
837 c_water_source, CONTENT_IGNORE);
839 cave.makeCave(vm, node_min, node_max, &ps, true, max_stone_y, heightmap);
844 bool MapgenBasic::generateCaverns(s16 max_stone_y)
846 if (node_min.Y > max_stone_y || node_min.Y > cavern_limit)
849 CavernsNoise caverns_noise(ndef, csize, &np_cavern,
850 seed, cavern_limit, cavern_taper, cavern_threshold);
852 return caverns_noise.generateCaverns(vm, node_min, node_max);
856 void MapgenBasic::generateDungeons(s16 max_stone_y, MgStoneType stone_type)
858 if (max_stone_y < node_min.Y)
864 dp.c_water = c_water_source;
865 dp.c_river_water = c_river_water_source;
867 dp.only_in_ground = true;
868 dp.corridor_len_min = 1;
869 dp.corridor_len_max = 13;
872 dp.y_min = -MAX_MAP_GENERATION_LIMIT;
873 dp.y_max = MAX_MAP_GENERATION_LIMIT;
875 dp.np_density = nparams_dungeon_density;
876 dp.np_alt_wall = nparams_dungeon_alt_wall;
878 switch (stone_type) {
881 dp.c_wall = c_cobble;
882 dp.c_alt_wall = c_mossycobble;
883 dp.c_stair = c_stair_cobble;
885 dp.diagonal_dirs = false;
886 dp.holesize = v3s16(1, 2, 1);
887 dp.room_size_min = v3s16(4, 4, 4);
888 dp.room_size_max = v3s16(8, 6, 8);
889 dp.room_size_large_min = v3s16(8, 8, 8);
890 dp.room_size_large_max = v3s16(16, 16, 16);
891 dp.notifytype = GENNOTIFY_DUNGEON;
893 case MGSTONE_DESERT_STONE:
894 dp.c_wall = c_desert_stone;
895 dp.c_alt_wall = CONTENT_IGNORE;
896 dp.c_stair = c_stair_desert_stone;
898 dp.diagonal_dirs = true;
899 dp.holesize = v3s16(2, 3, 2);
900 dp.room_size_min = v3s16(6, 9, 6);
901 dp.room_size_max = v3s16(10, 11, 10);
902 dp.room_size_large_min = v3s16(10, 13, 10);
903 dp.room_size_large_max = v3s16(18, 21, 18);
904 dp.notifytype = GENNOTIFY_TEMPLE;
906 case MGSTONE_SANDSTONE:
907 dp.c_wall = c_sandstonebrick;
908 dp.c_alt_wall = CONTENT_IGNORE;
909 dp.c_stair = c_stair_sandstone_block;
911 dp.diagonal_dirs = false;
912 dp.holesize = v3s16(2, 2, 2);
913 dp.room_size_min = v3s16(6, 4, 6);
914 dp.room_size_max = v3s16(10, 6, 10);
915 dp.room_size_large_min = v3s16(10, 8, 10);
916 dp.room_size_large_max = v3s16(18, 16, 18);
917 dp.notifytype = GENNOTIFY_DUNGEON;
921 DungeonGen dgen(ndef, &gennotify, &dp);
922 dgen.generate(vm, blockseed, full_node_min, full_node_max);
927 //// GenerateNotifier
930 GenerateNotifier::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,
979 std::list<GenNotifyEvent>::iterator it;
981 for (it = m_notify_events.begin(); it != m_notify_events.end(); ++it) {
982 GenNotifyEvent &gn = *it;
983 std::string name = (gn.type == GENNOTIFY_DECORATION) ?
984 "decoration#"+ itos(gn.id) :
985 flagdesc_gennotify[gn.type].name;
987 event_map[name].push_back(gn.pos);
991 m_notify_events.clear();
1000 MapgenParams::~MapgenParams()
1006 void MapgenParams::readParams(const Settings *settings)
1008 std::string seed_str;
1009 const char *seed_name = (settings == g_settings) ? "fixed_map_seed" : "seed";
1011 if (settings->getNoEx(seed_name, seed_str)) {
1012 if (!seed_str.empty())
1013 seed = read_seed(seed_str.c_str());
1015 myrand_bytes(&seed, sizeof(seed));
1018 std::string mg_name;
1019 if (settings->getNoEx("mg_name", mg_name)) {
1020 mgtype = Mapgen::getMapgenType(mg_name);
1021 if (mgtype == MAPGEN_INVALID)
1022 mgtype = MAPGEN_DEFAULT;
1025 settings->getS16NoEx("water_level", water_level);
1026 settings->getS16NoEx("mapgen_limit", mapgen_limit);
1027 settings->getS16NoEx("chunksize", chunksize);
1028 settings->getFlagStrNoEx("mg_flags", flags, flagdesc_mapgen);
1031 bparams = BiomeManager::createBiomeParams(BIOMEGEN_ORIGINAL);
1033 bparams->readParams(settings);
1034 bparams->seed = seed;
1039 void MapgenParams::writeParams(Settings *settings) const
1041 settings->set("mg_name", Mapgen::getMapgenName(mgtype));
1042 settings->setU64("seed", seed);
1043 settings->setS16("water_level", water_level);
1044 settings->setS16("mapgen_limit", mapgen_limit);
1045 settings->setS16("chunksize", chunksize);
1046 settings->setFlagStr("mg_flags", flags, flagdesc_mapgen, U32_MAX);
1049 bparams->writeParams(settings);
1052 // Calculate edges of outermost generated mapchunks (less than
1053 // 'mapgen_limit'), and corresponding exact limits for SAO entities.
1054 void MapgenParams::calcMapgenEdges()
1056 if (m_mapgen_edges_calculated)
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;
1083 // SAO position limits, in Irrlicht units
1084 m_sao_limit_min = mapgen_edge_min * BS - 3.0f;
1085 m_sao_limit_max = mapgen_edge_max * BS + 3.0f;
1087 m_mapgen_edges_calculated = true;
1091 bool MapgenParams::saoPosOverLimit(const v3f &p)
1093 if (!m_mapgen_edges_calculated)
1096 return p.X < m_sao_limit_min ||
1097 p.X > m_sao_limit_max ||
1098 p.Y < m_sao_limit_min ||
1099 p.Y > m_sao_limit_max ||
1100 p.Z < m_sao_limit_min ||
1101 p.Z > m_sao_limit_max;
1105 s32 MapgenParams::getSpawnRangeMax()
1109 return MYMIN(-mapgen_edge_min, mapgen_edge_max);