3 Copyright (C) 2010-2015 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
4 Copyright (C) 2010-2015 celeron55, Perttu Ahola <celeron55@gmail.com>
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
29 #include "content_sao.h"
32 #include "voxelalgorithms.h"
37 #include "serialization.h"
38 #include "util/serialize.h"
39 #include "util/numeric.h"
42 #include "mapgen_flat.h"
43 #include "mapgen_fractal.h"
44 #include "mapgen_v5.h"
45 #include "mapgen_v6.h"
46 #include "mapgen_v7.h"
47 #include "mapgen_valleys.h"
48 #include "mapgen_singlenode.h"
50 #include "dungeongen.h"
52 FlagDesc flagdesc_mapgen[] = {
54 {"dungeons", MG_DUNGEONS},
56 {"decorations", MG_DECORATIONS},
60 FlagDesc flagdesc_gennotify[] = {
61 {"dungeon", 1 << GENNOTIFY_DUNGEON},
62 {"temple", 1 << GENNOTIFY_TEMPLE},
63 {"cave_begin", 1 << GENNOTIFY_CAVE_BEGIN},
64 {"cave_end", 1 << GENNOTIFY_CAVE_END},
65 {"large_cave_begin", 1 << GENNOTIFY_LARGECAVE_BEGIN},
66 {"large_cave_end", 1 << GENNOTIFY_LARGECAVE_END},
67 {"decoration", 1 << GENNOTIFY_DECORATION},
80 static MapgenDesc g_reg_mapgens[] = {
87 {"singlenode", false},
91 ARRLEN(g_reg_mapgens) == MAPGEN_INVALID,
92 registered_mapgens_is_wrong_size);
114 Mapgen::Mapgen(int mapgenid, MapgenParams *params, EmergeManager *emerge) :
115 gennotify(emerge->gen_notify_on, &emerge->gen_notify_on_deco_ids)
119 water_level = params->water_level;
120 flags = params->flags;
121 csize = v3s16(1, 1, 1) * (params->chunksize * MAP_BLOCKSIZE);
124 We are losing half our entropy by doing this, but it is necessary to
125 preserve reverse compatibility. If the top half of our current 64 bit
126 seeds ever starts getting used, existing worlds will break due to a
127 different hash outcome and no way to differentiate between versions.
129 A solution could be to add a new bit to designate that the top half of
130 the seed value should be used, essentially a 1-bit version code, but
131 this would require increasing the total size of a seed to 9 bytes (yuck)
133 It's probably okay if this never gets fixed. 4.2 billion possibilities
134 ought to be enough for anyone.
136 seed = (s32)params->seed;
151 MapgenType Mapgen::getMapgenType(const std::string &mgname)
153 for (size_t i = 0; i != ARRLEN(g_reg_mapgens); i++) {
154 if (mgname == g_reg_mapgens[i].name)
155 return (MapgenType)i;
158 return MAPGEN_INVALID;
162 const char *Mapgen::getMapgenName(MapgenType mgtype)
164 size_t index = (size_t)mgtype;
165 if (index == MAPGEN_INVALID || index >= ARRLEN(g_reg_mapgens))
168 return g_reg_mapgens[index].name;
172 Mapgen *Mapgen::createMapgen(MapgenType mgtype, int mgid,
173 MapgenParams *params, EmergeManager *emerge)
177 return new MapgenFlat(mgid, (MapgenFlatParams *)params, emerge);
179 return new MapgenFractal(mgid, (MapgenFractalParams *)params, emerge);
180 case MAPGEN_SINGLENODE:
181 return new MapgenSinglenode(mgid, (MapgenSinglenodeParams *)params, emerge);
183 return new MapgenV5(mgid, (MapgenV5Params *)params, emerge);
185 return new MapgenV6(mgid, (MapgenV6Params *)params, emerge);
187 return new MapgenV7(mgid, (MapgenV7Params *)params, emerge);
189 return new MapgenValleys(mgid, (MapgenValleysParams *)params, emerge);
196 MapgenParams *Mapgen::createMapgenParams(MapgenType mgtype)
200 return new MapgenFlatParams;
202 return new MapgenFractalParams;
203 case MAPGEN_SINGLENODE:
204 return new MapgenSinglenodeParams;
206 return new MapgenV5Params;
208 return new MapgenV6Params;
210 return new MapgenV7Params;
212 return new MapgenValleysParams;
219 void Mapgen::getMapgenNames(std::vector<const char *> *mgnames, bool include_hidden)
221 for (u32 i = 0; i != ARRLEN(g_reg_mapgens); i++) {
222 if (include_hidden || g_reg_mapgens[i].is_user_visible)
223 mgnames->push_back(g_reg_mapgens[i].name);
228 u32 Mapgen::getBlockSeed(v3s16 p, s32 seed)
237 u32 Mapgen::getBlockSeed2(v3s16 p, s32 seed)
239 u32 n = 1619 * p.X + 31337 * p.Y + 52591 * p.Z + 1013 * seed;
241 return (n * (n * n * 60493 + 19990303) + 1376312589);
245 // Returns Y one under area minimum if not found
246 s16 Mapgen::findGroundLevelFull(v2s16 p2d)
248 v3s16 em = vm->m_area.getExtent();
249 s16 y_nodes_max = vm->m_area.MaxEdge.Y;
250 s16 y_nodes_min = vm->m_area.MinEdge.Y;
251 u32 i = vm->m_area.index(p2d.X, y_nodes_max, p2d.Y);
254 for (y = y_nodes_max; y >= y_nodes_min; y--) {
255 MapNode &n = vm->m_data[i];
256 if (ndef->get(n).walkable)
259 vm->m_area.add_y(em, i, -1);
261 return (y >= y_nodes_min) ? y : y_nodes_min - 1;
265 // Returns -MAX_MAP_GENERATION_LIMIT if not found
266 s16 Mapgen::findGroundLevel(v2s16 p2d, s16 ymin, s16 ymax)
268 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)
277 vm->m_area.add_y(em, i, -1);
279 return (y >= ymin) ? y : -MAX_MAP_GENERATION_LIMIT;
283 // Returns -MAX_MAP_GENERATION_LIMIT if not found or if ground is found first
284 s16 Mapgen::findLiquidSurface(v2s16 p2d, s16 ymin, s16 ymax)
286 v3s16 em = vm->m_area.getExtent();
287 u32 i = vm->m_area.index(p2d.X, ymax, p2d.Y);
290 for (y = ymax; y >= ymin; y--) {
291 MapNode &n = vm->m_data[i];
292 if (ndef->get(n).walkable)
293 return -MAX_MAP_GENERATION_LIMIT;
294 else if (ndef->get(n).isLiquid())
297 vm->m_area.add_y(em, i, -1);
299 return (y >= ymin) ? y : -MAX_MAP_GENERATION_LIMIT;
303 void Mapgen::updateHeightmap(v3s16 nmin, v3s16 nmax)
308 //TimeTaker t("Mapgen::updateHeightmap", NULL, PRECISION_MICRO);
310 for (s16 z = nmin.Z; z <= nmax.Z; z++) {
311 for (s16 x = nmin.X; x <= nmax.X; x++, index++) {
312 s16 y = findGroundLevel(v2s16(x, z), nmin.Y, nmax.Y);
314 heightmap[index] = y;
317 //printf("updateHeightmap: %dus\n", t.stop());
320 inline bool Mapgen::isLiquidHorizontallyFlowable(u32 vi, v3s16 em)
323 vm->m_area.add_x(em, vi_neg_x, -1);
324 if (vm->m_data[vi_neg_x].getContent() != CONTENT_IGNORE) {
325 const ContentFeatures &c_nx = ndef->get(vm->m_data[vi_neg_x]);
326 if (c_nx.floodable && !c_nx.isLiquid())
330 vm->m_area.add_x(em, vi_pos_x, +1);
331 if (vm->m_data[vi_pos_x].getContent() != CONTENT_IGNORE) {
332 const ContentFeatures &c_px = ndef->get(vm->m_data[vi_pos_x]);
333 if (c_px.floodable && !c_px.isLiquid())
337 vm->m_area.add_z(em, vi_neg_z, -1);
338 if (vm->m_data[vi_neg_z].getContent() != CONTENT_IGNORE) {
339 const ContentFeatures &c_nz = ndef->get(vm->m_data[vi_neg_z]);
340 if (c_nz.floodable && !c_nz.isLiquid())
344 vm->m_area.add_z(em, vi_pos_z, +1);
345 if (vm->m_data[vi_pos_z].getContent() != CONTENT_IGNORE) {
346 const ContentFeatures &c_pz = ndef->get(vm->m_data[vi_pos_z]);
347 if (c_pz.floodable && !c_pz.isLiquid())
353 void Mapgen::updateLiquid(UniqueQueue<v3s16> *trans_liquid, v3s16 nmin, v3s16 nmax)
355 bool isignored, isliquid, wasignored, wasliquid, waschecked, waspushed;
356 v3s16 em = vm->m_area.getExtent();
358 for (s16 z = nmin.Z + 1; z <= nmax.Z - 1; z++)
359 for (s16 x = nmin.X + 1; x <= nmax.X - 1; x++) {
365 u32 vi = vm->m_area.index(x, nmax.Y, z);
366 for (s16 y = nmax.Y; y >= nmin.Y; y--) {
367 isignored = vm->m_data[vi].getContent() == CONTENT_IGNORE;
368 isliquid = ndef->get(vm->m_data[vi]).isLiquid();
370 if (isignored || wasignored || isliquid == wasliquid) {
371 // Neither topmost node of liquid column nor topmost node below column
374 } else if (isliquid) {
375 // This is the topmost node in the column
376 bool ispushed = false;
377 if (isLiquidHorizontallyFlowable(vi, em)) {
378 trans_liquid->push_back(v3s16(x, y, z));
381 // Remember waschecked and waspushed to avoid repeated
382 // checks/pushes in case the column consists of only this node
384 waspushed = ispushed;
386 // This is the topmost node below a liquid column
388 vm->m_area.add_y(em, vi_above, 1);
389 if (!waspushed && (ndef->get(vm->m_data[vi]).floodable ||
390 (!waschecked && isLiquidHorizontallyFlowable(vi_above, em)))) {
391 // Push back the lowest node in the column which is one
392 // node above this one
393 trans_liquid->push_back(v3s16(x, y + 1, z));
397 wasliquid = isliquid;
398 wasignored = isignored;
399 vm->m_area.add_y(em, vi, -1);
405 void Mapgen::setLighting(u8 light, v3s16 nmin, v3s16 nmax)
407 ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG);
408 VoxelArea a(nmin, nmax);
410 for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) {
411 for (int y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) {
412 u32 i = vm->m_area.index(a.MinEdge.X, y, z);
413 for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++, i++)
414 vm->m_data[i].param1 = light;
420 void Mapgen::lightSpread(VoxelArea &a, v3s16 p, u8 light)
422 if (light <= 1 || !a.contains(p))
425 u32 vi = vm->m_area.index(p);
426 MapNode &n = vm->m_data[vi];
428 // Decay light in each of the banks separately
429 u8 light_day = light & 0x0F;
433 u8 light_night = light & 0xF0;
437 // Bail out only if we have no more light from either bank to propogate, or
438 // we hit a solid block that light cannot pass through.
439 if ((light_day <= (n.param1 & 0x0F) &&
440 light_night <= (n.param1 & 0xF0)) ||
441 !ndef->get(n).light_propagates)
444 // Since this recursive function only terminates when there is no light from
445 // either bank left, we need to take the max of both banks into account for
446 // the case where spreading has stopped for one light bank but not the other.
447 light = MYMAX(light_day, n.param1 & 0x0F) |
448 MYMAX(light_night, n.param1 & 0xF0);
452 lightSpread(a, p + v3s16(0, 0, 1), light);
453 lightSpread(a, p + v3s16(0, 1, 0), light);
454 lightSpread(a, p + v3s16(1, 0, 0), light);
455 lightSpread(a, p - v3s16(0, 0, 1), light);
456 lightSpread(a, p - v3s16(0, 1, 0), light);
457 lightSpread(a, p - v3s16(1, 0, 0), light);
461 void Mapgen::calcLighting(v3s16 nmin, v3s16 nmax, v3s16 full_nmin, v3s16 full_nmax,
462 bool propagate_shadow)
464 ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG);
465 //TimeTaker t("updateLighting");
467 propagateSunlight(nmin, nmax, propagate_shadow);
468 spreadLight(full_nmin, full_nmax);
470 //printf("updateLighting: %dms\n", t.stop());
474 void Mapgen::propagateSunlight(v3s16 nmin, v3s16 nmax, bool propagate_shadow)
476 //TimeTaker t("propagateSunlight");
477 VoxelArea a(nmin, nmax);
478 bool block_is_underground = (water_level >= nmax.Y);
479 v3s16 em = vm->m_area.getExtent();
481 // NOTE: Direct access to the low 4 bits of param1 is okay here because,
482 // by definition, sunlight will never be in the night lightbank.
484 for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) {
485 for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++) {
486 // see if we can get a light value from the overtop
487 u32 i = vm->m_area.index(x, a.MaxEdge.Y + 1, z);
488 if (vm->m_data[i].getContent() == CONTENT_IGNORE) {
489 if (block_is_underground)
491 } else if ((vm->m_data[i].param1 & 0x0F) != LIGHT_SUN &&
495 vm->m_area.add_y(em, i, -1);
497 for (int y = a.MaxEdge.Y; y >= a.MinEdge.Y; y--) {
498 MapNode &n = vm->m_data[i];
499 if (!ndef->get(n).sunlight_propagates)
501 n.param1 = LIGHT_SUN;
502 vm->m_area.add_y(em, i, -1);
506 //printf("propagateSunlight: %dms\n", t.stop());
510 void Mapgen::spreadLight(v3s16 nmin, v3s16 nmax)
512 //TimeTaker t("spreadLight");
513 VoxelArea a(nmin, nmax);
515 for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) {
516 for (int y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) {
517 u32 i = vm->m_area.index(a.MinEdge.X, y, z);
518 for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++, i++) {
519 MapNode &n = vm->m_data[i];
520 if (n.getContent() == CONTENT_IGNORE)
523 const ContentFeatures &cf = ndef->get(n);
524 if (!cf.light_propagates)
527 // TODO(hmmmmm): Abstract away direct param1 accesses with a
528 // wrapper, but something lighter than MapNode::get/setLight
530 u8 light_produced = cf.light_source;
532 n.param1 = light_produced | (light_produced << 4);
536 lightSpread(a, v3s16(x, y, z + 1), light);
537 lightSpread(a, v3s16(x, y + 1, z ), light);
538 lightSpread(a, v3s16(x + 1, y, z ), light);
539 lightSpread(a, v3s16(x, y, z - 1), light);
540 lightSpread(a, v3s16(x, y - 1, z ), light);
541 lightSpread(a, v3s16(x - 1, y, z ), light);
547 //printf("spreadLight: %dms\n", t.stop());
555 MapgenBasic::MapgenBasic(int mapgenid, MapgenParams *params, EmergeManager *emerge)
556 : Mapgen(mapgenid, params, emerge)
558 this->m_emerge = emerge;
559 this->m_bmgr = emerge->biomemgr;
561 //// Here, 'stride' refers to the number of elements needed to skip to index
562 //// an adjacent element for that coordinate in noise/height/biome maps
563 //// (*not* vmanip content map!)
565 // Note there is no X stride explicitly defined. Items adjacent in the X
566 // coordinate are assumed to be adjacent in memory as well (i.e. stride of 1).
568 // Number of elements to skip to get to the next Y coordinate
569 this->ystride = csize.X;
571 // Number of elements to skip to get to the next Z coordinate
572 this->zstride = csize.X * csize.Y;
574 // Z-stride value for maps oversized for 1-down overgeneration
575 this->zstride_1d = csize.X * (csize.Y + 1);
577 // Z-stride value for maps oversized for 1-up 1-down overgeneration
578 this->zstride_1u1d = csize.X * (csize.Y + 2);
580 //// Allocate heightmap
581 this->heightmap = new s16[csize.X * csize.Z];
583 //// Initialize biome generator
584 // TODO(hmmmm): should we have a way to disable biomemanager biomes?
585 biomegen = m_bmgr->createBiomeGen(BIOMEGEN_ORIGINAL, params->bparams, csize);
586 biomemap = biomegen->biomemap;
588 //// Look up some commonly used content
589 c_stone = ndef->getId("mapgen_stone");
590 c_desert_stone = ndef->getId("mapgen_desert_stone");
591 c_sandstone = ndef->getId("mapgen_sandstone");
592 c_water_source = ndef->getId("mapgen_water_source");
593 c_river_water_source = ndef->getId("mapgen_river_water_source");
595 // Fall back to more basic content if not defined
596 // river_water_source cannot fallback to water_source because river water
597 // needs to be non-renewable and have a short flow range.
598 if (c_desert_stone == CONTENT_IGNORE)
599 c_desert_stone = c_stone;
600 if (c_sandstone == CONTENT_IGNORE)
601 c_sandstone = c_stone;
603 //// Content used for dungeon generation
604 c_cobble = ndef->getId("mapgen_cobble");
605 c_mossycobble = ndef->getId("mapgen_mossycobble");
606 c_stair_cobble = ndef->getId("mapgen_stair_cobble");
607 c_stair_desert_stone = ndef->getId("mapgen_stair_desert_stone");
608 c_sandstonebrick = ndef->getId("mapgen_sandstonebrick");
609 c_stair_sandstonebrick = ndef->getId("mapgen_stair_sandstonebrick");
611 // Fall back to more basic content if not defined
612 if (c_mossycobble == CONTENT_IGNORE)
613 c_mossycobble = c_cobble;
614 if (c_stair_cobble == CONTENT_IGNORE)
615 c_stair_cobble = c_cobble;
616 if (c_stair_desert_stone == CONTENT_IGNORE)
617 c_stair_desert_stone = c_desert_stone;
618 if (c_sandstonebrick == CONTENT_IGNORE)
619 c_sandstonebrick = c_sandstone;
620 if (c_stair_sandstonebrick == CONTENT_IGNORE)
621 c_stair_sandstonebrick = c_sandstonebrick;
625 MapgenBasic::~MapgenBasic()
632 MgStoneType MapgenBasic::generateBiomes()
634 // can't generate biomes without a biome generator!
638 v3s16 em = vm->m_area.getExtent();
640 MgStoneType stone_type = MGSTONE_STONE;
642 noise_filler_depth->perlinMap2D(node_min.X, node_min.Z);
644 for (s16 z = node_min.Z; z <= node_max.Z; z++)
645 for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
649 u16 depth_water_top = 0;
650 u16 depth_riverbed = 0;
651 u32 vi = vm->m_area.index(x, node_max.Y, z);
653 // Check node at base of mapchunk above, either a node of a previously
654 // generated mapchunk or if not, a node of overgenerated base terrain.
655 content_t c_above = vm->m_data[vi + em.X].getContent();
656 bool air_above = c_above == CONTENT_AIR;
657 bool river_water_above = c_above == c_river_water_source;
658 bool water_above = c_above == c_water_source || river_water_above;
660 biomemap[index] = BIOME_NONE;
662 // If there is air or water above enable top/filler placement, otherwise force
663 // nplaced to stone level by setting a number exceeding any possible filler depth.
664 u16 nplaced = (air_above || water_above) ? 0 : U16_MAX;
666 for (s16 y = node_max.Y; y >= node_min.Y; y--) {
667 content_t c = vm->m_data[vi].getContent();
669 // Biome is recalculated each time an upper surface is detected while
670 // working down a column. The selected biome then remains in effect for
671 // all nodes below until the next surface and biome recalculation.
672 // Biome is recalculated:
673 // 1. At the surface of stone below air or water.
674 // 2. At the surface of water below air.
675 // 3. When stone or water is detected but biome has not yet been calculated.
676 bool is_stone_surface = (c == c_stone) &&
677 (air_above || water_above || !biome);
679 bool is_water_surface =
680 (c == c_water_source || c == c_river_water_source) &&
681 (air_above || !biome);
683 if (is_stone_surface || is_water_surface) {
684 biome = biomegen->getBiomeAtIndex(index, y);
686 if (biomemap[index] == BIOME_NONE && is_stone_surface)
687 biomemap[index] = biome->index;
689 depth_top = biome->depth_top;
690 base_filler = MYMAX(depth_top +
691 biome->depth_filler +
692 noise_filler_depth->result[index], 0.f);
693 depth_water_top = biome->depth_water_top;
694 depth_riverbed = biome->depth_riverbed;
696 // Detect stone type for dungeons during every biome calculation.
697 // This is more efficient than detecting per-node and will not
698 // miss any desert stone or sandstone biomes.
699 if (biome->c_stone == c_desert_stone)
700 stone_type = MGSTONE_DESERT_STONE;
701 else if (biome->c_stone == c_sandstone)
702 stone_type = MGSTONE_SANDSTONE;
706 content_t c_below = vm->m_data[vi - em.X].getContent();
708 // If the node below isn't solid, make this node stone, so that
709 // any top/filler nodes above are structurally supported.
710 // This is done by aborting the cycle of top/filler placement
711 // immediately by forcing nplaced to stone level.
712 if (c_below == CONTENT_AIR
713 || c_below == c_water_source
714 || c_below == c_river_water_source)
717 if (river_water_above) {
718 if (nplaced < depth_riverbed) {
719 vm->m_data[vi] = MapNode(biome->c_riverbed);
722 nplaced = U16_MAX; // Disable top/filler placement
723 river_water_above = false;
725 } else if (nplaced < depth_top) {
726 vm->m_data[vi] = MapNode(biome->c_top);
728 } else if (nplaced < base_filler) {
729 vm->m_data[vi] = MapNode(biome->c_filler);
732 vm->m_data[vi] = MapNode(biome->c_stone);
737 } else if (c == c_water_source) {
738 vm->m_data[vi] = MapNode((y > (s32)(water_level - depth_water_top))
739 ? biome->c_water_top : biome->c_water);
740 nplaced = 0; // Enable top/filler placement for next surface
743 } else if (c == c_river_water_source) {
744 vm->m_data[vi] = MapNode(biome->c_river_water);
745 nplaced = 0; // Enable riverbed placement for next surface
748 river_water_above = true;
749 } else if (c == CONTENT_AIR) {
750 nplaced = 0; // Enable top/filler placement for next surface
753 } else { // Possible various nodes overgenerated from neighbouring mapchunks
754 nplaced = U16_MAX; // Disable top/filler placement
759 vm->m_area.add_y(em, vi, -1);
767 void MapgenBasic::dustTopNodes()
769 if (node_max.Y < water_level)
772 v3s16 em = vm->m_area.getExtent();
775 for (s16 z = node_min.Z; z <= node_max.Z; z++)
776 for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
777 Biome *biome = (Biome *)m_bmgr->getRaw(biomemap[index]);
779 if (biome->c_dust == CONTENT_IGNORE)
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 vm->m_area.add_y(em, vi, -1);
808 content_t c = vm->m_data[vi].getContent();
809 if (!ndef->get(c).buildable_to && c != CONTENT_IGNORE && c != biome->c_dust) {
810 vm->m_area.add_y(em, vi, 1);
811 vm->m_data[vi] = MapNode(biome->c_dust);
817 void MapgenBasic::generateCaves(s16 max_stone_y, s16 large_cave_depth)
819 if (max_stone_y < node_min.Y)
822 CavesNoiseIntersection caves_noise(ndef, m_bmgr, csize,
823 &np_cave1, &np_cave2, seed, cave_width);
825 caves_noise.generateCaves(vm, node_min, node_max, biomemap);
827 if (node_max.Y > large_cave_depth)
830 PseudoRandom ps(blockseed + 21343);
831 u32 bruises_count = ps.range(0, 2);
832 for (u32 i = 0; i < bruises_count; i++) {
833 CavesRandomWalk cave(ndef, &gennotify, seed, water_level,
834 c_water_source, CONTENT_IGNORE);
836 cave.makeCave(vm, node_min, node_max, &ps, true, max_stone_y, heightmap);
841 void MapgenBasic::generateDungeons(s16 max_stone_y, MgStoneType stone_type)
843 if (max_stone_y < node_min.Y)
849 dp.c_water = c_water_source;
850 dp.c_river_water = c_river_water_source;
853 dp.y_min = -MAX_MAP_GENERATION_LIMIT;
854 dp.y_max = MAX_MAP_GENERATION_LIMIT;
855 dp.np_density = nparams_dungeon_density;
856 dp.np_alt_wall = nparams_dungeon_alt_wall;
858 switch (stone_type) {
861 dp.c_wall = c_cobble;
862 dp.c_alt_wall = c_mossycobble;
863 dp.c_stair = c_stair_cobble;
865 dp.diagonal_dirs = false;
866 dp.holesize = v3s16(1, 2, 1);
867 dp.roomsize = v3s16(0, 0, 0);
868 dp.notifytype = GENNOTIFY_DUNGEON;
870 case MGSTONE_DESERT_STONE:
871 dp.c_wall = c_desert_stone;
872 dp.c_alt_wall = CONTENT_IGNORE;
873 dp.c_stair = c_stair_desert_stone;
875 dp.diagonal_dirs = true;
876 dp.holesize = v3s16(2, 3, 2);
877 dp.roomsize = v3s16(2, 5, 2);
878 dp.notifytype = GENNOTIFY_TEMPLE;
880 case MGSTONE_SANDSTONE:
881 dp.c_wall = c_sandstonebrick;
882 dp.c_alt_wall = CONTENT_IGNORE;
883 dp.c_stair = c_stair_sandstonebrick;
885 dp.diagonal_dirs = false;
886 dp.holesize = v3s16(2, 2, 2);
887 dp.roomsize = v3s16(2, 0, 2);
888 dp.notifytype = GENNOTIFY_DUNGEON;
892 DungeonGen dgen(ndef, &gennotify, &dp);
893 dgen.generate(vm, blockseed, full_node_min, full_node_max);
898 //// GenerateNotifier
901 GenerateNotifier::GenerateNotifier()
907 GenerateNotifier::GenerateNotifier(u32 notify_on,
908 std::set<u32> *notify_on_deco_ids)
910 m_notify_on = notify_on;
911 m_notify_on_deco_ids = notify_on_deco_ids;
915 void GenerateNotifier::setNotifyOn(u32 notify_on)
917 m_notify_on = notify_on;
921 void GenerateNotifier::setNotifyOnDecoIds(std::set<u32> *notify_on_deco_ids)
923 m_notify_on_deco_ids = notify_on_deco_ids;
927 bool GenerateNotifier::addEvent(GenNotifyType type, v3s16 pos, u32 id)
929 if (!(m_notify_on & (1 << type)))
932 if (type == GENNOTIFY_DECORATION &&
933 m_notify_on_deco_ids->find(id) == m_notify_on_deco_ids->end())
940 m_notify_events.push_back(gne);
946 void GenerateNotifier::getEvents(
947 std::map<std::string, std::vector<v3s16> > &event_map,
950 std::list<GenNotifyEvent>::iterator it;
952 for (it = m_notify_events.begin(); it != m_notify_events.end(); ++it) {
953 GenNotifyEvent &gn = *it;
954 std::string name = (gn.type == GENNOTIFY_DECORATION) ?
955 "decoration#"+ itos(gn.id) :
956 flagdesc_gennotify[gn.type].name;
958 event_map[name].push_back(gn.pos);
962 m_notify_events.clear();
971 MapgenParams::~MapgenParams()
977 void MapgenParams::readParams(const Settings *settings)
979 std::string seed_str;
980 const char *seed_name = (settings == g_settings) ? "fixed_map_seed" : "seed";
982 if (settings->getNoEx(seed_name, seed_str)) {
983 if (!seed_str.empty())
984 seed = read_seed(seed_str.c_str());
986 myrand_bytes(&seed, sizeof(seed));
990 if (settings->getNoEx("mg_name", mg_name))
991 this->mgtype = Mapgen::getMapgenType(mg_name);
993 settings->getS16NoEx("water_level", water_level);
994 settings->getS16NoEx("chunksize", chunksize);
995 settings->getFlagStrNoEx("mg_flags", flags, flagdesc_mapgen);
998 bparams = BiomeManager::createBiomeParams(BIOMEGEN_ORIGINAL);
1000 bparams->readParams(settings);
1001 bparams->seed = seed;
1006 void MapgenParams::writeParams(Settings *settings) const
1008 settings->set("mg_name", Mapgen::getMapgenName(mgtype));
1009 settings->setU64("seed", seed);
1010 settings->setS16("water_level", water_level);
1011 settings->setS16("chunksize", chunksize);
1012 settings->setFlagStr("mg_flags", flags, flagdesc_mapgen, U32_MAX);
1015 bparams->writeParams(settings);