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 u32 vi = vm->m_area.index(x, node_max.Y, z);
541 // Check node at base of mapchunk above, either a node of a previously
542 // generated mapchunk or if not, a node of overgenerated base terrain.
543 content_t c_above = vm->m_data[vi + em.X].getContent();
544 bool air_above = c_above == CONTENT_AIR;
545 bool water_above = (c_above == c_water_source || c_above == c_river_water_source);
547 // If there is air or water above enable top/filler placement, otherwise force
548 // nplaced to stone level by setting a number exceeding any possible filler depth.
549 u16 nplaced = (air_above || water_above) ? 0 : U16_MAX;
551 for (s16 y = node_max.Y; y >= node_min.Y; y--) {
552 content_t c = vm->m_data[vi].getContent();
554 // Biome is recalculated each time an upper surface is detected while
555 // working down a column. The selected biome then remains in effect for
556 // all nodes below until the next surface and biome recalculation.
557 // Biome is recalculated:
558 // 1. At the surface of stone below air or water.
559 // 2. At the surface of water below air.
560 // 3. When stone or water is detected but biome has not yet been calculated.
561 if ((c == c_stone && (air_above || water_above || !biome))
562 || ((c == c_water_source || c == c_river_water_source)
563 && (air_above || !biome))) {
564 biome = biomegen->getBiomeAtIndex(index, y);
566 depth_top = biome->depth_top;
567 base_filler = MYMAX(depth_top
568 + biome->depth_filler
569 + noise_filler_depth->result[index], 0.f);
570 depth_water_top = biome->depth_water_top;
572 // Detect stone type for dungeons during every biome calculation.
573 // This is more efficient than detecting per-node and will not
574 // miss any desert stone or sandstone biomes.
575 if (biome->c_stone == c_desert_stone)
576 stone_type = MGSTONE_DESERT_STONE;
577 else if (biome->c_stone == c_sandstone)
578 stone_type = MGSTONE_SANDSTONE;
582 content_t c_below = vm->m_data[vi - em.X].getContent();
584 // If the node below isn't solid, make this node stone, so that
585 // any top/filler nodes above are structurally supported.
586 // This is done by aborting the cycle of top/filler placement
587 // immediately by forcing nplaced to stone level.
588 if (c_below == CONTENT_AIR
589 || c_below == c_water_source
590 || c_below == c_river_water_source)
593 if (nplaced < depth_top) {
594 vm->m_data[vi] = MapNode(biome->c_top);
596 } else if (nplaced < base_filler) {
597 vm->m_data[vi] = MapNode(biome->c_filler);
600 vm->m_data[vi] = MapNode(biome->c_stone);
605 } else if (c == c_water_source) {
606 vm->m_data[vi] = MapNode((y > (s32)(water_level - depth_water_top))
607 ? biome->c_water_top : biome->c_water);
608 nplaced = 0; // Enable top/filler placement for next surface
611 } else if (c == c_river_water_source) {
612 vm->m_data[vi] = MapNode(biome->c_river_water);
613 nplaced = depth_top; // Enable filler placement for next surface
616 } else if (c == CONTENT_AIR) {
617 nplaced = 0; // Enable top/filler placement for next surface
620 } else { // Possible various nodes overgenerated from neighbouring mapchunks
621 nplaced = U16_MAX; // Disable top/filler placement
626 vm->m_area.add_y(em, vi, -1);
634 void MapgenBasic::dustTopNodes()
636 if (node_max.Y < water_level)
639 v3s16 em = vm->m_area.getExtent();
642 for (s16 z = node_min.Z; z <= node_max.Z; z++)
643 for (s16 x = node_min.X; x <= node_max.X; x++, index++) {
644 Biome *biome = (Biome *)m_bmgr->getRaw(biomemap[index]);
646 if (biome->c_dust == CONTENT_IGNORE)
649 u32 vi = vm->m_area.index(x, full_node_max.Y, z);
650 content_t c_full_max = vm->m_data[vi].getContent();
653 if (c_full_max == CONTENT_AIR) {
654 y_start = full_node_max.Y - 1;
655 } else if (c_full_max == CONTENT_IGNORE) {
656 vi = vm->m_area.index(x, node_max.Y + 1, z);
657 content_t c_max = vm->m_data[vi].getContent();
659 if (c_max == CONTENT_AIR)
660 y_start = node_max.Y;
667 vi = vm->m_area.index(x, y_start, z);
668 for (s16 y = y_start; y >= node_min.Y - 1; y--) {
669 if (vm->m_data[vi].getContent() != CONTENT_AIR)
672 vm->m_area.add_y(em, vi, -1);
675 content_t c = vm->m_data[vi].getContent();
676 if (!ndef->get(c).buildable_to && c != CONTENT_IGNORE && c != biome->c_dust) {
677 vm->m_area.add_y(em, vi, 1);
678 vm->m_data[vi] = MapNode(biome->c_dust);
684 void MapgenBasic::generateCaves(s16 max_stone_y, s16 large_cave_depth)
686 if (max_stone_y < node_min.Y)
689 CavesNoiseIntersection caves_noise(ndef, m_bmgr, csize,
690 &np_cave1, &np_cave2, seed, cave_width);
692 caves_noise.generateCaves(vm, node_min, node_max, biomemap);
694 if (node_max.Y > large_cave_depth)
697 PseudoRandom ps(blockseed + 21343);
698 u32 bruises_count = ps.range(0, 2);
699 for (u32 i = 0; i < bruises_count; i++) {
700 CavesRandomWalk cave(ndef, &gennotify, seed, water_level,
701 c_water_source, CONTENT_IGNORE);
703 cave.makeCave(vm, node_min, node_max, &ps, true, max_stone_y, heightmap);
708 void MapgenBasic::generateDungeons(s16 max_stone_y, MgStoneType stone_type)
710 if (max_stone_y < node_min.Y)
717 dp.np_rarity = nparams_dungeon_rarity;
718 dp.np_density = nparams_dungeon_density;
719 dp.np_wetness = nparams_dungeon_wetness;
720 dp.c_water = c_water_source;
722 switch (stone_type) {
725 dp.c_cobble = c_cobble;
726 dp.c_moss = c_mossycobble;
727 dp.c_stair = c_stair_cobble;
729 dp.diagonal_dirs = false;
731 dp.holesize = v3s16(1, 2, 1);
732 dp.roomsize = v3s16(0, 0, 0);
733 dp.notifytype = GENNOTIFY_DUNGEON;
735 case MGSTONE_DESERT_STONE:
736 dp.c_cobble = c_desert_stone;
737 dp.c_moss = c_desert_stone;
738 dp.c_stair = c_desert_stone;
740 dp.diagonal_dirs = true;
742 dp.holesize = v3s16(2, 3, 2);
743 dp.roomsize = v3s16(2, 5, 2);
744 dp.notifytype = GENNOTIFY_TEMPLE;
746 case MGSTONE_SANDSTONE:
747 dp.c_cobble = c_sandstonebrick;
748 dp.c_moss = c_sandstonebrick;
749 dp.c_stair = c_sandstonebrick;
751 dp.diagonal_dirs = false;
753 dp.holesize = v3s16(2, 2, 2);
754 dp.roomsize = v3s16(2, 0, 2);
755 dp.notifytype = GENNOTIFY_DUNGEON;
759 DungeonGen dgen(ndef, &gennotify, &dp);
760 dgen.generate(vm, blockseed, full_node_min, full_node_max);
765 //// GenerateNotifier
768 GenerateNotifier::GenerateNotifier()
774 GenerateNotifier::GenerateNotifier(u32 notify_on,
775 std::set<u32> *notify_on_deco_ids)
777 m_notify_on = notify_on;
778 m_notify_on_deco_ids = notify_on_deco_ids;
782 void GenerateNotifier::setNotifyOn(u32 notify_on)
784 m_notify_on = notify_on;
788 void GenerateNotifier::setNotifyOnDecoIds(std::set<u32> *notify_on_deco_ids)
790 m_notify_on_deco_ids = notify_on_deco_ids;
794 bool GenerateNotifier::addEvent(GenNotifyType type, v3s16 pos, u32 id)
796 if (!(m_notify_on & (1 << type)))
799 if (type == GENNOTIFY_DECORATION &&
800 m_notify_on_deco_ids->find(id) == m_notify_on_deco_ids->end())
807 m_notify_events.push_back(gne);
813 void GenerateNotifier::getEvents(
814 std::map<std::string, std::vector<v3s16> > &event_map,
817 std::list<GenNotifyEvent>::iterator it;
819 for (it = m_notify_events.begin(); it != m_notify_events.end(); ++it) {
820 GenNotifyEvent &gn = *it;
821 std::string name = (gn.type == GENNOTIFY_DECORATION) ?
822 "decoration#"+ itos(gn.id) :
823 flagdesc_gennotify[gn.type].name;
825 event_map[name].push_back(gn.pos);
829 m_notify_events.clear();
838 MapgenParams::~MapgenParams()
845 void MapgenParams::load(const Settings &settings)
847 std::string seed_str;
848 const char *seed_name = (&settings == g_settings) ? "fixed_map_seed" : "seed";
850 if (settings.getNoEx(seed_name, seed_str) && !seed_str.empty())
851 seed = read_seed(seed_str.c_str());
853 myrand_bytes(&seed, sizeof(seed));
855 settings.getNoEx("mg_name", mg_name);
856 settings.getS16NoEx("water_level", water_level);
857 settings.getS16NoEx("chunksize", chunksize);
858 settings.getFlagStrNoEx("mg_flags", flags, flagdesc_mapgen);
861 bparams = BiomeManager::createBiomeParams(BIOMEGEN_ORIGINAL);
863 bparams->readParams(&settings);
864 bparams->seed = seed;
868 MapgenFactory *mgfactory = EmergeManager::getMapgenFactory(mg_name);
870 sparams = mgfactory->createMapgenParams();
871 sparams->readParams(&settings);
876 void MapgenParams::save(Settings &settings) const
878 settings.set("mg_name", mg_name);
879 settings.setU64("seed", seed);
880 settings.setS16("water_level", water_level);
881 settings.setS16("chunksize", chunksize);
882 settings.setFlagStr("mg_flags", flags, flagdesc_mapgen, U32_MAX);
885 bparams->writeParams(&settings);
888 sparams->writeParams(&settings);