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"
43 #include "dungeongen.h"
45 FlagDesc flagdesc_mapgen[] = {
48 {"dungeons", MG_DUNGEONS},
51 {"decorations", MG_DECORATIONS},
55 FlagDesc flagdesc_gennotify[] = {
56 {"dungeon", 1 << GENNOTIFY_DUNGEON},
57 {"temple", 1 << GENNOTIFY_TEMPLE},
58 {"cave_begin", 1 << GENNOTIFY_CAVE_BEGIN},
59 {"cave_end", 1 << GENNOTIFY_CAVE_END},
60 {"large_cave_begin", 1 << GENNOTIFY_LARGECAVE_BEGIN},
61 {"large_cave_end", 1 << GENNOTIFY_LARGECAVE_END},
62 {"decoration", 1 << GENNOTIFY_DECORATION},
87 Mapgen::Mapgen(int mapgenid, MapgenParams *params, EmergeManager *emerge) :
88 gennotify(emerge->gen_notify_on, &emerge->gen_notify_on_deco_ids)
92 water_level = params->water_level;
93 flags = params->flags;
94 csize = v3s16(1, 1, 1) * (params->chunksize * MAP_BLOCKSIZE);
97 We are losing half our entropy by doing this, but it is necessary to
98 preserve reverse compatibility. If the top half of our current 64 bit
99 seeds ever starts getting used, existing worlds will break due to a
100 different hash outcome and no way to differentiate between versions.
102 A solution could be to add a new bit to designate that the top half of
103 the seed value should be used, essentially a 1-bit version code, but
104 this would require increasing the total size of a seed to 9 bytes (yuck)
106 It's probably okay if this never gets fixed. 4.2 billion possibilities
107 ought to be enough for anyone.
109 seed = (s32)params->seed;
124 u32 Mapgen::getBlockSeed(v3s16 p, s32 seed)
133 u32 Mapgen::getBlockSeed2(v3s16 p, s32 seed)
135 u32 n = 1619 * p.X + 31337 * p.Y + 52591 * p.Z + 1013 * seed;
137 return (n * (n * n * 60493 + 19990303) + 1376312589);
141 // Returns Y one under area minimum if not found
142 s16 Mapgen::findGroundLevelFull(v2s16 p2d)
144 v3s16 em = vm->m_area.getExtent();
145 s16 y_nodes_max = vm->m_area.MaxEdge.Y;
146 s16 y_nodes_min = vm->m_area.MinEdge.Y;
147 u32 i = vm->m_area.index(p2d.X, y_nodes_max, p2d.Y);
150 for (y = y_nodes_max; y >= y_nodes_min; y--) {
151 MapNode &n = vm->m_data[i];
152 if (ndef->get(n).walkable)
155 vm->m_area.add_y(em, i, -1);
157 return (y >= y_nodes_min) ? y : y_nodes_min - 1;
161 // Returns -MAX_MAP_GENERATION_LIMIT if not found
162 s16 Mapgen::findGroundLevel(v2s16 p2d, s16 ymin, s16 ymax)
164 v3s16 em = vm->m_area.getExtent();
165 u32 i = vm->m_area.index(p2d.X, ymax, p2d.Y);
168 for (y = ymax; y >= ymin; y--) {
169 MapNode &n = vm->m_data[i];
170 if (ndef->get(n).walkable)
173 vm->m_area.add_y(em, i, -1);
175 return (y >= ymin) ? y : -MAX_MAP_GENERATION_LIMIT;
179 // Returns -MAX_MAP_GENERATION_LIMIT if not found or if ground is found first
180 s16 Mapgen::findLiquidSurface(v2s16 p2d, s16 ymin, s16 ymax)
182 v3s16 em = vm->m_area.getExtent();
183 u32 i = vm->m_area.index(p2d.X, ymax, p2d.Y);
186 for (y = ymax; y >= ymin; y--) {
187 MapNode &n = vm->m_data[i];
188 if (ndef->get(n).walkable)
189 return -MAX_MAP_GENERATION_LIMIT;
190 else if (ndef->get(n).isLiquid())
193 vm->m_area.add_y(em, i, -1);
195 return (y >= ymin) ? y : -MAX_MAP_GENERATION_LIMIT;
199 void Mapgen::updateHeightmap(v3s16 nmin, v3s16 nmax)
204 //TimeTaker t("Mapgen::updateHeightmap", NULL, PRECISION_MICRO);
206 for (s16 z = nmin.Z; z <= nmax.Z; z++) {
207 for (s16 x = nmin.X; x <= nmax.X; x++, index++) {
208 s16 y = findGroundLevel(v2s16(x, z), nmin.Y, nmax.Y);
210 heightmap[index] = y;
213 //printf("updateHeightmap: %dus\n", t.stop());
216 inline bool Mapgen::isLiquidHorizontallyFlowable(u32 vi, v3s16 em)
219 vm->m_area.add_x(em, vi_neg_x, -1);
220 if (vm->m_data[vi_neg_x].getContent() != CONTENT_IGNORE) {
221 const ContentFeatures &c_nx = ndef->get(vm->m_data[vi_neg_x]);
222 if (c_nx.floodable && !c_nx.isLiquid())
226 vm->m_area.add_x(em, vi_pos_x, +1);
227 if (vm->m_data[vi_pos_x].getContent() != CONTENT_IGNORE) {
228 const ContentFeatures &c_px = ndef->get(vm->m_data[vi_pos_x]);
229 if (c_px.floodable && !c_px.isLiquid())
233 vm->m_area.add_z(em, vi_neg_z, -1);
234 if (vm->m_data[vi_neg_z].getContent() != CONTENT_IGNORE) {
235 const ContentFeatures &c_nz = ndef->get(vm->m_data[vi_neg_z]);
236 if (c_nz.floodable && !c_nz.isLiquid())
240 vm->m_area.add_z(em, vi_pos_z, +1);
241 if (vm->m_data[vi_pos_z].getContent() != CONTENT_IGNORE) {
242 const ContentFeatures &c_pz = ndef->get(vm->m_data[vi_pos_z]);
243 if (c_pz.floodable && !c_pz.isLiquid())
249 void Mapgen::updateLiquid(UniqueQueue<v3s16> *trans_liquid, v3s16 nmin, v3s16 nmax)
251 bool isignored, isliquid, wasignored, wasliquid, waschecked, waspushed;
252 v3s16 em = vm->m_area.getExtent();
254 for (s16 z = nmin.Z + 1; z <= nmax.Z - 1; z++)
255 for (s16 x = nmin.X + 1; x <= nmax.X - 1; x++) {
261 u32 vi = vm->m_area.index(x, nmax.Y, z);
262 for (s16 y = nmax.Y; y >= nmin.Y; y--) {
263 isignored = vm->m_data[vi].getContent() == CONTENT_IGNORE;
264 isliquid = ndef->get(vm->m_data[vi]).isLiquid();
266 if (isignored || wasignored || isliquid == wasliquid) {
267 // Neither topmost node of liquid column nor topmost node below column
270 } else if (isliquid) {
271 // This is the topmost node in the column
272 bool ispushed = false;
273 if (isLiquidHorizontallyFlowable(vi, em)) {
274 trans_liquid->push_back(v3s16(x, y, z));
277 // Remember waschecked and waspushed to avoid repeated
278 // checks/pushes in case the column consists of only this node
280 waspushed = ispushed;
282 // This is the topmost node below a liquid column
284 vm->m_area.add_y(em, vi_above, 1);
285 if (!waspushed && (ndef->get(vm->m_data[vi]).floodable ||
286 (!waschecked && isLiquidHorizontallyFlowable(vi_above, em)))) {
287 // Push back the lowest node in the column which is one
288 // node above this one
289 trans_liquid->push_back(v3s16(x, y + 1, z));
293 wasliquid = isliquid;
294 wasignored = isignored;
295 vm->m_area.add_y(em, vi, -1);
301 void Mapgen::setLighting(u8 light, v3s16 nmin, v3s16 nmax)
303 ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG);
304 VoxelArea a(nmin, nmax);
306 for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) {
307 for (int y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) {
308 u32 i = vm->m_area.index(a.MinEdge.X, y, z);
309 for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++, i++)
310 vm->m_data[i].param1 = light;
316 void Mapgen::lightSpread(VoxelArea &a, v3s16 p, u8 light)
318 if (light <= 1 || !a.contains(p))
321 u32 vi = vm->m_area.index(p);
322 MapNode &n = vm->m_data[vi];
324 // Decay light in each of the banks separately
325 u8 light_day = light & 0x0F;
329 u8 light_night = light & 0xF0;
333 // Bail out only if we have no more light from either bank to propogate, or
334 // we hit a solid block that light cannot pass through.
335 if ((light_day <= (n.param1 & 0x0F) &&
336 light_night <= (n.param1 & 0xF0)) ||
337 !ndef->get(n).light_propagates)
340 // Since this recursive function only terminates when there is no light from
341 // either bank left, we need to take the max of both banks into account for
342 // the case where spreading has stopped for one light bank but not the other.
343 light = MYMAX(light_day, n.param1 & 0x0F) |
344 MYMAX(light_night, n.param1 & 0xF0);
348 lightSpread(a, p + v3s16(0, 0, 1), light);
349 lightSpread(a, p + v3s16(0, 1, 0), light);
350 lightSpread(a, p + v3s16(1, 0, 0), light);
351 lightSpread(a, p - v3s16(0, 0, 1), light);
352 lightSpread(a, p - v3s16(0, 1, 0), light);
353 lightSpread(a, p - v3s16(1, 0, 0), light);
357 void Mapgen::calcLighting(v3s16 nmin, v3s16 nmax, v3s16 full_nmin, v3s16 full_nmax,
358 bool propagate_shadow)
360 ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG);
361 //TimeTaker t("updateLighting");
363 propagateSunlight(nmin, nmax, propagate_shadow);
364 spreadLight(full_nmin, full_nmax);
366 //printf("updateLighting: %dms\n", t.stop());
370 void Mapgen::propagateSunlight(v3s16 nmin, v3s16 nmax, bool propagate_shadow)
372 //TimeTaker t("propagateSunlight");
373 VoxelArea a(nmin, nmax);
374 bool block_is_underground = (water_level >= nmax.Y);
375 v3s16 em = vm->m_area.getExtent();
377 // NOTE: Direct access to the low 4 bits of param1 is okay here because,
378 // by definition, sunlight will never be in the night lightbank.
380 for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) {
381 for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++) {
382 // see if we can get a light value from the overtop
383 u32 i = vm->m_area.index(x, a.MaxEdge.Y + 1, z);
384 if (vm->m_data[i].getContent() == CONTENT_IGNORE) {
385 if (block_is_underground)
387 } else if ((vm->m_data[i].param1 & 0x0F) != LIGHT_SUN &&
391 vm->m_area.add_y(em, i, -1);
393 for (int y = a.MaxEdge.Y; y >= a.MinEdge.Y; y--) {
394 MapNode &n = vm->m_data[i];
395 if (!ndef->get(n).sunlight_propagates)
397 n.param1 = LIGHT_SUN;
398 vm->m_area.add_y(em, i, -1);
402 //printf("propagateSunlight: %dms\n", t.stop());
406 void Mapgen::spreadLight(v3s16 nmin, v3s16 nmax)
408 //TimeTaker t("spreadLight");
409 VoxelArea a(nmin, nmax);
411 for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) {
412 for (int y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) {
413 u32 i = vm->m_area.index(a.MinEdge.X, y, z);
414 for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++, i++) {
415 MapNode &n = vm->m_data[i];
416 if (n.getContent() == CONTENT_IGNORE)
419 const ContentFeatures &cf = ndef->get(n);
420 if (!cf.light_propagates)
423 // TODO(hmmmmm): Abstract away direct param1 accesses with a
424 // wrapper, but something lighter than MapNode::get/setLight
426 u8 light_produced = cf.light_source;
428 n.param1 = light_produced | (light_produced << 4);
432 lightSpread(a, v3s16(x, y, z + 1), light);
433 lightSpread(a, v3s16(x, y + 1, z ), light);
434 lightSpread(a, v3s16(x + 1, y, z ), light);
435 lightSpread(a, v3s16(x, y, z - 1), light);
436 lightSpread(a, v3s16(x, y - 1, z ), light);
437 lightSpread(a, v3s16(x - 1, y, z ), light);
443 //printf("spreadLight: %dms\n", t.stop());
451 MapgenBasic::MapgenBasic(int mapgenid, MapgenParams *params, EmergeManager *emerge)
452 : Mapgen(mapgenid, params, emerge)
454 this->m_emerge = emerge;
455 this->m_bmgr = emerge->biomemgr;
457 //// Here, 'stride' refers to the number of elements needed to skip to index
458 //// an adjacent element for that coordinate in noise/height/biome maps
459 //// (*not* vmanip content map!)
461 // Note there is no X stride explicitly defined. Items adjacent in the X
462 // coordinate are assumed to be adjacent in memory as well (i.e. stride of 1).
464 // Number of elements to skip to get to the next Y coordinate
465 this->ystride = csize.X;
467 // Number of elements to skip to get to the next Z coordinate
468 this->zstride = csize.X * csize.Y;
470 // Z-stride value for maps oversized for 1-down overgeneration
471 this->zstride_1d = csize.X * (csize.Y + 1);
473 // Z-stride value for maps oversized for 1-up 1-down overgeneration
474 this->zstride_1u1d = csize.X * (csize.Y + 2);
476 //// Allocate heightmap
477 this->heightmap = new s16[csize.X * csize.Z];
479 //// Initialize biome generator
480 // TODO(hmmmm): should we have a way to disable biomemanager biomes?
481 biomegen = m_bmgr->createBiomeGen(BIOMEGEN_ORIGINAL, params->bparams, csize);
482 biomemap = biomegen->biomemap;
484 //// Look up some commonly used content
485 c_stone = ndef->getId("mapgen_stone");
486 c_water_source = ndef->getId("mapgen_water_source");
487 c_desert_stone = ndef->getId("mapgen_desert_stone");
488 c_sandstone = ndef->getId("mapgen_sandstone");
489 c_river_water_source = ndef->getId("mapgen_river_water_source");
491 // Fall back to more basic content if not defined
492 if (c_desert_stone == CONTENT_IGNORE)
493 c_desert_stone = c_stone;
494 if (c_sandstone == CONTENT_IGNORE)
495 c_sandstone = c_stone;
496 if (c_river_water_source == CONTENT_IGNORE)
497 c_river_water_source = c_water_source;
499 //// Content used for dungeon generation
500 c_cobble = ndef->getId("mapgen_cobble");
501 c_stair_cobble = ndef->getId("mapgen_stair_cobble");
502 c_mossycobble = ndef->getId("mapgen_mossycobble");
503 c_sandstonebrick = ndef->getId("mapgen_sandstonebrick");
504 c_stair_sandstonebrick = ndef->getId("mapgen_stair_sandstonebrick");
506 // Fall back to more basic content if not defined
507 if (c_mossycobble == CONTENT_IGNORE)
508 c_mossycobble = c_cobble;
509 if (c_stair_cobble == CONTENT_IGNORE)
510 c_stair_cobble = c_cobble;
511 if (c_sandstonebrick == CONTENT_IGNORE)
512 c_sandstonebrick = c_sandstone;
513 if (c_stair_sandstonebrick == CONTENT_IGNORE)
514 c_stair_sandstonebrick = c_sandstone;
518 MapgenBasic::~MapgenBasic()
525 MgStoneType MapgenBasic::generateBiomes()
527 v3s16 em = vm->m_area.getExtent();
529 MgStoneType stone_type = MGSTONE_STONE;
531 noise_filler_depth->perlinMap2D(node_min.X, node_min.Z);
533 for (s16 z = node_min.Z; z <= node_max.Z; z++)
534 for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
538 u16 depth_water_top = 0;
539 u16 depth_riverbed = 0;
540 u32 vi = vm->m_area.index(x, node_max.Y, z);
542 // Check node at base of mapchunk above, either a node of a previously
543 // generated mapchunk or if not, a node of overgenerated base terrain.
544 content_t c_above = vm->m_data[vi + em.X].getContent();
545 bool air_above = c_above == CONTENT_AIR;
546 bool river_water_above = c_above == c_river_water_source;
547 bool water_above = c_above == c_water_source || river_water_above;
549 // If there is air or water above enable top/filler placement, otherwise force
550 // nplaced to stone level by setting a number exceeding any possible filler depth.
551 u16 nplaced = (air_above || water_above) ? 0 : U16_MAX;
553 for (s16 y = node_max.Y; y >= node_min.Y; y--) {
554 content_t c = vm->m_data[vi].getContent();
556 // Biome is recalculated each time an upper surface is detected while
557 // working down a column. The selected biome then remains in effect for
558 // all nodes below until the next surface and biome recalculation.
559 // Biome is recalculated:
560 // 1. At the surface of stone below air or water.
561 // 2. At the surface of water below air.
562 // 3. When stone or water is detected but biome has not yet been calculated.
563 if ((c == c_stone && (air_above || water_above || !biome))
564 || ((c == c_water_source || c == c_river_water_source)
565 && (air_above || !biome))) {
566 biome = biomegen->getBiomeAtIndex(index, y);
568 depth_top = biome->depth_top;
569 base_filler = MYMAX(depth_top +
570 biome->depth_filler +
571 noise_filler_depth->result[index], 0.f);
572 depth_water_top = biome->depth_water_top;
573 depth_riverbed = biome->depth_riverbed;
575 // Detect stone type for dungeons during every biome calculation.
576 // This is more efficient than detecting per-node and will not
577 // miss any desert stone or sandstone biomes.
578 if (biome->c_stone == c_desert_stone)
579 stone_type = MGSTONE_DESERT_STONE;
580 else if (biome->c_stone == c_sandstone)
581 stone_type = MGSTONE_SANDSTONE;
585 content_t c_below = vm->m_data[vi - em.X].getContent();
587 // If the node below isn't solid, make this node stone, so that
588 // any top/filler nodes above are structurally supported.
589 // This is done by aborting the cycle of top/filler placement
590 // immediately by forcing nplaced to stone level.
591 if (c_below == CONTENT_AIR
592 || c_below == c_water_source
593 || c_below == c_river_water_source)
596 if (river_water_above) {
597 if (nplaced < depth_riverbed) {
598 vm->m_data[vi] = MapNode(biome->c_riverbed);
601 nplaced = U16_MAX; // Disable top/filler placement
602 river_water_above = false;
604 } else if (nplaced < depth_top) {
605 vm->m_data[vi] = MapNode(biome->c_top);
607 } else if (nplaced < base_filler) {
608 vm->m_data[vi] = MapNode(biome->c_filler);
611 vm->m_data[vi] = MapNode(biome->c_stone);
616 } else if (c == c_water_source) {
617 vm->m_data[vi] = MapNode((y > (s32)(water_level - depth_water_top))
618 ? biome->c_water_top : biome->c_water);
619 nplaced = 0; // Enable top/filler placement for next surface
622 } else if (c == c_river_water_source) {
623 vm->m_data[vi] = MapNode(biome->c_river_water);
624 nplaced = 0; // Enable riverbed placement for next surface
627 river_water_above = true;
628 } else if (c == CONTENT_AIR) {
629 nplaced = 0; // Enable top/filler placement for next surface
632 } else { // Possible various nodes overgenerated from neighbouring mapchunks
633 nplaced = U16_MAX; // Disable top/filler placement
638 vm->m_area.add_y(em, vi, -1);
646 void MapgenBasic::dustTopNodes()
648 if (node_max.Y < water_level)
651 v3s16 em = vm->m_area.getExtent();
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++) {
656 Biome *biome = (Biome *)m_bmgr->getRaw(biomemap[index]);
658 if (biome->c_dust == CONTENT_IGNORE)
661 u32 vi = vm->m_area.index(x, full_node_max.Y, z);
662 content_t c_full_max = vm->m_data[vi].getContent();
665 if (c_full_max == CONTENT_AIR) {
666 y_start = full_node_max.Y - 1;
667 } else if (c_full_max == CONTENT_IGNORE) {
668 vi = vm->m_area.index(x, node_max.Y + 1, z);
669 content_t c_max = vm->m_data[vi].getContent();
671 if (c_max == CONTENT_AIR)
672 y_start = node_max.Y;
679 vi = vm->m_area.index(x, y_start, z);
680 for (s16 y = y_start; y >= node_min.Y - 1; y--) {
681 if (vm->m_data[vi].getContent() != CONTENT_AIR)
684 vm->m_area.add_y(em, vi, -1);
687 content_t c = vm->m_data[vi].getContent();
688 if (!ndef->get(c).buildable_to && c != CONTENT_IGNORE && c != biome->c_dust) {
689 vm->m_area.add_y(em, vi, 1);
690 vm->m_data[vi] = MapNode(biome->c_dust);
696 void MapgenBasic::generateCaves(s16 max_stone_y, s16 large_cave_depth)
698 if (max_stone_y < node_min.Y)
701 CavesNoiseIntersection caves_noise(ndef, m_bmgr, csize,
702 &np_cave1, &np_cave2, seed, cave_width);
704 caves_noise.generateCaves(vm, node_min, node_max, biomemap);
706 if (node_max.Y > large_cave_depth)
709 PseudoRandom ps(blockseed + 21343);
710 u32 bruises_count = ps.range(0, 2);
711 for (u32 i = 0; i < bruises_count; i++) {
712 CavesRandomWalk cave(ndef, &gennotify, seed, water_level,
713 c_water_source, CONTENT_IGNORE);
715 cave.makeCave(vm, node_min, node_max, &ps, true, max_stone_y, heightmap);
720 void MapgenBasic::generateDungeons(s16 max_stone_y, MgStoneType stone_type)
722 if (max_stone_y < node_min.Y)
729 dp.np_rarity = nparams_dungeon_rarity;
730 dp.np_density = nparams_dungeon_density;
731 dp.np_wetness = nparams_dungeon_wetness;
732 dp.c_water = c_water_source;
734 switch (stone_type) {
737 dp.c_cobble = c_cobble;
738 dp.c_moss = c_mossycobble;
739 dp.c_stair = c_stair_cobble;
741 dp.diagonal_dirs = false;
743 dp.holesize = v3s16(1, 2, 1);
744 dp.roomsize = v3s16(0, 0, 0);
745 dp.notifytype = GENNOTIFY_DUNGEON;
747 case MGSTONE_DESERT_STONE:
748 dp.c_cobble = c_desert_stone;
749 dp.c_moss = c_desert_stone;
750 dp.c_stair = c_desert_stone;
752 dp.diagonal_dirs = true;
754 dp.holesize = v3s16(2, 3, 2);
755 dp.roomsize = v3s16(2, 5, 2);
756 dp.notifytype = GENNOTIFY_TEMPLE;
758 case MGSTONE_SANDSTONE:
759 dp.c_cobble = c_sandstonebrick;
760 dp.c_moss = c_sandstonebrick;
761 dp.c_stair = c_sandstonebrick;
763 dp.diagonal_dirs = false;
765 dp.holesize = v3s16(2, 2, 2);
766 dp.roomsize = v3s16(2, 0, 2);
767 dp.notifytype = GENNOTIFY_DUNGEON;
771 DungeonGen dgen(ndef, &gennotify, &dp);
772 dgen.generate(vm, blockseed, full_node_min, full_node_max);
777 //// GenerateNotifier
780 GenerateNotifier::GenerateNotifier()
786 GenerateNotifier::GenerateNotifier(u32 notify_on,
787 std::set<u32> *notify_on_deco_ids)
789 m_notify_on = notify_on;
790 m_notify_on_deco_ids = notify_on_deco_ids;
794 void GenerateNotifier::setNotifyOn(u32 notify_on)
796 m_notify_on = notify_on;
800 void GenerateNotifier::setNotifyOnDecoIds(std::set<u32> *notify_on_deco_ids)
802 m_notify_on_deco_ids = notify_on_deco_ids;
806 bool GenerateNotifier::addEvent(GenNotifyType type, v3s16 pos, u32 id)
808 if (!(m_notify_on & (1 << type)))
811 if (type == GENNOTIFY_DECORATION &&
812 m_notify_on_deco_ids->find(id) == m_notify_on_deco_ids->end())
819 m_notify_events.push_back(gne);
825 void GenerateNotifier::getEvents(
826 std::map<std::string, std::vector<v3s16> > &event_map,
829 std::list<GenNotifyEvent>::iterator it;
831 for (it = m_notify_events.begin(); it != m_notify_events.end(); ++it) {
832 GenNotifyEvent &gn = *it;
833 std::string name = (gn.type == GENNOTIFY_DECORATION) ?
834 "decoration#"+ itos(gn.id) :
835 flagdesc_gennotify[gn.type].name;
837 event_map[name].push_back(gn.pos);
841 m_notify_events.clear();
850 MapgenParams::~MapgenParams()
857 void MapgenParams::load(const Settings &settings)
859 std::string seed_str;
860 const char *seed_name = (&settings == g_settings) ? "fixed_map_seed" : "seed";
862 if (settings.getNoEx(seed_name, seed_str) && !seed_str.empty())
863 seed = read_seed(seed_str.c_str());
865 myrand_bytes(&seed, sizeof(seed));
867 settings.getNoEx("mg_name", mg_name);
868 settings.getS16NoEx("water_level", water_level);
869 settings.getS16NoEx("chunksize", chunksize);
870 settings.getFlagStrNoEx("mg_flags", flags, flagdesc_mapgen);
873 bparams = BiomeManager::createBiomeParams(BIOMEGEN_ORIGINAL);
875 bparams->readParams(&settings);
876 bparams->seed = seed;
880 MapgenFactory *mgfactory = EmergeManager::getMapgenFactory(mg_name);
882 sparams = mgfactory->createMapgenParams();
883 sparams->readParams(&settings);
888 void MapgenParams::save(Settings &settings) const
890 settings.set("mg_name", mg_name);
891 settings.setU64("seed", seed);
892 settings.setS16("water_level", water_level);
893 settings.setS16("chunksize", chunksize);
894 settings.setFlagStr("mg_flags", flags, flagdesc_mapgen, U32_MAX);
897 bparams->writeParams(&settings);
900 sparams->writeParams(&settings);