X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fmapgen.cpp;h=301601b6c4441fbb64c260b6df7afe39c2b0b129;hb=4e1f50035e860a00636ca5d804c267119df99601;hp=73fe633185370cbc1b47210ccee95840115658a1;hpb=6d0ea26c2d62c3774ff384cf1bfc2a3372b49a3b;p=dragonfireclient.git diff --git a/src/mapgen.cpp b/src/mapgen.cpp index 73fe63318..301601b6c 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -34,2970 +34,1103 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "main.h" // For g_profiler #include "treegen.h" #include "mapgen_v6.h" +#include "mapgen_v7.h" +#include "serialization.h" +#include "util/serialize.h" +#include "filesys.h" FlagDesc flagdesc_mapgen[] = { {"trees", MG_TREES}, {"caves", MG_CAVES}, {"dungeons", MG_DUNGEONS}, - {"v6_forests", MGV6_FORESTS}, + {"v6_jungles", MGV6_JUNGLES}, {"v6_biome_blend", MGV6_BIOME_BLEND}, {"flat", MG_FLAT}, - {NULL, 0} + {"nolight", MG_NOLIGHT}, + {NULL, 0} }; -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////// Emerge Manager //////////////////////////////// +FlagDesc flagdesc_ore[] = { + {"absheight", OREFLAG_ABSHEIGHT}, + {"scatter_noisedensity", OREFLAG_DENSITY}, + {"claylike_nodeisnt", OREFLAG_NODEISNT}, + {NULL, 0} +}; +FlagDesc flagdesc_deco_schematic[] = { + {"place_center_x", DECO_PLACE_CENTER_X}, + {"place_center_y", DECO_PLACE_CENTER_Y}, + {"place_center_z", DECO_PLACE_CENTER_Z}, + {NULL, 0} +}; -EmergeManager::EmergeManager(IGameDef *gamedef, BiomeDefManager *bdef) { - //register built-in mapgens - registerMapgen("v6", new MapgenFactoryV6()); - - //the order of these assignments is pretty important - this->biomedef = bdef ? bdef : new BiomeDefManager(gamedef); - this->params = NULL; - this->mapgen = NULL; -} +/////////////////////////////////////////////////////////////////////////////// -EmergeManager::~EmergeManager() { - delete biomedef; - delete mapgen; - delete params; +Ore *createOre(OreType type) { + switch (type) { + case ORE_SCATTER: + return new OreScatter; + case ORE_SHEET: + return new OreSheet; + //case ORE_CLAYLIKE: //TODO: implement this! + // return new OreClaylike; + default: + return NULL; + } } -void EmergeManager::initMapgens(MapgenParams *mgparams) { - if (mapgen) - return; - - this->params = mgparams; - this->mapgen = getMapgen(); //only one mapgen for now! +Ore::~Ore() { + delete np; + delete noise; } -Mapgen *EmergeManager::getMapgen() { - if (!mapgen) { - mapgen = createMapgen(params->mg_name, 0, params, this); - if (!mapgen) { - infostream << "EmergeManager: falling back to mapgen v6" << std::endl; - delete params; - params = createMapgenParams("v6"); - mapgen = createMapgen("v6", 0, params, this); +void Ore::resolveNodeNames(INodeDefManager *ndef) { + if (ore == CONTENT_IGNORE) { + ore = ndef->getId(ore_name); + if (ore == CONTENT_IGNORE) { + errorstream << "Ore::resolveNodeNames: ore node '" + << ore_name << "' not defined"; + ore = CONTENT_AIR; + wherein.push_back(CONTENT_AIR); + return; } } - return mapgen; -} -void EmergeManager::addBlockToQueue() { - //STUB + for (size_t i=0; i != wherein_names.size(); i++) { + std::string name = wherein_names[i]; + content_t c = ndef->getId(name); + if (c != CONTENT_IGNORE) { + wherein.push_back(c); + } + } } -int EmergeManager::getGroundLevelAtPoint(v2s16 p) { - if (!mapgen) - return 0; - return mapgen->getGroundLevelAtPoint(p); -} +void Ore::placeOre(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax) { + int in_range = 0; + in_range |= (nmin.Y <= height_max && nmax.Y >= height_min); + if (flags & OREFLAG_ABSHEIGHT) + in_range |= (nmin.Y >= -height_max && nmax.Y <= -height_min) << 1; + if (!in_range) + return; -bool EmergeManager::isBlockUnderground(v3s16 blockpos) { - /* - v2s16 p = v2s16((blockpos.X * MAP_BLOCKSIZE) + MAP_BLOCKSIZE / 2, - (blockpos.Y * MAP_BLOCKSIZE) + MAP_BLOCKSIZE / 2); - int ground_level = getGroundLevelAtPoint(p); - return blockpos.Y * (MAP_BLOCKSIZE + 1) <= min(water_level, ground_level); - */ + int ymin, ymax; + if (in_range & ORE_RANGE_MIRROR) { + ymin = MYMAX(nmin.Y, -height_max); + ymax = MYMIN(nmax.Y, -height_min); + } else { + ymin = MYMAX(nmin.Y, height_min); + ymax = MYMIN(nmax.Y, height_max); + } + if (clust_size >= ymax - ymin + 1) + return; - //yuck, but then again, should i bother being accurate? - //the height of the nodes in a single block is quite variable - return blockpos.Y * (MAP_BLOCKSIZE + 1) <= params->water_level; + nmin.Y = ymin; + nmax.Y = ymax; + generate(mg->vm, mg->seed, blockseed, nmin, nmax); } -u32 EmergeManager::getBlockSeed(v3s16 p) { - return (u32)(params->seed & 0xFFFFFFFF) + - p.Z * 38134234 + - p.Y * 42123 + - p.Y * 23; -} +void OreScatter::generate(ManualMapVoxelManipulator *vm, int seed, + u32 blockseed, v3s16 nmin, v3s16 nmax) { + PseudoRandom pr(blockseed); + MapNode n_ore(ore, 0, ore_param2); + + int volume = (nmax.X - nmin.X + 1) * + (nmax.Y - nmin.Y + 1) * + (nmax.Z - nmin.Z + 1); + int csize = clust_size; + int orechance = (csize * csize * csize) / clust_num_ores; + int nclusters = volume / clust_scarcity; + + for (int i = 0; i != nclusters; i++) { + int x0 = pr.range(nmin.X, nmax.X - csize + 1); + int y0 = pr.range(nmin.Y, nmax.Y - csize + 1); + int z0 = pr.range(nmin.Z, nmax.Z - csize + 1); + + if (np && (NoisePerlin3D(np, x0, y0, z0, seed) < nthresh)) + continue; + for (int z1 = 0; z1 != csize; z1++) + for (int y1 = 0; y1 != csize; y1++) + for (int x1 = 0; x1 != csize; x1++) { + if (pr.range(1, orechance) != 1) + continue; -Mapgen *EmergeManager::createMapgen(std::string mgname, int mgid, - MapgenParams *mgparams, EmergeManager *emerge) { - std::map::const_iterator iter = mglist.find(mgname); - if (iter == mglist.end()) { - errorstream << "EmergeManager; mapgen " << mgname << - " not registered" << std::endl; - return NULL; + u32 i = vm->m_area.index(x0 + x1, y0 + y1, z0 + z1); + for (size_t ii = 0; ii < wherein.size(); ii++) + if (vm->m_data[i].getContent() == wherein[ii]) + vm->m_data[i] = n_ore; + } } - - MapgenFactory *mgfactory = iter->second; - return mgfactory->createMapgen(mgid, mgparams, emerge); } -MapgenParams *EmergeManager::createMapgenParams(std::string mgname) { - std::map::const_iterator iter = mglist.find(mgname); - if (iter == mglist.end()) { - errorstream << "EmergeManager: mapgen " << mgname << - " not registered" << std::endl; - return NULL; +void OreSheet::generate(ManualMapVoxelManipulator *vm, int seed, + u32 blockseed, v3s16 nmin, v3s16 nmax) { + PseudoRandom pr(blockseed + 4234); + MapNode n_ore(ore, 0, ore_param2); + + int max_height = clust_size; + int y_start = pr.range(nmin.Y, nmax.Y - max_height); + + if (!noise) { + int sx = nmax.X - nmin.X + 1; + int sz = nmax.Z - nmin.Z + 1; + noise = new Noise(np, 0, sx, sz); + } + noise->seed = seed + y_start; + noise->perlinMap2D(nmin.X, nmin.Z); + + int index = 0; + for (int z = nmin.Z; z <= nmax.Z; z++) + for (int x = nmin.X; x <= nmax.X; x++) { + float noiseval = noise->result[index++]; + if (noiseval < nthresh) + continue; + + int height = max_height * (1. / pr.range(1, 3)); + int y0 = y_start + np->scale * noiseval; //pr.range(1, 3) - 1; + int y1 = y0 + height; + for (int y = y0; y != y1; y++) { + u32 i = vm->m_area.index(x, y, z); + if (!vm->m_area.contains(i)) + continue; + + for (size_t ii = 0; ii < wherein.size(); ii++) + if (vm->m_data[i].getContent() == wherein[ii]) + vm->m_data[i] = n_ore; + } } - - MapgenFactory *mgfactory = iter->second; - return mgfactory->createMapgenParams(); } -MapgenParams *EmergeManager::getParamsFromSettings(Settings *settings) { - std::string mg_name = settings->get("mg_name"); - MapgenParams *mgparams = createMapgenParams(mg_name); - - mgparams->mg_name = mg_name; - mgparams->seed = settings->getU64(settings == g_settings ? "fixed_map_seed" : "seed"); - mgparams->water_level = settings->getS16("water_level"); - mgparams->chunksize = settings->getS16("chunksize"); - mgparams->flags = settings->getFlagStr("mg_flags", flagdesc_mapgen); - - if (!mgparams->readParams(settings)) { - delete mgparams; - return NULL; +/////////////////////////////////////////////////////////////////////////////// + + +Decoration *createDecoration(DecorationType type) { + switch (type) { + case DECO_SIMPLE: + return new DecoSimple; + case DECO_SCHEMATIC: + return new DecoSchematic; + //case DECO_LSYSTEM: + // return new DecoLSystem; + default: + return NULL; } - return mgparams; } -void EmergeManager::setParamsToSettings(Settings *settings) { - settings->set("mg_name", params->mg_name); - settings->setU64("seed", params->seed); - settings->setS16("water_level", params->water_level); - settings->setS16("chunksize", params->chunksize); - settings->setFlagStr("mg_flags", params->flags, flagdesc_mapgen); +Decoration::Decoration() { + mapseed = 0; + np = NULL; + fill_ratio = 0; + sidelen = 1; +} + - params->writeParams(settings); +Decoration::~Decoration() { + delete np; } -void EmergeManager::registerMapgen(std::string mgname, MapgenFactory *mgfactory) { - mglist.insert(std::make_pair(mgname, mgfactory)); - infostream << "EmergeManager: registered mapgen " << mgname << std::endl; +void Decoration::resolveNodeNames(INodeDefManager *ndef) { + this->ndef = ndef; + + if (c_place_on == CONTENT_IGNORE) + c_place_on = ndef->getId(place_on_name); } -///////////////////// +void Decoration::placeDeco(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax) { + PseudoRandom ps(blockseed + 53); + int carea_size = nmax.X - nmin.X + 1; -bool MapgenV6Params::readParams(Settings *settings) { - freq_desert = settings->getFloat("mgv6_freq_desert"); - freq_beach = settings->getFloat("mgv6_freq_beach"); + // Divide area into parts + if (carea_size % sidelen) { + errorstream << "Decoration::placeDeco: chunk size is not divisible by " + "sidelen; setting sidelen to " << carea_size << std::endl; + sidelen = carea_size; + } + + s16 divlen = carea_size / sidelen; + int area = sidelen * sidelen; + + for (s16 z0 = 0; z0 < divlen; z0++) + for (s16 x0 = 0; x0 < divlen; x0++) { + v2s16 p2d_center( // Center position of part of division + nmin.X + sidelen / 2 + sidelen * x0, + nmin.Z + sidelen / 2 + sidelen * z0 + ); + v2s16 p2d_min( // Minimum edge of part of division + nmin.X + sidelen * x0, + nmin.Z + sidelen * z0 + ); + v2s16 p2d_max( // Maximum edge of part of division + nmin.X + sidelen + sidelen * x0 - 1, + nmin.Z + sidelen + sidelen * z0 - 1 + ); - np_terrain_base = settings->getNoiseParams("mgv6_np_terrain_base"); - np_terrain_higher = settings->getNoiseParams("mgv6_np_terrain_higher"); - np_steepness = settings->getNoiseParams("mgv6_np_steepness"); - np_height_select = settings->getNoiseParams("mgv6_np_height_select"); - np_trees = settings->getNoiseParams("mgv6_np_trees"); - np_mud = settings->getNoiseParams("mgv6_np_mud"); - np_beach = settings->getNoiseParams("mgv6_np_beach"); - np_biome = settings->getNoiseParams("mgv6_np_biome"); - np_cave = settings->getNoiseParams("mgv6_np_cave"); - - bool success = - np_terrain_base && np_terrain_higher && np_steepness && - np_height_select && np_trees && np_mud && - np_beach && np_biome && np_cave; - return success; + // Amount of decorations + float nval = np ? + NoisePerlin2D(np, p2d_center.X, p2d_center.Y, mapseed) : + fill_ratio; + u32 deco_count = area * MYMAX(nval, 0.f); + + for (u32 i = 0; i < deco_count; i++) { + s16 x = ps.range(p2d_min.X, p2d_max.X); + s16 z = ps.range(p2d_min.Y, p2d_max.Y); + + int mapindex = carea_size * (z - nmin.Z) + (x - nmin.X); + + s16 y = mg->heightmap ? + mg->heightmap[mapindex] : + mg->findGroundLevel(v2s16(x, z), nmin.Y, nmax.Y); + + if (y < nmin.Y || y > nmax.Y) + continue; + + int height = getHeight(); + int max_y = nmax.Y;// + MAP_BLOCKSIZE - 1; + if (y + 1 + height > max_y) { + continue; +#if 0 + printf("Decoration at (%d %d %d) cut off\n", x, y, z); + //add to queue + JMutexAutoLock cutofflock(cutoff_mutex); + cutoffs.push_back(CutoffData(x, y, z, height)); +#endif + } + + if (mg->biomemap) { + std::set::iterator iter; + + if (biomes.size()) { + iter = biomes.find(mg->biomemap[mapindex]); + if (iter == biomes.end()) + continue; + } + } + + generate(mg, &ps, max_y, v3s16(x, y, z)); + } + } } -void MapgenV6Params::writeParams(Settings *settings) { - settings->setFloat("mgv6_freq_desert", freq_desert); - settings->setFloat("mgv6_freq_beach", freq_beach); +#if 0 +void Decoration::placeCutoffs(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax) { + PseudoRandom pr(blockseed + 53); + std::vector handled_cutoffs; - settings->setNoiseParams("mgv6_np_terrain_base", np_terrain_base); - settings->setNoiseParams("mgv6_np_terrain_higher", np_terrain_higher); - settings->setNoiseParams("mgv6_np_steepness", np_steepness); - settings->setNoiseParams("mgv6_np_height_select", np_height_select); - settings->setNoiseParams("mgv6_np_trees", np_trees); - settings->setNoiseParams("mgv6_np_mud", np_mud); - settings->setNoiseParams("mgv6_np_beach", np_beach); - settings->setNoiseParams("mgv6_np_biome", np_biome); - settings->setNoiseParams("mgv6_np_cave", np_cave); + // Copy over the cutoffs we're interested in so we don't needlessly hold a lock + { + JMutexAutoLock cutofflock(cutoff_mutex); + for (std::list::iterator i = cutoffs.begin(); + i != cutoffs.end(); ++i) { + CutoffData cutoff = *i; + v3s16 p = cutoff.p; + s16 height = cutoff.height; + if (p.X < nmin.X || p.X > nmax.X || + p.Z < nmin.Z || p.Z > nmax.Z) + continue; + if (p.Y + height < nmin.Y || p.Y > nmax.Y) + continue; + + handled_cutoffs.push_back(cutoff); + } + } + + // Generate the cutoffs + for (size_t i = 0; i != handled_cutoffs.size(); i++) { + v3s16 p = handled_cutoffs[i].p; + s16 height = handled_cutoffs[i].height; + + if (p.Y + height > nmax.Y) { + //printf("Decoration at (%d %d %d) cut off again!\n", p.X, p.Y, p.Z); + cuttoffs.push_back(v3s16(p.X, p.Y, p.Z)); + } + + generate(mg, &pr, nmax.Y, nmin.Y - p.Y, v3s16(p.X, nmin.Y, p.Z)); + } + + // Remove cutoffs that were handled from the cutoff list + { + JMutexAutoLock cutofflock(cutoff_mutex); + for (std::list::iterator i = cutoffs.begin(); + i != cutoffs.end(); ++i) { + + for (size_t j = 0; j != handled_cutoffs.size(); j++) { + CutoffData coff = *i; + if (coff.p == handled_cutoffs[j].p) + i = cutoffs.erase(i); + } + } + } } +#endif -/////////////////////////////////// legacy static functions for farmesh +/////////////////////////////////////////////////////////////////////////////// -s16 Mapgen::find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision) { - //just need to return something - s16 level = 5; - return level; +void DecoSimple::resolveNodeNames(INodeDefManager *ndef) { + Decoration::resolveNodeNames(ndef); + + if (c_deco == CONTENT_IGNORE) { + c_deco = ndef->getId(deco_name); + if (c_deco == CONTENT_IGNORE) { + errorstream << "DecoSimple::resolveNodeNames: decoration node '" + << deco_name << "' not defined" << std::endl; + c_deco = CONTENT_AIR; + } + } + if (c_spawnby == CONTENT_IGNORE) { + c_spawnby = ndef->getId(spawnby_name); + if (c_spawnby == CONTENT_IGNORE) { + errorstream << "DecoSimple::resolveNodeNames: spawnby node '" + << deco_name << "' not defined" << std::endl; + nspawnby = -1; + c_spawnby = CONTENT_AIR; + } + } + + if (c_decolist.size()) + return; + + for (size_t i = 0; i != decolist_names.size(); i++) { + content_t c = ndef->getId(decolist_names[i]); + if (c == CONTENT_IGNORE) { + errorstream << "DecoSimple::resolveNodeNames: decolist node '" + << decolist_names[i] << "' not defined" << std::endl; + c = CONTENT_AIR; + } + c_decolist.push_back(c); + } } -bool Mapgen::get_have_beach(u64 seed, v2s16 p2d) { - double sandnoise = noise2d_perlin( - 0.2+(float)p2d.X/250, 0.7+(float)p2d.Y/250, - seed+59420, 3, 0.50); +void DecoSimple::generate(Mapgen *mg, PseudoRandom *pr, s16 max_y, v3s16 p) { + ManualMapVoxelManipulator *vm = mg->vm; + + u32 vi = vm->m_area.index(p); + if (vm->m_data[vi].getContent() != c_place_on && + c_place_on != CONTENT_IGNORE) + return; + + if (nspawnby != -1) { + int nneighs = 0; + v3s16 dirs[8] = { // a Moore neighborhood + v3s16( 0, 0, 1), + v3s16( 0, 0, -1), + v3s16( 1, 0, 0), + v3s16(-1, 0, 0), + v3s16( 1, 0, 1), + v3s16(-1, 0, 1), + v3s16(-1, 0, -1), + v3s16( 1, 0, -1) + }; + + for (int i = 0; i != 8; i++) { + u32 index = vm->m_area.index(p + dirs[i]); + if (vm->m_area.contains(index) && + vm->m_data[index].getContent() == c_spawnby) + nneighs++; + } + + if (nneighs < nspawnby) + return; + } + + size_t ndecos = c_decolist.size(); + content_t c_place = ndecos ? c_decolist[pr->range(0, ndecos - 1)] : c_deco; + + s16 height = (deco_height_max > 0) ? + pr->range(deco_height, deco_height_max) : deco_height; - return (sandnoise > 0.15); + height = MYMIN(height, max_y - p.Y); + + v3s16 em = vm->m_area.getExtent(); + for (int i = 0; i < height; i++) { + vm->m_area.add_y(em, vi, 1); + + content_t c = vm->m_data[vi].getContent(); + if (c != CONTENT_AIR && c != CONTENT_IGNORE) + break; + + vm->m_data[vi] = MapNode(c_place); + } } -double Mapgen::tree_amount_2d(u64 seed, v2s16 p) { - double noise = noise2d_perlin( - 0.5+(float)p.X/125, 0.5+(float)p.Y/125, - seed+2, 4, 0.66); - double zeroval = -0.39; - if(noise < zeroval) - return 0; - else - return 0.04 * (noise-zeroval) / (1.0-zeroval); +int DecoSimple::getHeight() { + return (deco_height_max > 0) ? deco_height_max : deco_height; } -#if 0 /// BIG COMMENT -namespace mapgen -{ +std::string DecoSimple::getName() { + return deco_name; +} -/* - Some helper functions for the map generator -*/ -#if 1 -// Returns Y one under area minimum if not found -static s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d, - INodeDefManager *ndef) -{ - v3s16 em = vmanip.m_area.getExtent(); - s16 y_nodes_max = vmanip.m_area.MaxEdge.Y; - s16 y_nodes_min = vmanip.m_area.MinEdge.Y; - u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y)); - s16 y; - for(y=y_nodes_max; y>=y_nodes_min; y--) - { - MapNode &n = vmanip.m_data[i]; - if(ndef->get(n).walkable) - break; +/////////////////////////////////////////////////////////////////////////////// - vmanip.m_area.add_y(em, i, -1); - } - if(y >= y_nodes_min) - return y; - else - return y_nodes_min - 1; + +DecoSchematic::DecoSchematic() { + node_names = NULL; + schematic = NULL; + flags = 0; + size = v3s16(0, 0, 0); } -#if 0 -// Returns Y one under area minimum if not found -static s16 find_ground_level_clever(VoxelManipulator &vmanip, v2s16 p2d, - INodeDefManager *ndef) -{ - if(!vmanip.m_area.contains(v3s16(p2d.X, vmanip.m_area.MaxEdge.Y, p2d.Y))) - return vmanip.m_area.MinEdge.Y-1; - v3s16 em = vmanip.m_area.getExtent(); - s16 y_nodes_max = vmanip.m_area.MaxEdge.Y; - s16 y_nodes_min = vmanip.m_area.MinEdge.Y; - u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y)); - s16 y; - content_t c_tree = ndef->getId("mapgen_tree"); - content_t c_leaves = ndef->getId("mapgen_leaves"); - for(y=y_nodes_max; y>=y_nodes_min; y--) - { - MapNode &n = vmanip.m_data[i]; - if(ndef->get(n).walkable - && n.getContent() != c_tree - && n.getContent() != c_leaves) - break; - vmanip.m_area.add_y(em, i, -1); - } - if(y >= y_nodes_min) - return y; - else - return y_nodes_min - 1; +DecoSchematic::~DecoSchematic() { + delete node_names; + delete []schematic; } -#endif -// Returns Y one under area minimum if not found -static s16 find_stone_level(VoxelManipulator &vmanip, v2s16 p2d, - INodeDefManager *ndef) -{ - v3s16 em = vmanip.m_area.getExtent(); - s16 y_nodes_max = vmanip.m_area.MaxEdge.Y; - s16 y_nodes_min = vmanip.m_area.MinEdge.Y; - u32 i = vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y)); - s16 y; - content_t c_stone = ndef->getId("mapgen_stone"); - content_t c_desert_stone = ndef->getId("mapgen_desert_stone"); - for(y=y_nodes_max; y>=y_nodes_min; y--) - { - MapNode &n = vmanip.m_data[i]; - content_t c = n.getContent(); - if(c != CONTENT_IGNORE && ( - c == c_stone || c == c_desert_stone)) - break; - vmanip.m_area.add_y(em, i, -1); +void DecoSchematic::resolveNodeNames(INodeDefManager *ndef) { + Decoration::resolveNodeNames(ndef); + + if (filename.empty()) + return; + + if (!node_names) { + errorstream << "DecoSchematic::resolveNodeNames: node name list was " + "not created" << std::endl; + return; + } + + for (size_t i = 0; i != node_names->size(); i++) { + std::string name = node_names->at(i); + + std::map::iterator it; + it = replacements.find(name); + if (it != replacements.end()) + name = it->second; + + content_t c = ndef->getId(name); + if (c == CONTENT_IGNORE) { + errorstream << "DecoSchematic::resolveNodeNames: node '" + << name << "' not defined" << std::endl; + c = CONTENT_AIR; + } + + c_nodes.push_back(c); } - if(y >= y_nodes_min) - return y; - else - return y_nodes_min - 1; + + for (int i = 0; i != size.X * size.Y * size.Z; i++) + schematic[i].setContent(c_nodes[schematic[i].getContent()]); + + delete node_names; + node_names = NULL; } -#endif -#if 0 +void DecoSchematic::generate(Mapgen *mg, PseudoRandom *pr, s16 max_y, v3s16 p) { + ManualMapVoxelManipulator *vm = mg->vm; + + if (flags & DECO_PLACE_CENTER_X) + p.X -= (size.X + 1) / 2; + if (flags & DECO_PLACE_CENTER_Y) + p.Y -= (size.Y + 1) / 2; + if (flags & DECO_PLACE_CENTER_Z) + p.Z -= (size.Z + 1) / 2; + + u32 vi = vm->m_area.index(p); + if (vm->m_data[vi].getContent() != c_place_on && + c_place_on != CONTENT_IGNORE) + return; + + Rotation rot = (rotation == ROTATE_RAND) ? + (Rotation)pr->range(ROTATE_0, ROTATE_270) : rotation; + + blitToVManip(p, vm, rot, false); +} -static void make_papyrus(VoxelManipulator &vmanip, v3s16 p0, - INodeDefManager *ndef) -{ - MapNode papyrusnode(ndef->getId("mapgen_papyrus")); - s16 trunk_h = myrand_range(2, 3); - v3s16 p1 = p0; - for(s16 ii=0; iigetId("mapgen_cactus")); - s16 trunk_h = 3; - v3s16 p1 = p0; - for(s16 ii=0; iim_area.index(p.X + x, p.Y + y, p.Z + z); + if (!vm->m_area.contains(vi)) continue; - u32 vi = vmanip.m_area.index(p); - if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) + + if (schematic[i].getContent() == CONTENT_IGNORE) continue; - vmanip.m_data[vi] = MapNode(ndef->getId("mapgen_cobble")); - } - { - v3s16 p = roomplace + v3s16(roomsize.X-1,y,z); - if(vmanip.m_area.contains(p) == false) + + if (schematic[i].param1 == MTSCHEM_PROB_NEVER) continue; - u32 vi = vmanip.m_area.index(p); - if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) + + if (!force_placement) { + content_t c = vm->m_data[vi].getContent(); + if (c != CONTENT_AIR && c != CONTENT_IGNORE) + continue; + } + + if (schematic[i].param1 != MTSCHEM_PROB_ALWAYS && + myrand_range(1, 255) > schematic[i].param1) continue; - vmanip.m_data[vi] = MapNode(ndef->getId("mapgen_cobble")); + + vm->m_data[vi] = schematic[i]; + vm->m_data[vi].param1 = 0; + + if (rot) + vm->m_data[vi].rotateAlongYAxis(ndef, rot); } } +} - // Make +-Z walls - for(s16 x=0; xgetId("mapgen_cobble")); - } - { - v3s16 p = roomplace + v3s16(x,y,roomsize.Z-1); - if(vmanip.m_area.contains(p) == false) - continue; - u32 vi = vmanip.m_area.index(p); - if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) - continue; - vmanip.m_data[vi] = MapNode(ndef->getId("mapgen_cobble")); - } + +void DecoSchematic::placeStructure(Map *map, v3s16 p) { + assert(schematic != NULL); + ManualMapVoxelManipulator *vm = new ManualMapVoxelManipulator(map); + + Rotation rot = (rotation == ROTATE_RAND) ? + (Rotation)myrand_range(ROTATE_0, ROTATE_270) : rotation; + + v3s16 s = (rot == ROTATE_90 || rot == ROTATE_270) ? + v3s16(size.Z, size.Y, size.X) : size; + + if (flags & DECO_PLACE_CENTER_X) + p.X -= (s.X + 1) / 2; + if (flags & DECO_PLACE_CENTER_Y) + p.Y -= (s.Y + 1) / 2; + if (flags & DECO_PLACE_CENTER_Z) + p.Z -= (s.Z + 1) / 2; + + v3s16 bp1 = getNodeBlockPos(p); + v3s16 bp2 = getNodeBlockPos(p + s - v3s16(1,1,1)); + vm->initialEmerge(bp1, bp2); + + blitToVManip(p, vm, rot, true); + + std::map lighting_modified_blocks; + std::map modified_blocks; + vm->blitBackAll(&modified_blocks); + + // TODO: Optimize this by using Mapgen::calcLighting() instead + lighting_modified_blocks.insert(modified_blocks.begin(), modified_blocks.end()); + map->updateLighting(lighting_modified_blocks, modified_blocks); + + MapEditEvent event; + event.type = MEET_OTHER; + for (std::map::iterator + it = modified_blocks.begin(); + it != modified_blocks.end(); ++it) + event.modified_blocks.insert(it->first); + + map->dispatchEvent(&event); +} + + +bool DecoSchematic::loadSchematicFile() { + content_t cignore = CONTENT_IGNORE; + bool have_cignore = false; + + std::ifstream is(filename.c_str(), std::ios_base::binary); + + u32 signature = readU32(is); + if (signature != MTSCHEM_FILE_SIGNATURE) { + errorstream << "loadSchematicFile: invalid schematic " + "file" << std::endl; + return false; + } + + u16 version = readU16(is); + if (version > 2) { + errorstream << "loadSchematicFile: unsupported schematic " + "file version" << std::endl; + return false; } - // Make +-Y walls (floor and ceiling) - for(s16 z=0; zgetId("mapgen_cobble")); - } - { - v3s16 p = roomplace + v3s16(x,roomsize.Y-1,z); - if(vmanip.m_area.contains(p) == false) - continue; - u32 vi = vmanip.m_area.index(p); - if(vmanip.m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) - continue; - vmanip.m_data[vi] = MapNode(ndef->getId("mapgen_cobble")); + size = readV3S16(is); + int nodecount = size.X * size.Y * size.Z; + + u16 nidmapcount = readU16(is); + + node_names = new std::vector; + for (int i = 0; i != nidmapcount; i++) { + std::string name = deSerializeString(is); + if (name == "ignore") { + name = "air"; + cignore = i; + have_cignore = true; } + node_names->push_back(name); } - // Fill with air - for(s16 z=1; z usednodes; + int nodecount = size.X * size.Y * size.Z; + build_nnlist_and_update_ids(schematic, nodecount, &usednodes); + + u16 numids = usednodes.size(); + writeU16(ss, numids); // name count + for (int i = 0; i != numids; i++) + ss << serializeString(ndef->get(usednodes[i]).name); // node names + + // compressed bulk node data + MapNode::serializeBulk(ss, SER_FMT_VER_HIGHEST_WRITE, schematic, + nodecount, 2, 2, true); -static void make_door1(VoxelManipulator &vmanip, v3s16 doorplace, v3s16 doordir, - INodeDefManager *ndef) -{ - make_hole1(vmanip, doorplace, ndef); - // Place torch (for testing) - //vmanip.m_data[vmanip.m_area.index(doorplace)] = MapNode(ndef->getId("mapgen_torch")); + fs::safeWriteToFile(filename, ss.str()); } -static v3s16 rand_ortho_dir(PseudoRandom &random) -{ - if(random.next()%2==0) - return random.next()%2 ? v3s16(-1,0,0) : v3s16(1,0,0); - else - return random.next()%2 ? v3s16(0,0,-1) : v3s16(0,0,1); -} -static v3s16 turn_xz(v3s16 olddir, int t) -{ - v3s16 dir; - if(t == 0) - { - // Turn right - dir.X = olddir.Z; - dir.Z = -olddir.X; - dir.Y = olddir.Y; - } - else - { - // Turn left - dir.X = -olddir.Z; - dir.Z = olddir.X; - dir.Y = olddir.Y; - } - return dir; -} +void build_nnlist_and_update_ids(MapNode *nodes, u32 nodecount, + std::vector *usednodes) { + std::map nodeidmap; + content_t numids = 0; + + for (u32 i = 0; i != nodecount; i++) { + content_t id; + content_t c = nodes[i].getContent(); -static v3s16 random_turn(PseudoRandom &random, v3s16 olddir) -{ - int turn = random.range(0,2); - v3s16 dir; - if(turn == 0) - { - // Go straight - dir = olddir; + std::map::const_iterator it = nodeidmap.find(c); + if (it == nodeidmap.end()) { + id = numids; + numids++; + + usednodes->push_back(c); + nodeidmap.insert(std::make_pair(c, id)); + } else { + id = it->second; + } + nodes[i].setContent(id); } - else if(turn == 1) - // Turn right - dir = turn_xz(olddir, 0); - else - // Turn left - dir = turn_xz(olddir, 1); - return dir; } -static void make_corridor(VoxelManipulator &vmanip, v3s16 doorplace, - v3s16 doordir, v3s16 &result_place, v3s16 &result_dir, - PseudoRandom &random, INodeDefManager *ndef) -{ - make_hole1(vmanip, doorplace, ndef); - v3s16 p0 = doorplace; - v3s16 dir = doordir; - u32 length; - if(random.next()%2) - length = random.range(1,13); - else - length = random.range(1,6); - length = random.range(1,13); - u32 partlength = random.range(1,13); - u32 partcount = 0; - s16 make_stairs = 0; - if(random.next()%2 == 0 && partlength >= 3) - make_stairs = random.next()%2 ? 1 : -1; - for(u32 i=0; igetId("mapgen_cobble")), 0); - make_fill(vmanip, p, v3s16(1,2,1), 0, MapNode(CONTENT_AIR), - VMANIP_FLAG_DUNGEON_INSIDE); - make_fill(vmanip, p-dir, v3s16(1,2,1), 0, MapNode(CONTENT_AIR), - VMANIP_FLAG_DUNGEON_INSIDE); - } - else - { - make_fill(vmanip, p+v3s16(-1,-1,-1), v3s16(3,4,3), - VMANIP_FLAG_DUNGEON_UNTOUCHABLE, MapNode(ndef->getId("mapgen_cobble")), 0); - make_hole1(vmanip, p, ndef); - /*make_fill(vmanip, p, v3s16(1,2,1), 0, MapNode(CONTENT_AIR), - VMANIP_FLAG_DUNGEON_INSIDE);*/ - } - p0 = p; - } - else - { - // Can't go here, turn away - dir = turn_xz(dir, random.range(0,1)); - make_stairs = -make_stairs; - partcount = 0; - partlength = random.range(1,length); - continue; - } +bool DecoSchematic::getSchematicFromMap(Map *map, v3s16 p1, v3s16 p2) { + ManualMapVoxelManipulator *vm = new ManualMapVoxelManipulator(map); - partcount++; - if(partcount >= partlength) - { - partcount = 0; + v3s16 bp1 = getNodeBlockPos(p1); + v3s16 bp2 = getNodeBlockPos(p2); + vm->initialEmerge(bp1, bp2); + + size = p2 - p1 + 1; + schematic = new MapNode[size.X * size.Y * size.Z]; + + u32 i = 0; + for (s16 z = p1.Z; z <= p2.Z; z++) + for (s16 y = p1.Y; y <= p2.Y; y++) { + u32 vi = vm->m_area.index(p1.X, y, z); + for (s16 x = p1.X; x <= p2.X; x++, i++, vi++) { + schematic[i] = vm->m_data[vi]; + schematic[i].param1 = MTSCHEM_PROB_ALWAYS; + } + } - dir = random_turn(random, dir); + delete vm; + return true; +} - partlength = random.range(1,length); - make_stairs = 0; - if(random.next()%2 == 0 && partlength >= 3) - make_stairs = random.next()%2 ? 1 : -1; +void DecoSchematic::applyProbabilities(std::vector > *plist, + v3s16 p0) { + for (size_t i = 0; i != plist->size(); i++) { + v3s16 p = (*plist)[i].first - p0; + int index = p.Z * (size.Y * size.X) + p.Y * size.X + p.X; + if (index < size.Z * size.Y * size.X) { + u8 prob = (*plist)[i].second; + schematic[index].param1 = prob; + + // trim unnecessary node names from schematic + if (prob == MTSCHEM_PROB_NEVER) + schematic[index].setContent(CONTENT_AIR); } } - result_place = p0; - result_dir = dir; } -class RoomWalker -{ -public: - RoomWalker(VoxelManipulator &vmanip_, v3s16 pos, PseudoRandom &random, - INodeDefManager *ndef): - vmanip(vmanip_), - m_pos(pos), - m_random(random), - m_ndef(ndef) - { - randomizeDir(); - } +/////////////////////////////////////////////////////////////////////////////// - void randomizeDir() - { - m_dir = rand_ortho_dir(m_random); - } - void setPos(v3s16 pos) - { - m_pos = pos; - } +Mapgen::Mapgen() { + seed = 0; + water_level = 0; + generating = false; + id = -1; + vm = NULL; + ndef = NULL; + heightmap = NULL; + biomemap = NULL; +} - void setDir(v3s16 dir) - { - m_dir = dir; - } - - bool findPlaceForDoor(v3s16 &result_place, v3s16 &result_dir) - { - for(u32 i=0; i<100; i++) - { - v3s16 p = m_pos + m_dir; - v3s16 p1 = p + v3s16(0,1,0); - if(vmanip.m_area.contains(p) == false - || vmanip.m_area.contains(p1) == false - || i % 4 == 0) - { - randomizeDir(); - continue; - } - if(vmanip.getNodeNoExNoEmerge(p).getContent() - == m_ndef->getId("mapgen_cobble") - && vmanip.getNodeNoExNoEmerge(p1).getContent() - == m_ndef->getId("mapgen_cobble")) - { - // Found wall, this is a good place! - result_place = p; - result_dir = m_dir; - // Randomize next direction - randomizeDir(); - return true; - } - /* - Determine where to move next - */ - // Jump one up if the actual space is there - if(vmanip.getNodeNoExNoEmerge(p+v3s16(0,0,0)).getContent() - == m_ndef->getId("mapgen_cobble") - && vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent() - == CONTENT_AIR - && vmanip.getNodeNoExNoEmerge(p+v3s16(0,2,0)).getContent() - == CONTENT_AIR) - p += v3s16(0,1,0); - // Jump one down if the actual space is there - if(vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent() - == m_ndef->getId("mapgen_cobble") - && vmanip.getNodeNoExNoEmerge(p+v3s16(0,0,0)).getContent() - == CONTENT_AIR - && vmanip.getNodeNoExNoEmerge(p+v3s16(0,-1,0)).getContent() - == CONTENT_AIR) - p += v3s16(0,-1,0); - // Check if walking is now possible - if(vmanip.getNodeNoExNoEmerge(p).getContent() - != CONTENT_AIR - || vmanip.getNodeNoExNoEmerge(p+v3s16(0,1,0)).getContent() - != CONTENT_AIR) - { - // Cannot continue walking here - randomizeDir(); - continue; - } - // Move there - m_pos = p; - } - return false; - } - - bool findPlaceForRoomDoor(v3s16 roomsize, v3s16 &result_doorplace, - v3s16 &result_doordir, v3s16 &result_roomplace) - { - for(s16 trycount=0; trycount<30; trycount++) - { - v3s16 doorplace; - v3s16 doordir; - bool r = findPlaceForDoor(doorplace, doordir); - if(r == false) - continue; - v3s16 roomplace; - // X east, Z north, Y up -#if 1 - if(doordir == v3s16(1,0,0)) // X+ - roomplace = doorplace + - v3s16(0,-1,m_random.range(-roomsize.Z+2,-2)); - if(doordir == v3s16(-1,0,0)) // X- - roomplace = doorplace + - v3s16(-roomsize.X+1,-1,m_random.range(-roomsize.Z+2,-2)); - if(doordir == v3s16(0,0,1)) // Z+ - roomplace = doorplace + - v3s16(m_random.range(-roomsize.X+2,-2),-1,0); - if(doordir == v3s16(0,0,-1)) // Z- - roomplace = doorplace + - v3s16(m_random.range(-roomsize.X+2,-2),-1,-roomsize.Z+1); -#endif -#if 0 - if(doordir == v3s16(1,0,0)) // X+ - roomplace = doorplace + v3s16(0,-1,-roomsize.Z/2); - if(doordir == v3s16(-1,0,0)) // X- - roomplace = doorplace + v3s16(-roomsize.X+1,-1,-roomsize.Z/2); - if(doordir == v3s16(0,0,1)) // Z+ - roomplace = doorplace + v3s16(-roomsize.X/2,-1,0); - if(doordir == v3s16(0,0,-1)) // Z- - roomplace = doorplace + v3s16(-roomsize.X/2,-1,-roomsize.Z+1); -#endif - - // Check fit - bool fits = true; - for(s16 z=1; zgetId("mapgen_torch")); - - // Quit if last room - if(i == room_count-1) +// Returns Y one under area minimum if not found +s16 Mapgen::findGroundLevelFull(v2s16 p2d) { + v3s16 em = vm->m_area.getExtent(); + s16 y_nodes_max = vm->m_area.MaxEdge.Y; + s16 y_nodes_min = vm->m_area.MinEdge.Y; + u32 i = vm->m_area.index(p2d.X, y_nodes_max, p2d.Y); + s16 y; + + for (y = y_nodes_max; y >= y_nodes_min; y--) { + MapNode &n = vm->m_data[i]; + if (ndef->get(n).walkable) break; - // Determine walker start position - - bool start_in_last_room = (random.range(0,2)!=0); - //bool start_in_last_room = true; - - v3s16 walker_start_place; - - if(start_in_last_room) - { - walker_start_place = last_room_center; - } - else - { - walker_start_place = room_center; - // Store center of current room as the last one - last_room_center = room_center; - } - - // Create walker and find a place for a door - RoomWalker walker(vmanip, walker_start_place, random, ndef); - v3s16 doorplace; - v3s16 doordir; - bool r = walker.findPlaceForDoor(doorplace, doordir); - if(r == false) - return; - - if(random.range(0,1)==0) - // Make the door - make_door1(vmanip, doorplace, doordir, ndef); - else - // Don't actually make a door - doorplace -= doordir; - - // Make a random corridor starting from the door - v3s16 corridor_end; - v3s16 corridor_end_dir; - make_corridor(vmanip, doorplace, doordir, corridor_end, - corridor_end_dir, random, ndef); - - // Find a place for a random sized room - roomsize = v3s16(random.range(4,8),random.range(4,6),random.range(4,8)); - walker.setPos(corridor_end); - walker.setDir(corridor_end_dir); - r = walker.findPlaceForRoomDoor(roomsize, doorplace, doordir, roomplace); - if(r == false) - return; - - if(random.range(0,1)==0) - // Make the door - make_door1(vmanip, doorplace, doordir, ndef); - else - // Don't actually make a door - roomplace -= doordir; - - } -} -#endif - -#if 0 -static void make_nc(VoxelManipulator &vmanip, PseudoRandom &random, - INodeDefManager *ndef) -{ - v3s16 dir; - u8 facedir_i = 0; - s32 r = random.range(0, 3); - if(r == 0){ - dir = v3s16( 1, 0, 0); - facedir_i = 3; - } - if(r == 1){ - dir = v3s16(-1, 0, 0); - facedir_i = 1; - } - if(r == 2){ - dir = v3s16( 0, 0, 1); - facedir_i = 2; - } - if(r == 3){ - dir = v3s16( 0, 0,-1); - facedir_i = 0; - } - v3s16 p = vmanip.m_area.MinEdge + v3s16( - 16+random.range(0,15), - 16+random.range(0,15), - 16+random.range(0,15)); - vmanip.m_data[vmanip.m_area.index(p)] = MapNode(ndef->getId("mapgen_nyancat"), facedir_i); - u32 length = random.range(3,15); - for(u32 j=0; jgetId("mapgen_nyancat_rainbow")); - } -} -#endif - -/* - Noise functions. Make sure seed is mangled differently in each one. -*/ - -#if 0 -/* - Scaling the output of the noise function affects the overdrive of the - contour function, which affects the shape of the output considerably. -*/ -#define CAVE_NOISE_SCALE 12.0 -//#define CAVE_NOISE_SCALE 10.0 -//#define CAVE_NOISE_SCALE 7.5 -//#define CAVE_NOISE_SCALE 5.0 -//#define CAVE_NOISE_SCALE 1.0 - -//#define CAVE_NOISE_THRESHOLD (2.5/CAVE_NOISE_SCALE) -#define CAVE_NOISE_THRESHOLD (1.5/CAVE_NOISE_SCALE) - -NoiseParams get_cave_noise1_params(u64 seed) -{ - /*return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 5, 0.7, - 200, CAVE_NOISE_SCALE);*/ - /*return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 4, 0.7, - 100, CAVE_NOISE_SCALE);*/ - /*return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 5, 0.6, - 100, CAVE_NOISE_SCALE);*/ - /*return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 5, 0.3, - 100, CAVE_NOISE_SCALE);*/ - return NoiseParams(NOISE_PERLIN_CONTOUR, seed+52534, 4, 0.5, - 50, CAVE_NOISE_SCALE); - //return NoiseParams(NOISE_CONSTANT_ONE); -} - -NoiseParams get_cave_noise2_params(u64 seed) -{ - /*return NoiseParams(NOISE_PERLIN_CONTOUR_FLIP_YZ, seed+10325, 5, 0.7, - 200, CAVE_NOISE_SCALE);*/ - /*return NoiseParams(NOISE_PERLIN_CONTOUR_FLIP_YZ, seed+10325, 4, 0.7, - 100, CAVE_NOISE_SCALE);*/ - /*return NoiseParams(NOISE_PERLIN_CONTOUR_FLIP_YZ, seed+10325, 5, 0.3, - 100, CAVE_NOISE_SCALE);*/ - return NoiseParams(NOISE_PERLIN_CONTOUR_FLIP_YZ, seed+10325, 4, 0.5, - 50, CAVE_NOISE_SCALE); - //return NoiseParams(NOISE_CONSTANT_ONE); -} - -NoiseParams get_ground_noise1_params(u64 seed) -{ - return NoiseParams(NOISE_PERLIN, seed+983240, 4, - 0.55, 80.0, 40.0); -} - -NoiseParams get_ground_crumbleness_params(u64 seed) -{ - return NoiseParams(NOISE_PERLIN, seed+34413, 3, - 1.3, 20.0, 1.0); -} - -NoiseParams get_ground_wetness_params(u64 seed) -{ - return NoiseParams(NOISE_PERLIN, seed+32474, 4, - 1.1, 40.0, 1.0); -} - -bool is_cave(u64 seed, v3s16 p) -{ - double d1 = noise3d_param(get_cave_noise1_params(seed), p.X,p.Y,p.Z); - double d2 = noise3d_param(get_cave_noise2_params(seed), p.X,p.Y,p.Z); - return d1*d2 > CAVE_NOISE_THRESHOLD; -} - -/* - Ground density noise shall be interpreted by using this. - - TODO: No perlin noises here, they should be outsourced - and buffered - NOTE: The speed of these actually isn't terrible -*/ -bool val_is_ground(double ground_noise1_val, v3s16 p, u64 seed) -{ - //return ((double)p.Y < ground_noise1_val); - - double f = 0.55 + noise2d_perlin( - 0.5+(float)p.X/250, 0.5+(float)p.Z/250, - seed+920381, 3, 0.45); - if(f < 0.01) - f = 0.01; - else if(f >= 1.0) - f *= 1.6; - double h = WATER_LEVEL + 10 * noise2d_perlin( - 0.5+(float)p.X/250, 0.5+(float)p.Z/250, - seed+84174, 4, 0.5); - /*double f = 1; - double h = 0;*/ - return ((double)p.Y - h < ground_noise1_val * f); -} - -/* - Queries whether a position is ground or not. -*/ -bool is_ground(u64 seed, v3s16 p) -{ - double val1 = noise3d_param(get_ground_noise1_params(seed), p.X,p.Y,p.Z); - return val_is_ground(val1, p, seed); -} -#endif - -// Amount of trees per area in nodes -double tree_amount_2d(u64 seed, v2s16 p) -{ - /*double noise = noise2d_perlin( - 0.5+(float)p.X/250, 0.5+(float)p.Y/250, - seed+2, 5, 0.66);*/ - double noise = noise2d_perlin( - 0.5+(float)p.X/125, 0.5+(float)p.Y/125, - seed+2, 4, 0.66); - double zeroval = -0.39; - if(noise < zeroval) - return 0; - else - return 0.04 * (noise-zeroval) / (1.0-zeroval); -} - -#if 0 -double surface_humidity_2d(u64 seed, v2s16 p) -{ - double noise = noise2d_perlin( - 0.5+(float)p.X/500, 0.5+(float)p.Y/500, - seed+72384, 4, 0.66); - noise = (noise + 1.0)/2.0; - if(noise < 0.0) - noise = 0.0; - if(noise > 1.0) - noise = 1.0; - return noise; -} - -/* - Incrementally find ground level from 3d noise -*/ -s16 find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision) -{ - // Start a bit fuzzy to make averaging lower precision values - // more useful - s16 level = myrand_range(-precision/2, precision/2); - s16 dec[] = {31000, 100, 20, 4, 1, 0}; - s16 i; - for(i = 1; dec[i] != 0 && precision <= dec[i]; i++) - { - // First find non-ground by going upwards - // Don't stop in caves. - { - s16 max = level+dec[i-1]*2; - v3s16 p(p2d.X, level, p2d.Y); - for(; p.Y < max; p.Y += dec[i]) - { - if(!is_ground(seed, p)) - { - level = p.Y; - break; - } - } - } - // Then find ground by going downwards from there. - // Go in caves, too, when precision is 1. - { - s16 min = level-dec[i-1]*2; - v3s16 p(p2d.X, level, p2d.Y); - for(; p.Y>min; p.Y-=dec[i]) - { - bool ground = is_ground(seed, p); - /*if(dec[i] == 1 && is_cave(seed, p)) - ground = false;*/ - if(ground) - { - level = p.Y; - break; - } - } - } + vm->m_area.add_y(em, i, -1); } - - // This is more like the actual ground level - level += dec[i-1]/2; - - return level; -} - -double get_sector_average_ground_level(u64 seed, v2s16 sectorpos, double p=4); - -double get_sector_average_ground_level(u64 seed, v2s16 sectorpos, double p) -{ - v2s16 node_min = sectorpos*MAP_BLOCKSIZE; - v2s16 node_max = (sectorpos+v2s16(1,1))*MAP_BLOCKSIZE-v2s16(1,1); - double a = 0; - a += find_ground_level_from_noise(seed, - v2s16(node_min.X, node_min.Y), p); - a += find_ground_level_from_noise(seed, - v2s16(node_min.X, node_max.Y), p); - a += find_ground_level_from_noise(seed, - v2s16(node_max.X, node_max.Y), p); - a += find_ground_level_from_noise(seed, - v2s16(node_max.X, node_min.Y), p); - a += find_ground_level_from_noise(seed, - v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y+MAP_BLOCKSIZE/2), p); - a /= 5; - return a; -} - -double get_sector_maximum_ground_level(u64 seed, v2s16 sectorpos, double p=4); - -double get_sector_maximum_ground_level(u64 seed, v2s16 sectorpos, double p) -{ - v2s16 node_min = sectorpos*MAP_BLOCKSIZE; - v2s16 node_max = (sectorpos+v2s16(1,1))*MAP_BLOCKSIZE-v2s16(1,1); - double a = -31000; - // Corners - a = MYMAX(a, find_ground_level_from_noise(seed, - v2s16(node_min.X, node_min.Y), p)); - a = MYMAX(a, find_ground_level_from_noise(seed, - v2s16(node_min.X, node_max.Y), p)); - a = MYMAX(a, find_ground_level_from_noise(seed, - v2s16(node_max.X, node_max.Y), p)); - a = MYMAX(a, find_ground_level_from_noise(seed, - v2s16(node_min.X, node_min.Y), p)); - // Center - a = MYMAX(a, find_ground_level_from_noise(seed, - v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y+MAP_BLOCKSIZE/2), p)); - // Side middle points - a = MYMAX(a, find_ground_level_from_noise(seed, - v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y), p)); - a = MYMAX(a, find_ground_level_from_noise(seed, - v2s16(node_min.X+MAP_BLOCKSIZE/2, node_max.Y), p)); - a = MYMAX(a, find_ground_level_from_noise(seed, - v2s16(node_min.X, node_min.Y+MAP_BLOCKSIZE/2), p)); - a = MYMAX(a, find_ground_level_from_noise(seed, - v2s16(node_max.X, node_min.Y+MAP_BLOCKSIZE/2), p)); - return a; + return (y >= y_nodes_min) ? y : y_nodes_min - 1; } -double get_sector_minimum_ground_level(u64 seed, v2s16 sectorpos, double p=4); - -double get_sector_minimum_ground_level(u64 seed, v2s16 sectorpos, double p) -{ - v2s16 node_min = sectorpos*MAP_BLOCKSIZE; - v2s16 node_max = (sectorpos+v2s16(1,1))*MAP_BLOCKSIZE-v2s16(1,1); - double a = 31000; - // Corners - a = MYMIN(a, find_ground_level_from_noise(seed, - v2s16(node_min.X, node_min.Y), p)); - a = MYMIN(a, find_ground_level_from_noise(seed, - v2s16(node_min.X, node_max.Y), p)); - a = MYMIN(a, find_ground_level_from_noise(seed, - v2s16(node_max.X, node_max.Y), p)); - a = MYMIN(a, find_ground_level_from_noise(seed, - v2s16(node_min.X, node_min.Y), p)); - // Center - a = MYMIN(a, find_ground_level_from_noise(seed, - v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y+MAP_BLOCKSIZE/2), p)); - // Side middle points - a = MYMIN(a, find_ground_level_from_noise(seed, - v2s16(node_min.X+MAP_BLOCKSIZE/2, node_min.Y), p)); - a = MYMIN(a, find_ground_level_from_noise(seed, - v2s16(node_min.X+MAP_BLOCKSIZE/2, node_max.Y), p)); - a = MYMIN(a, find_ground_level_from_noise(seed, - v2s16(node_min.X, node_min.Y+MAP_BLOCKSIZE/2), p)); - a = MYMIN(a, find_ground_level_from_noise(seed, - v2s16(node_max.X, node_min.Y+MAP_BLOCKSIZE/2), p)); - return a; -} -#endif -// Required by mapgen.h -bool block_is_underground(u64 seed, v3s16 blockpos) -{ - /*s16 minimum_groundlevel = (s16)get_sector_minimum_ground_level( - seed, v2s16(blockpos.X, blockpos.Z));*/ - // Nah, this is just a heuristic, just return something - s16 minimum_groundlevel = WATER_LEVEL; - - if(blockpos.Y*MAP_BLOCKSIZE + MAP_BLOCKSIZE <= minimum_groundlevel) - return true; - else - return false; -} +s16 Mapgen::findGroundLevel(v2s16 p2d, s16 ymin, s16 ymax) { + v3s16 em = vm->m_area.getExtent(); + u32 i = vm->m_area.index(p2d.X, ymax, p2d.Y); + s16 y; + + for (y = ymax; y >= ymin; y--) { + MapNode &n = vm->m_data[i]; + if (ndef->get(n).walkable) + break; -#define AVERAGE_MUD_AMOUNT 4 - -double base_rock_level_2d(u64 seed, v2s16 p) -{ - // The base ground level - double base = (double)WATER_LEVEL - (double)AVERAGE_MUD_AMOUNT - + 20. * noise2d_perlin( - 0.5+(float)p.X/250., 0.5+(float)p.Y/250., - seed+82341, 5, 0.6); - - /*// A bit hillier one - double base2 = WATER_LEVEL - 4.0 + 40. * noise2d_perlin( - 0.5+(float)p.X/250., 0.5+(float)p.Y/250., - seed+93413, 6, 0.69); - if(base2 > base) - base = base2;*/ -#if 1 - // Higher ground level - double higher = (double)WATER_LEVEL + 20. + 16. * noise2d_perlin( - 0.5+(float)p.X/500., 0.5+(float)p.Y/500., - seed+85039, 5, 0.6); - //higher = 30; // For debugging - - // Limit higher to at least base - if(higher < base) - higher = base; - - // Steepness factor of cliffs - double b = 0.85 + 0.5 * noise2d_perlin( - 0.5+(float)p.X/125., 0.5+(float)p.Y/125., - seed-932, 5, 0.7); - b = rangelim(b, 0.0, 1000.0); - b = b*b*b*b*b*b*b; - b *= 5; - b = rangelim(b, 0.5, 1000.0); - // Values 1.5...100 give quite horrible looking slopes - if(b > 1.5 && b < 100.0){ - if(b < 10.0) - b = 1.5; - else - b = 100.0; + vm->m_area.add_y(em, i, -1); } - //dstream<<"b="<vmanip); - assert(data->nodedef); - assert(data->blockpos_requested.X >= data->blockpos_min.X && - data->blockpos_requested.Y >= data->blockpos_min.Y && - data->blockpos_requested.Z >= data->blockpos_min.Z); - assert(data->blockpos_requested.X <= data->blockpos_max.X && - data->blockpos_requested.Y <= data->blockpos_max.Y && - data->blockpos_requested.Z <= data->blockpos_max.Z); - - INodeDefManager *ndef = data->nodedef; - - // Hack: use minimum block coordinates for old code that assumes - // a single block - v3s16 blockpos = data->blockpos_requested; - - /*dstream<<"makeBlock(): ("<blockpos_min; - v3s16 blockpos_max = data->blockpos_max; - v3s16 blockpos_full_min = blockpos_min - v3s16(1,1,1); - v3s16 blockpos_full_max = blockpos_max + v3s16(1,1,1); - - ManualMapVoxelManipulator &vmanip = *(data->vmanip); - // Area of central chunk - v3s16 node_min = blockpos_min*MAP_BLOCKSIZE; - v3s16 node_max = (blockpos_max+v3s16(1,1,1))*MAP_BLOCKSIZE-v3s16(1,1,1); - // Full allocated area - v3s16 full_node_min = (blockpos_min-1)*MAP_BLOCKSIZE; - v3s16 full_node_max = (blockpos_max+2)*MAP_BLOCKSIZE-v3s16(1,1,1); - - v3s16 central_area_size = node_max - node_min + v3s16(1,1,1); - - const s16 max_spread_amount = MAP_BLOCKSIZE; - - int volume_blocks = (blockpos_max.X - blockpos_min.X + 1) - * (blockpos_max.Y - blockpos_min.Y + 1) - * (blockpos_max.Z - blockpos_max.Z + 1); - - int volume_nodes = volume_blocks * - MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; - - // Generated surface area - //double gen_area_nodes = MAP_BLOCKSIZE*MAP_BLOCKSIZE * rel_volume; - - // Horribly wrong heuristic, but better than nothing - bool block_is_underground = (WATER_LEVEL > node_max.Y); - - /* - Create a block-specific seed - */ - u32 blockseed = get_blockseed(data->seed, full_node_min); - - /* - Cache some ground type values for speed - */ - -// Creates variables c_name=id and n_name=node -#define CONTENT_VARIABLE(ndef, name)\ - content_t c_##name = ndef->getId("mapgen_" #name);\ - MapNode n_##name(c_##name); -// Default to something else if was CONTENT_IGNORE -#define CONTENT_VARIABLE_FALLBACK(name, dname)\ - if(c_##name == CONTENT_IGNORE){\ - c_##name = c_##dname;\ - n_##name = n_##dname;\ - } - - CONTENT_VARIABLE(ndef, stone); - CONTENT_VARIABLE(ndef, air); - CONTENT_VARIABLE(ndef, water_source); - CONTENT_VARIABLE(ndef, dirt); - CONTENT_VARIABLE(ndef, sand); - CONTENT_VARIABLE(ndef, gravel); - CONTENT_VARIABLE(ndef, clay); - CONTENT_VARIABLE(ndef, lava_source); - CONTENT_VARIABLE(ndef, cobble); - CONTENT_VARIABLE(ndef, mossycobble); - CONTENT_VARIABLE(ndef, dirt_with_grass); - CONTENT_VARIABLE(ndef, junglegrass); - CONTENT_VARIABLE(ndef, stone_with_coal); - CONTENT_VARIABLE(ndef, stone_with_iron); - CONTENT_VARIABLE(ndef, mese); - CONTENT_VARIABLE(ndef, desert_sand); - CONTENT_VARIABLE_FALLBACK(desert_sand, sand); - CONTENT_VARIABLE(ndef, desert_stone); - CONTENT_VARIABLE_FALLBACK(desert_stone, stone); - - // Maximum height of the stone surface and obstacles. - // This is used to guide the cave generation - s16 stone_surface_max_y = 0; - - /* - Generate general ground level to full area - */ - { -#if 1 - TimeTaker timer1("Generating ground level"); - - for(s16 x=node_min.X; x<=node_max.X; x++) - for(s16 z=node_min.Z; z<=node_max.Z; z++) - { - // Node position - v2s16 p2d = v2s16(x,z); - - /* - Skip of already generated - */ - /*{ - v3s16 p(p2d.X, node_min.Y, p2d.Y); - if(vmanip.m_data[vmanip.m_area.index(p)].d != CONTENT_AIR) + + //TimeTaker t("Mapgen::updateHeightmap", NULL, PRECISION_MICRO); + int index = 0; + for (s16 z = nmin.Z; z <= nmax.Z; z++) { + for (s16 x = nmin.X; x <= nmax.X; x++, index++) { + s16 y = findGroundLevel(v2s16(x, z), nmin.Y, nmax.Y); + + // if the values found are out of range, trust the old heightmap + if (y == nmax.Y && heightmap[index] > nmax.Y) continue; - }*/ - - // Ground height at this point - float surface_y_f = 0.0; - - // Use perlin noise for ground height - surface_y_f = base_rock_level_2d(data->seed, p2d); - - /*// Experimental stuff - { - float a = highlands_level_2d(data->seed, p2d); - if(a > surface_y_f) - surface_y_f = a; - }*/ - - // Convert to integer - s16 surface_y = (s16)surface_y_f; - - // Log it - if(surface_y > stone_surface_max_y) - stone_surface_max_y = surface_y; - - BiomeType bt = get_biome(data->seed, p2d); - /* - Fill ground with stone - */ - { - // Use fast index incrementing - v3s16 em = vmanip.m_area.getExtent(); - u32 i = vmanip.m_area.index(v3s16(p2d.X, node_min.Y, p2d.Y)); - for(s16 y=node_min.Y; y<=node_max.Y; y++) - { - if(vmanip.m_data[i].getContent() == CONTENT_IGNORE){ - if(y <= surface_y){ - if(y > WATER_LEVEL && bt == BT_DESERT) - vmanip.m_data[i] = n_desert_stone; - else - vmanip.m_data[i] = n_stone; - } else if(y <= WATER_LEVEL){ - vmanip.m_data[i] = MapNode(c_water_source); - } else { - vmanip.m_data[i] = MapNode(c_air); - } - } - vmanip.m_area.add_y(em, i, 1); - } + if (y == nmin.Y - 1 && heightmap[index] < nmin.Y) + continue; + + heightmap[index] = y; } } -#endif - - }//timer1 - - // Limit dirt flow area by 1 because mud is flown into neighbors. - assert(central_area_size.X == central_area_size.Z); - s16 mudflow_minpos = 0-max_spread_amount+1; - s16 mudflow_maxpos = central_area_size.X+max_spread_amount-2; - - /* - Loop this part, it will make stuff look older and newer nicely - */ - - const u32 age_loops = 2; - for(u32 i_age=0; i_ageseed+34329, 3, 0.50); - cave_amount = MYMAX(0.0, cave_amount); - u32 caves_count = cave_amount * volume_nodes / 50000; - u32 bruises_count = 1; - PseudoRandom ps(blockseed+21343); - PseudoRandom ps2(blockseed+1032); - if(ps.range(1, 6) == 1) - bruises_count = ps.range(0, ps.range(0, 2)); - if(get_biome(data->seed, v2s16(node_min.X, node_min.Y)) == BT_DESERT){ - caves_count /= 3; - bruises_count /= 3; - } - for(u32 jj=0; jj= caves_count); - s16 min_tunnel_diameter = 2; - s16 max_tunnel_diameter = ps.range(2,6); - int dswitchint = ps.range(1,14); - u16 tunnel_routepoints = 0; - int part_max_length_rs = 0; - if(large_cave){ - part_max_length_rs = ps.range(2,4); - tunnel_routepoints = ps.range(5, ps.range(15,30)); - min_tunnel_diameter = 5; - max_tunnel_diameter = ps.range(7, ps.range(8,24)); - } else { - part_max_length_rs = ps.range(2,9); - tunnel_routepoints = ps.range(10, ps.range(15,30)); - } - bool large_cave_is_flat = (ps.range(0,1) == 0); - - v3f main_direction(0,0,0); - - // Allowed route area size in nodes - v3s16 ar = central_area_size; - - // Area starting point in nodes - v3s16 of = node_min; - - // Allow a bit more - //(this should be more than the maximum radius of the tunnel) - //s16 insure = 5; // Didn't work with max_d = 20 - s16 insure = 10; - s16 more = max_spread_amount - max_tunnel_diameter/2 - insure; - ar += v3s16(1,0,1) * more * 2; - of -= v3s16(1,0,1) * more; - - s16 route_y_min = 0; - // Allow half a diameter + 7 over stone surface - s16 route_y_max = -of.Y + stone_surface_max_y + max_tunnel_diameter/2 + 7; - - /*// If caves, don't go through surface too often - if(large_cave == false) - route_y_max -= ps.range(0, max_tunnel_diameter*2);*/ - - // Limit maximum to area - route_y_max = rangelim(route_y_max, 0, ar.Y-1); - - if(large_cave) - { - /*// Minimum is at y=0 - route_y_min = -of.Y - 0;*/ - // Minimum is at y=max_tunnel_diameter/4 - //route_y_min = -of.Y + max_tunnel_diameter/4; - //s16 min = -of.Y + max_tunnel_diameter/4; - //s16 min = -of.Y + 0; - s16 min = 0; - if(node_min.Y < WATER_LEVEL && node_max.Y > WATER_LEVEL) - { - min = WATER_LEVEL - max_tunnel_diameter/3 - of.Y; - route_y_max = WATER_LEVEL + max_tunnel_diameter/3 - of.Y; - } - route_y_min = ps.range(min, min + max_tunnel_diameter); - route_y_min = rangelim(route_y_min, 0, route_y_max); - } - - /*dstream<<"route_y_min = "<= 0){ - coming_from_surface = (jj % 4 == 0 && large_cave == false); - if(coming_from_surface) - route_start_y_min = -of.Y + stone_surface_max_y + 10; - }*/ - route_start_y_min = rangelim(route_start_y_min, 0, ar.Y-1); - route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y-1); +void Mapgen::updateLiquid(UniqueQueue *trans_liquid, v3s16 nmin, v3s16 nmax) { + bool isliquid, wasliquid, rare; + v3s16 em = vm->m_area.getExtent(); + rare = g_settings->getBool("liquid_finite"); + int rarecnt = 0; - // Randomize starting position - v3f orp( - (float)(ps.next()%ar.X)+0.5, - (float)(ps.range(route_start_y_min, route_start_y_max))+0.5, - (float)(ps.next()%ar.Z)+0.5 - ); + for (s16 z = nmin.Z; z <= nmax.Z; z++) { + for (s16 x = nmin.X; x <= nmax.X; x++) { + wasliquid = true; - v3s16 startp(orp.X, orp.Y, orp.Z); - startp += of; - - MapNode airnode(CONTENT_AIR); - MapNode waternode(c_water_source); - MapNode lavanode(c_lava_source); - - /* - Generate some tunnel starting from orp - */ - - for(u16 j=0; jm_area.index(x, nmax.Y, z); + for (s16 y = nmax.Y; y >= nmin.Y; y--) { + isliquid = ndef->get(vm->m_data[i]).isLiquid(); - // Randomize size - s16 min_d = min_tunnel_diameter; - s16 max_d = max_tunnel_diameter; - s16 rs = ps.range(min_d, max_d); - - // Every second section is rough - bool randomize_xz = (ps2.range(1,2) == 1); - - v3s16 maxlen; - if(large_cave) - { - maxlen = v3s16( - rs*part_max_length_rs, - rs*part_max_length_rs/2, - rs*part_max_length_rs - ); - } - else - { - maxlen = v3s16( - rs*part_max_length_rs, - ps.range(1, rs*part_max_length_rs), - rs*part_max_length_rs - ); - } + // there was a change between liquid and nonliquid, add to queue. no need to add every with liquid_finite + if (isliquid != wasliquid && (!rare || !(rarecnt++ % 36))) + trans_liquid->push_back(v3s16(x, y, z)); - v3f vec; - - vec = v3f( - (float)(ps.next()%(maxlen.X*1))-(float)maxlen.X/2, - (float)(ps.next()%(maxlen.Y*1))-(float)maxlen.Y/2, - (float)(ps.next()%(maxlen.Z*1))-(float)maxlen.Z/2 - ); - - // Jump downward sometimes - if(!large_cave && ps.range(0,12) == 0) - { - vec = v3f( - (float)(ps.next()%(maxlen.X*1))-(float)maxlen.X/2, - (float)(ps.next()%(maxlen.Y*2))-(float)maxlen.Y*2/2, - (float)(ps.next()%(maxlen.Z*1))-(float)maxlen.Z/2 - ); + wasliquid = isliquid; + vm->m_area.add_y(em, i, -1); } - - /*if(large_cave){ - v3f p = orp + vec; - s16 h = find_ground_level_clever(vmanip, - v2s16(p.X, p.Z), ndef); - route_y_min = h - rs/3; - route_y_max = h + rs; - }*/ - - vec += main_direction; - - v3f rp = orp + vec; - if(rp.X < 0) - rp.X = 0; - else if(rp.X >= ar.X) - rp.X = ar.X-1; - if(rp.Y < route_y_min) - rp.Y = route_y_min; - else if(rp.Y >= route_y_max) - rp.Y = route_y_max-1; - if(rp.Z < 0) - rp.Z = 0; - else if(rp.Z >= ar.Z) - rp.Z = ar.Z-1; - vec = rp - orp; - - for(float f=0; f<1.0; f+=1.0/vec.getLength()) - { - v3f fp = orp + vec * f; - fp.X += 0.1*ps.range(-10,10); - fp.Z += 0.1*ps.range(-10,10); - v3s16 cp(fp.X, fp.Y, fp.Z); - - s16 d0 = -rs/2; - s16 d1 = d0 + rs; - if(randomize_xz){ - d0 += ps.range(-1,1); - d1 += ps.range(-1,1); - } - for(s16 z0=d0; z0<=d1; z0++) - { - s16 si = rs/2 - MYMAX(0, abs(z0)-rs/7-1); - for(s16 x0=-si-ps.range(0,1); x0<=si-1+ps.range(0,1); x0++) - { - s16 maxabsxz = MYMAX(abs(x0), abs(z0)); - s16 si2 = rs/2 - MYMAX(0, maxabsxz-rs/7-1); - for(s16 y0=-si2; y0<=si2; y0++) - { - /*// Make better floors in small caves - if(y0 <= -rs/2 && rs<=7) - continue;*/ - if(large_cave_is_flat){ - // Make large caves not so tall - if(rs > 7 && abs(y0) >= rs/3) - continue; - } - - s16 z = cp.Z + z0; - s16 y = cp.Y + y0; - s16 x = cp.X + x0; - v3s16 p(x,y,z); - p += of; - - if(vmanip.m_area.contains(p) == false) - continue; - - u32 i = vmanip.m_area.index(p); - - if(large_cave) - { - if(full_node_min.Y < WATER_LEVEL && - full_node_max.Y > WATER_LEVEL){ - if(p.Y <= WATER_LEVEL) - vmanip.m_data[i] = waternode; - else - vmanip.m_data[i] = airnode; - } else if(full_node_max.Y < WATER_LEVEL){ - if(p.Y < startp.Y - 2) - vmanip.m_data[i] = lavanode; - else - vmanip.m_data[i] = airnode; - } else { - vmanip.m_data[i] = airnode; - } - } else { - // Don't replace air or water or lava or ignore - if(vmanip.m_data[i].getContent() == CONTENT_IGNORE || - vmanip.m_data[i].getContent() == CONTENT_AIR || - vmanip.m_data[i].getContent() == c_water_source || - vmanip.m_data[i].getContent() == c_lava_source) - continue; - - vmanip.m_data[i] = airnode; - - // Set tunnel flag - vmanip.m_flags[i] |= VMANIP_FLAG_CAVE; - } - } - } - } - } - - orp = rp; } - } +} - }//timer1 -#endif - -#if 1 - { - // 15ms @cs=8 - TimeTaker timer1("add mud"); - - /* - Add mud to the central chunk - */ - - for(s16 x=node_min.X; x<=node_max.X; x++) - for(s16 z=node_min.Z; z<=node_max.Z; z++) - { - // Node position in 2d - v2s16 p2d = v2s16(x,z); - - // Randomize mud amount - s16 mud_add_amount = get_mud_add_amount(data->seed, p2d) / 2.0 + 0.5; - - // Find ground level - s16 surface_y = find_stone_level(vmanip, p2d, ndef); - // Handle area not found - if(surface_y == vmanip.m_area.MinEdge.Y - 1) - continue; - - MapNode addnode(c_dirt); - BiomeType bt = get_biome(data->seed, p2d); - - if(bt == BT_DESERT) - addnode = MapNode(c_desert_sand); - - if(bt == BT_DESERT && surface_y + mud_add_amount <= WATER_LEVEL+1){ - addnode = MapNode(c_sand); - } else if(mud_add_amount <= 0){ - mud_add_amount = 1 - mud_add_amount; - addnode = MapNode(c_gravel); - } else if(bt == BT_NORMAL && get_have_beach(data->seed, p2d) && - surface_y + mud_add_amount <= WATER_LEVEL+2){ - addnode = MapNode(c_sand); - } - - if(bt == BT_DESERT){ - if(surface_y > 20){ - mud_add_amount = MYMAX(0, mud_add_amount - (surface_y - 20)/5); - } - } - - /* - If topmost node is grass, change it to mud. - It might be if it was flown to there from a neighboring - chunk and then converted. - */ - { - u32 i = vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y)); - MapNode *n = &vmanip.m_data[i]; - if(n->getContent() == c_dirt_with_grass) - *n = MapNode(c_dirt); - } - - /* - Add mud on ground - */ - { - s16 mudcount = 0; - v3s16 em = vmanip.m_area.getExtent(); - s16 y_start = surface_y+1; - u32 i = vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y)); - for(s16 y=y_start; y<=node_max.Y; y++) - { - if(mudcount >= mud_add_amount) - break; - MapNode &n = vmanip.m_data[i]; - n = addnode; - mudcount++; +void Mapgen::setLighting(v3s16 nmin, v3s16 nmax, u8 light) { + ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG); + VoxelArea a(nmin, nmax); - vmanip.m_area.add_y(em, i, 1); - } + for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) { + for (int y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) { + u32 i = vm->m_area.index(a.MinEdge.X, y, z); + for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++, i++) + vm->m_data[i].param1 = light; } - - } - - }//timer1 -#endif - - /* - Add blobs of dirt and gravel underground - */ - if(get_biome(data->seed, v2s16(node_min.X, node_min.Y)) == BT_NORMAL) - { - PseudoRandom pr(blockseed+983); - for(int i=0; i -32 && pr.range(0,1) == 0) - n1 = MapNode(c_dirt); - else - n1 = MapNode(c_gravel); - for(int x1=0; x1m_area.index(p); + MapNode &nn = vm->m_data[vi]; - for(s16 x=mudflow_minpos; x<=mudflow_maxpos; x++) - for(s16 z=mudflow_minpos; z<=mudflow_maxpos; z++) - { - // Invert coordinates every 2nd iteration - if(k%2 == 0) - { - x = mudflow_maxpos - (x-mudflow_minpos); - z = mudflow_maxpos - (z-mudflow_minpos); - } + light--; + // should probably compare masked, but doesn't seem to make a difference + if (light <= nn.param1 || !ndef->get(nn).light_propagates) + return; - // Node position in 2d - v2s16 p2d = v2s16(node_min.X, node_min.Z) + v2s16(x,z); - - v3s16 em = vmanip.m_area.getExtent(); - u32 i = vmanip.m_area.index(v3s16(p2d.X, node_max.Y, p2d.Y)); - s16 y=node_max.Y; - - while(y >= node_min.Y) - { - - for(;; y--) - { - MapNode *n = NULL; - // Find mud - for(; y>=node_min.Y; y--) - { - n = &vmanip.m_data[i]; - //if(content_walkable(n->d)) - // break; - if(n->getContent() == c_dirt || - n->getContent() == c_dirt_with_grass || - n->getContent() == c_gravel) - break; + nn.param1 = light; - vmanip.m_area.add_y(em, i, -1); - } + lightSpread(a, p + v3s16(0, 0, 1), light); + lightSpread(a, p + v3s16(0, 1, 0), light); + lightSpread(a, p + v3s16(1, 0, 0), light); + lightSpread(a, p - v3s16(0, 0, 1), light); + lightSpread(a, p - v3s16(0, 1, 0), light); + lightSpread(a, p - v3s16(1, 0, 0), light); +} - // Stop if out of area - //if(vmanip.m_area.contains(i) == false) - if(y < node_min.Y) - break; - - /*// If not mud, do nothing to it - MapNode *n = &vmanip.m_data[i]; - if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS) - continue;*/ - - if(n->getContent() == c_dirt || - n->getContent() == c_dirt_with_grass) - { - // Make it exactly mud - n->setContent(c_dirt); - - /* - Don't flow it if the stuff under it is not mud - */ - { - u32 i2 = i; - vmanip.m_area.add_y(em, i2, -1); - // Cancel if out of area - if(vmanip.m_area.contains(i2) == false) - continue; - MapNode *n2 = &vmanip.m_data[i2]; - if(n2->getContent() != c_dirt && - n2->getContent() != c_dirt_with_grass) - continue; - } - } - /*s16 recurse_count = 0; - mudflow_recurse:*/ - - v3s16 dirs4[4] = { - v3s16(0,0,1), // back - v3s16(1,0,0), // right - v3s16(0,0,-1), // front - v3s16(-1,0,0), // left - }; - - // Theck that upper is air or doesn't exist. - // Cancel dropping if upper keeps it in place - u32 i3 = i; - vmanip.m_area.add_y(em, i3, 1); - if(vmanip.m_area.contains(i3) == true - && ndef->get(vmanip.m_data[i3]).walkable) - { - continue; - } +void Mapgen::calcLighting(v3s16 nmin, v3s16 nmax) { + VoxelArea a(nmin, nmax); + bool block_is_underground = (water_level >= nmax.Y); - // Drop mud on side + ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG); + //TimeTaker t("updateLighting"); - for(u32 di=0; di<4; di++) - { - v3s16 dirp = dirs4[di]; - u32 i2 = i; - // Move to side - vmanip.m_area.add_p(em, i2, dirp); - // Fail if out of area - if(vmanip.m_area.contains(i2) == false) - continue; - // Check that side is air - MapNode *n2 = &vmanip.m_data[i2]; - if(ndef->get(*n2).walkable) - continue; - // Check that under side is air - vmanip.m_area.add_y(em, i2, -1); - if(vmanip.m_area.contains(i2) == false) - continue; - n2 = &vmanip.m_data[i2]; - if(ndef->get(*n2).walkable) - continue; - /*// Check that under that is air (need a drop of 2) - vmanip.m_area.add_y(em, i2, -1); - if(vmanip.m_area.contains(i2) == false) + // first, send vertical rays of sunshine downward + v3s16 em = vm->m_area.getExtent(); + for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) { + for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++) { + // see if we can get a light value from the overtop + u32 i = vm->m_area.index(x, a.MaxEdge.Y + 1, z); + if (vm->m_data[i].getContent() == CONTENT_IGNORE) { + if (block_is_underground) continue; - n2 = &vmanip.m_data[i2]; - if(content_walkable(n2->d)) - continue;*/ - // Loop further down until not air - bool dropped_to_unknown = false; - do{ - vmanip.m_area.add_y(em, i2, -1); - n2 = &vmanip.m_data[i2]; - // if out of known area - if(vmanip.m_area.contains(i2) == false - || n2->getContent() == CONTENT_IGNORE){ - dropped_to_unknown = true; - break; - } - }while(ndef->get(*n2).walkable == false); - // Loop one up so that we're in air - vmanip.m_area.add_y(em, i2, 1); - n2 = &vmanip.m_data[i2]; - - bool old_is_water = (n->getContent() == c_water_source); - // Move mud to new place - if(!dropped_to_unknown) { - *n2 = *n; - // Set old place to be air (or water) - if(old_is_water) - *n = MapNode(c_water_source); - else - *n = MapNode(CONTENT_AIR); - } - - // Done - break; - } - } - } - } - - } - - }//timer1 -#endif - - } // Aging loop - /*********************** - END OF AGING LOOP - ************************/ - - /* - Add top and bottom side of water to transforming_liquid queue - */ - - for(s16 x=full_node_min.X; x<=full_node_max.X; x++) - for(s16 z=full_node_min.Z; z<=full_node_max.Z; z++) - { - // Node position - v2s16 p2d(x,z); - { - bool water_found = false; - // Use fast index incrementing - v3s16 em = vmanip.m_area.getExtent(); - u32 i = vmanip.m_area.index(v3s16(p2d.X, full_node_max.Y, p2d.Y)); - for(s16 y=full_node_max.Y; y>=full_node_min.Y; y--) - { - if(y == full_node_max.Y){ - water_found = - (vmanip.m_data[i].getContent() == c_water_source || - vmanip.m_data[i].getContent() == c_lava_source); - } - else if(water_found == false) - { - if(vmanip.m_data[i].getContent() == c_water_source || - vmanip.m_data[i].getContent() == c_lava_source) - { - v3s16 p = v3s16(p2d.X, y, p2d.Y); - data->transforming_liquid.push_back(p); - water_found = true; - } - } - else - { - // This can be done because water_found can only - // turn to true and end up here after going through - // a single block. - if(vmanip.m_data[i+1].getContent() != c_water_source || - vmanip.m_data[i+1].getContent() != c_lava_source) - { - v3s16 p = v3s16(p2d.X, y+1, p2d.Y); - data->transforming_liquid.push_back(p); - water_found = false; - } - } - - vmanip.m_area.add_y(em, i, -1); + } else if ((vm->m_data[i].param1 & 0x0F) != LIGHT_SUN) { + continue; } - } - } - - /* - Grow grass - */ - - for(s16 x=full_node_min.X; x<=full_node_max.X; x++) - for(s16 z=full_node_min.Z; z<=full_node_max.Z; z++) - { - // Node position in 2d - v2s16 p2d = v2s16(x,z); - - /* - Find the lowest surface to which enough light ends up - to make grass grow. - - Basically just wait until not air and not leaves. - */ - s16 surface_y = 0; - { - v3s16 em = vmanip.m_area.getExtent(); - u32 i = vmanip.m_area.index(v3s16(p2d.X, node_max.Y, p2d.Y)); - s16 y; - // Go to ground level - for(y=node_max.Y; y>=full_node_min.Y; y--) - { - MapNode &n = vmanip.m_data[i]; - if(ndef->get(n).param_type != CPT_LIGHT - || ndef->get(n).liquid_type != LIQUID_NONE) + vm->m_area.add_y(em, i, -1); + + for (int y = a.MaxEdge.Y; y >= a.MinEdge.Y; y--) { + MapNode &n = vm->m_data[i]; + if (!ndef->get(n).sunlight_propagates) break; - vmanip.m_area.add_y(em, i, -1); + n.param1 = LIGHT_SUN; + vm->m_area.add_y(em, i, -1); } - if(y >= full_node_min.Y) - surface_y = y; - else - surface_y = full_node_min.Y; - } - - u32 i = vmanip.m_area.index(p2d.X, surface_y, p2d.Y); - MapNode *n = &vmanip.m_data[i]; - if(n->getContent() == c_dirt){ - // Well yeah, this can't be overground... - if(surface_y < WATER_LEVEL - 20) - continue; - n->setContent(c_dirt_with_grass); } } - /* - Generate some trees - */ - assert(central_area_size.X == central_area_size.Z); - { - PseudoRandom ps (blockseed); - // Divide area into parts - s16 div = 8; - s16 sidelen = central_area_size.X / div; - double area = sidelen * sidelen; - for(s16 x0=0; x0seed, p2d_center); - // Put trees in random places on part of division - for(u32 i=0; im_area.index(a.MinEdge.X, y, z); + for (int x = a.MinEdge.X; x <= a.MaxEdge.X; x++, i++) { + MapNode &n = vm->m_data[i]; + if (n.getContent() == CONTENT_IGNORE || + !ndef->get(n).light_propagates) continue; - // Don't make a tree so high that it doesn't fit - if(y > node_max.Y - 6) - continue; - v3s16 p(x,y,z); - /* - Trees grow only on mud and grass - */ - { - u32 i = vmanip.m_area.index(v3s16(p)); - MapNode *n = &vmanip.m_data[i]; - if(n->getContent() != c_dirt - && n->getContent() != c_dirt_with_grass) - continue; - } - p.Y++; - // Make a tree - treegen::make_tree(vmanip, p, false, ndef, ps.next()); - } - } - } -#if 0 - /* - Make base ground level - */ - - for(s16 x=node_min.X; x<=node_max.X; x++) - for(s16 z=node_min.Z; z<=node_max.Z; z++) - { - // Node position - v2s16 p2d(x,z); - { - // Use fast index incrementing - v3s16 em = vmanip.m_area.getExtent(); - u32 i = vmanip.m_area.index(v3s16(p2d.X, node_min.Y, p2d.Y)); - for(s16 y=node_min.Y; y<=node_max.Y; y++) - { - // Only modify places that have no content - if(vmanip.m_data[i].getContent() == CONTENT_IGNORE) - { - // First priority: make air and water. - // This avoids caves inside water. - if(all_is_ground_except_caves == false - && val_is_ground(noisebuf_ground.get(x,y,z), - v3s16(x,y,z), data->seed) == false) - { - if(y <= WATER_LEVEL) - vmanip.m_data[i] = n_water_source; - else - vmanip.m_data[i] = n_air; - } - else if(noisebuf_cave.get(x,y,z) > CAVE_NOISE_THRESHOLD) - vmanip.m_data[i] = n_air; - else - vmanip.m_data[i] = n_stone; - } - - vmanip->m_area.add_y(em, i, 1); - } - } - } - - /* - Add mud and sand and others underground (in place of stone) - */ - - for(s16 x=node_min.X; x<=node_max.X; x++) - for(s16 z=node_min.Z; z<=node_max.Z; z++) - { - // Node position - v2s16 p2d(x,z); - { - // Use fast index incrementing - v3s16 em = vmanip.m_area.getExtent(); - u32 i = vmanip.m_area.index(v3s16(p2d.X, node_max.Y, p2d.Y)); - for(s16 y=node_max.Y; y>=node_min.Y; y--) - { - if(vmanip.m_data[i].getContent() == c_stone) - { - if(noisebuf_ground_crumbleness.get(x,y,z) > 1.3) - { - if(noisebuf_ground_wetness.get(x,y,z) > 0.0) - vmanip.m_data[i] = n_dirt; - else - vmanip.m_data[i] = n_sand; - } - else if(noisebuf_ground_crumbleness.get(x,y,z) > 0.7) - { - if(noisebuf_ground_wetness.get(x,y,z) < -0.6) - vmanip.m_data[i] = n_gravel; - } - else if(noisebuf_ground_crumbleness.get(x,y,z) < - -3.0 + MYMIN(0.1 * sqrt((float)MYMAX(0, -y)), 1.5)) - { - vmanip.m_data[i] = n_lava_source; - for(s16 x1=-1; x1<=1; x1++) - for(s16 y1=-1; y1<=1; y1++) - for(s16 z1=-1; z1<=1; z1++) - data->transforming_liquid.push_back( - v3s16(p2d.X+x1, y+y1, p2d.Y+z1)); - } - } - - vmanip->m_area.add_y(em, i, -1); - } - } - } - - /* - Add dungeons - */ - - //if(node_min.Y < approx_groundlevel) - //if(myrand() % 3 == 0) - //if(myrand() % 3 == 0 && node_min.Y < approx_groundlevel) - //if(myrand() % 100 == 0 && node_min.Y < approx_groundlevel) - //float dungeon_rarity = g_settings.getFloat("dungeon_rarity"); - float dungeon_rarity = 0.02; - if(((noise3d(blockpos.X,blockpos.Y,blockpos.Z,data->seed)+1.0)/2.0) - < dungeon_rarity - && node_min.Y < approx_groundlevel) - { - // Dungeon generator doesn't modify places which have this set - vmanip->clearFlag(VMANIP_FLAG_DUNGEON_INSIDE - | VMANIP_FLAG_DUNGEON_PRESERVE); - - // Set all air and water to be untouchable to make dungeons open - // to caves and open air - for(s16 x=full_node_min.X; x<=full_node_max.X; x++) - for(s16 z=full_node_min.Z; z<=full_node_max.Z; z++) - { - // Node position - v2s16 p2d(x,z); - { - // Use fast index incrementing - v3s16 em = vmanip.m_area.getExtent(); - u32 i = vmanip.m_area.index(v3s16(p2d.X, full_node_max.Y, p2d.Y)); - for(s16 y=full_node_max.Y; y>=full_node_min.Y; y--) - { - if(vmanip.m_data[i].getContent() == CONTENT_AIR) - vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE; - else if(vmanip.m_data[i].getContent() == c_water_source) - vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE; - vmanip->m_area.add_y(em, i, -1); - } - } - } - - PseudoRandom random(blockseed+2); - - // Add it - make_dungeon1(vmanip, random, ndef); - - // Convert some cobble to mossy cobble - for(s16 x=full_node_min.X; x<=full_node_max.X; x++) - for(s16 z=full_node_min.Z; z<=full_node_max.Z; z++) - { - // Node position - v2s16 p2d(x,z); - { - // Use fast index incrementing - v3s16 em = vmanip.m_area.getExtent(); - u32 i = vmanip.m_area.index(v3s16(p2d.X, full_node_max.Y, p2d.Y)); - for(s16 y=full_node_max.Y; y>=full_node_min.Y; y--) - { - // (noisebuf not used because it doesn't contain the - // full area) - double wetness = noise3d_param( - get_ground_wetness_params(data->seed), x,y,z); - double d = noise3d_perlin((float)x/2.5, - (float)y/2.5,(float)z/2.5, - blockseed, 2, 1.4); - if(vmanip.m_data[i].getContent() == c_cobble) - { - if(d < wetness/3.0) - { - vmanip.m_data[i].setContent(c_mossycobble); - } - } - /*else if(vmanip.m_flags[i] & VMANIP_FLAG_DUNGEON_INSIDE) - { - if(wetness > 1.2) - vmanip.m_data[i].setContent(c_dirt); - }*/ - vmanip->m_area.add_y(em, i, -1); + u8 light_produced = ndef->get(n).light_source & 0x0F; + if (light_produced) + n.param1 = light_produced; + + u8 light = n.param1 & 0x0F; + if (light) { + lightSpread(a, v3s16(x, y, z + 1), light - 1); + lightSpread(a, v3s16(x, y + 1, z ), light - 1); + lightSpread(a, v3s16(x + 1, y, z ), light - 1); + lightSpread(a, v3s16(x, y, z - 1), light - 1); + lightSpread(a, v3s16(x, y - 1, z ), light - 1); + lightSpread(a, v3s16(x - 1, y, z ), light - 1); } } } } - /* - Add NC - */ - { - PseudoRandom ncrandom(blockseed+9324342); - if(ncrandom.range(0, 1000) == 0 && blockpos.Y <= -3) - { - make_nc(vmanip, ncrandom, ndef); - } - } - - /* - Add top and bottom side of water to transforming_liquid queue - */ - - for(s16 x=node_min.X; x<=node_max.X; x++) - for(s16 z=node_min.Z; z<=node_max.Z; z++) - { - // Node position - v2s16 p2d(x,z); - { - bool water_found = false; - // Use fast index incrementing - v3s16 em = vmanip.m_area.getExtent(); - u32 i = vmanip.m_area.index(v3s16(p2d.X, node_max.Y, p2d.Y)); - for(s16 y=node_max.Y; y>=node_min.Y; y--) - { - if(water_found == false) - { - if(vmanip.m_data[i].getContent() == c_water_source) - { - v3s16 p = v3s16(p2d.X, y, p2d.Y); - data->transforming_liquid.push_back(p); - water_found = true; - } - } - else - { - // This can be done because water_found can only - // turn to true and end up here after going through - // a single block. - if(vmanip.m_data[i+1].getContent() != c_water_source) - { - v3s16 p = v3s16(p2d.X, y+1, p2d.Y); - data->transforming_liquid.push_back(p); - water_found = false; - } - } - - vmanip->m_area.add_y(em, i, -1); - } - } - } - - /* - If close to ground level - */ - - //if(abs(approx_ground_depth) < 30) - if(minimum_ground_depth < 5 && maximum_ground_depth > -5) - { - /* - Add grass and mud - */ - - for(s16 x=node_min.X; x<=node_max.X; x++) - for(s16 z=node_min.Z; z<=node_max.Z; z++) - { - // Node position - v2s16 p2d(x,z); - { - bool possibly_have_sand = get_have_beach(data->seed, p2d); - bool have_sand = false; - u32 current_depth = 0; - bool air_detected = false; - bool water_detected = false; - bool have_clay = false; - - // Use fast index incrementing - s16 start_y = node_max.Y+2; - v3s16 em = vmanip.m_area.getExtent(); - u32 i = vmanip.m_area.index(v3s16(p2d.X, start_y, p2d.Y)); - for(s16 y=start_y; y>=node_min.Y-3; y--) - { - if(vmanip.m_data[i].getContent() == c_water_source) - water_detected = true; - if(vmanip.m_data[i].getContent() == CONTENT_AIR) - air_detected = true; - - if((vmanip.m_data[i].getContent() == c_stone - || vmanip.m_data[i].getContent() == c_dirt_with_grass - || vmanip.m_data[i].getContent() == c_dirt - || vmanip.m_data[i].getContent() == c_sand - || vmanip.m_data[i].getContent() == c_gravel - ) && (air_detected || water_detected)) - { - if(current_depth == 0 && y <= WATER_LEVEL+2 - && possibly_have_sand) - have_sand = true; - - if(current_depth < 4) - { - if(have_sand) - { - vmanip.m_data[i] = MapNode(c_sand); - } - #if 1 - else if(current_depth==0 && !water_detected - && y >= WATER_LEVEL && air_detected) - vmanip.m_data[i] = MapNode(c_dirt_with_grass); - #endif - else - vmanip.m_data[i] = MapNode(c_dirt); - } - else - { - if(vmanip.m_data[i].getContent() == c_dirt - || vmanip.m_data[i].getContent() == c_dirt_with_grass) - vmanip.m_data[i] = MapNode(c_stone); - } - - current_depth++; - - if(current_depth >= 8) - break; - } - else if(current_depth != 0) - break; - - vmanip->m_area.add_y(em, i, -1); - } - } - } - - /* - Calculate some stuff - */ - - float surface_humidity = surface_humidity_2d(data->seed, p2d_center); - bool is_jungle = surface_humidity > 0.75; - // Amount of trees - u32 tree_count = gen_area_nodes * tree_amount_2d(data->seed, p2d_center); - if(is_jungle) - tree_count *= 5; - - /* - Add trees - */ - PseudoRandom treerandom(blockseed); - // Put trees in random places on part of division - for(u32 i=0; iseed, v2s16(x,z), 4); - // Don't make a tree under water level - if(y < WATER_LEVEL) - continue; - // Make sure tree fits (only trees whose starting point is - // at this block are added) - if(y < node_min.Y || y > node_max.Y) - continue; - /* - Find exact ground level - */ - v3s16 p(x,y+6,z); - bool found = false; - for(; p.Y >= y-6; p.Y--) - { - u32 i = vmanip->m_area.index(p); - MapNode *n = &vmanip->m_data[i]; - if(n->getContent() != CONTENT_AIR && n->getContent() != c_water_source && n->getContent() != CONTENT_IGNORE) - { - found = true; - break; - } - } - // If not found, handle next one - if(found == false) - continue; - - { - u32 i = vmanip->m_area.index(p); - MapNode *n = &vmanip->m_data[i]; - - if(n->getContent() != c_dirt && n->getContent() != c_dirt_with_grass && n->getContent() != c_sand) - continue; - - // Papyrus grows only on mud and in water - if(n->getContent() == c_dirt && y <= WATER_LEVEL) - { - p.Y++; - make_papyrus(vmanip, p, ndef); - } - // Trees grow only on mud and grass, on land - else if((n->getContent() == c_dirt || n->getContent() == c_dirt_with_grass) && y > WATER_LEVEL + 2) - { - p.Y++; - //if(surface_humidity_2d(data->seed, v2s16(x, y)) < 0.5) - if(is_jungle == false) - { - bool is_apple_tree; - if(myrand_range(0,4) != 0) - is_apple_tree = false; - else - is_apple_tree = noise2d_perlin( - 0.5+(float)p.X/100, 0.5+(float)p.Z/100, - data->seed+342902, 3, 0.45) > 0.2; - make_tree(vmanip, p, is_apple_tree, ndef); - } - else - make_jungletree(vmanip, p, ndef); - } - // Cactii grow only on sand, on land - else if(n->getContent() == c_sand && y > WATER_LEVEL + 2) - { - p.Y++; - make_cactus(vmanip, p, ndef); - } - } - } - - /* - Add jungle grass - */ - if(is_jungle) - { - PseudoRandom grassrandom(blockseed); - for(u32 i=0; iseed, v2s16(x,z), 4); - if(y < WATER_LEVEL) - continue; - if(y < node_min.Y || y > node_max.Y) - continue; - /* - Find exact ground level - */ - v3s16 p(x,y+6,z); - bool found = false; - for(; p.Y >= y-6; p.Y--) - { - u32 i = vmanip->m_area.index(p); - MapNode *n = &vmanip->m_data[i]; - if(data->nodedef->get(*n).is_ground_content) - { - found = true; - break; - } - } - // If not found, handle next one - if(found == false) - continue; - p.Y++; - if(vmanip.m_area.contains(p) == false) - continue; - if(vmanip.m_data[vmanip.m_area.index(p)].getContent() != CONTENT_AIR) - continue; - /*p.Y--; - if(vmanip.m_area.contains(p)) - vmanip.m_data[vmanip.m_area.index(p)] = c_dirt; - p.Y++;*/ - if(vmanip.m_area.contains(p)) - vmanip.m_data[vmanip.m_area.index(p)] = c_junglegrass; - } - } - -#if 0 - /* - Add some kind of random stones - */ - - u32 random_stone_count = gen_area_nodes * - randomstone_amount_2d(data->seed, p2d_center); - // Put in random places on part of division - for(u32 i=0; iseed, v2s16(x,z), 1); - // Don't add under water level - /*if(y < WATER_LEVEL) - continue;*/ - // Don't add if doesn't belong to this block - if(y < node_min.Y || y > node_max.Y) - continue; - v3s16 p(x,y,z); - // Filter placement - /*{ - u32 i = vmanip->m_area.index(v3s16(p)); - MapNode *n = &vmanip->m_data[i]; - if(n->getContent() != c_dirt && n->getContent() != c_dirt_with_grass) - continue; - }*/ - // Will be placed one higher - p.Y++; - // Add it - make_randomstone(vmanip, p); - } -#endif - -#if 0 - /* - Add larger stones - */ - - u32 large_stone_count = gen_area_nodes * - largestone_amount_2d(data->seed, p2d_center); - //u32 large_stone_count = 1; - // Put in random places on part of division - for(u32 i=0; iseed, v2s16(x,z), 1); - // Don't add under water level - /*if(y < WATER_LEVEL) - continue;*/ - // Don't add if doesn't belong to this block - if(y < node_min.Y || y > node_max.Y) - continue; - v3s16 p(x,y,z); - // Filter placement - /*{ - u32 i = vmanip->m_area.index(v3s16(p)); - MapNode *n = &vmanip->m_data[i]; - if(n->getContent() != c_dirt && n->getContent() != c_dirt_with_grass) - continue; - }*/ - // Will be placed one lower - p.Y--; - // Add it - make_largestone(vmanip, p); - } -#endif - } + //printf("updateLighting: %dms\n", t.stop()); +} - /* - Add minerals - */ - { - PseudoRandom mineralrandom(blockseed); - - /* - Add meseblocks - */ - for(s16 i=0; i 0.0) - new_content = MapNode(c_stone_with_iron); - /*if(noisebuf_ground_wetness.get(x,y,z) > 0.0) - vmanip.m_data[i] = MapNode(c_dirt); - else - vmanip.m_data[i] = MapNode(c_sand);*/ - } - /*else if(noisebuf_ground_crumbleness.get(x,y,z) > 0.1) - { - }*/ - - if(new_content.getContent() != CONTENT_IGNORE) - { - for(u16 i=0; i<27; i++) - { - v3s16 p = v3s16(x,y,z) + g_27dirs[i]; - u32 vi = vmanip.m_area.index(p); - if(vmanip.m_data[vi].getContent() == base_content) - { - if(mineralrandom.next()%sparseness == 0) - vmanip.m_data[vi] = new_content; - } - } - } - } - } - /* - Add coal - */ - //for(s16 i=0; i < MYMAX(0, 50 - abs(node_min.Y+8 - (-30))); i++) - //for(s16 i=0; i<50; i++) - u16 coal_amount = 30; - u16 coal_rareness = 60 / coal_amount; - if(coal_rareness == 0) - coal_rareness = 1; - if(mineralrandom.next()%coal_rareness == 0) - { - u16 a = mineralrandom.next() % 16; - u16 amount = coal_amount * a*a*a / 1000; - for(s16 i=0; i nmax.Y); + bool sunlight = !block_is_underground; - core::map light_sources; - core::map unlight_from; + ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG); - voxalgo::clearLightAndCollectSources(vmanip, a, bank, ndef, - light_sources, unlight_from); - - bool inexistent_top_provides_sunlight = !block_is_underground; - voxalgo::SunlightPropagateResult res = voxalgo::propagateSunlight( - vmanip, a, inexistent_top_provides_sunlight, - light_sources, ndef); - // TODO: Do stuff according to bottom_sunlight_valid + for (int i = 0; i < 2; i++) { + enum LightBank bank = banks[i]; + std::set light_sources; + std::map unlight_from; - vmanip.unspreadLight(bank, unlight_from, light_sources, ndef); + voxalgo::clearLightAndCollectSources(*vm, a, bank, ndef, + light_sources, unlight_from); + voxalgo::propagateSunlight(*vm, a, sunlight, light_sources, ndef); - vmanip.spreadLight(bank, light_sources, ndef); - } + vm->unspreadLight(bank, unlight_from, light_sources, ndef); + vm->spreadLight(bank, light_sources, ndef); } } + + +//////////////////////// Mapgen V6 parameter read/write + +bool MapgenV6Params::readParams(Settings *settings) { + freq_desert = settings->getFloat("mgv6_freq_desert"); + freq_beach = settings->getFloat("mgv6_freq_beach"); -#endif ///BIG COMMENT + bool success = + settings->getNoiseParams("mgv6_np_terrain_base", np_terrain_base) && + settings->getNoiseParams("mgv6_np_terrain_higher", np_terrain_higher) && + settings->getNoiseParams("mgv6_np_steepness", np_steepness) && + settings->getNoiseParams("mgv6_np_height_select", np_height_select) && + settings->getNoiseParams("mgv6_np_mud", np_mud) && + settings->getNoiseParams("mgv6_np_beach", np_beach) && + settings->getNoiseParams("mgv6_np_biome", np_biome) && + settings->getNoiseParams("mgv6_np_cave", np_cave) && + settings->getNoiseParams("mgv6_np_humidity", np_humidity) && + settings->getNoiseParams("mgv6_np_trees", np_trees) && + settings->getNoiseParams("mgv6_np_apple_trees", np_apple_trees); + return success; +} + + +void MapgenV6Params::writeParams(Settings *settings) { + settings->setFloat("mgv6_freq_desert", freq_desert); + settings->setFloat("mgv6_freq_beach", freq_beach); + + settings->setNoiseParams("mgv6_np_terrain_base", np_terrain_base); + settings->setNoiseParams("mgv6_np_terrain_higher", np_terrain_higher); + settings->setNoiseParams("mgv6_np_steepness", np_steepness); + settings->setNoiseParams("mgv6_np_height_select", np_height_select); + settings->setNoiseParams("mgv6_np_mud", np_mud); + settings->setNoiseParams("mgv6_np_beach", np_beach); + settings->setNoiseParams("mgv6_np_biome", np_biome); + settings->setNoiseParams("mgv6_np_cave", np_cave); + settings->setNoiseParams("mgv6_np_humidity", np_humidity); + settings->setNoiseParams("mgv6_np_trees", np_trees); + settings->setNoiseParams("mgv6_np_apple_trees", np_apple_trees); +} -BlockMakeData::BlockMakeData(): - no_op(false), - vmanip(NULL), - seed(0), - nodedef(NULL) -{} -BlockMakeData::~BlockMakeData() -{ - delete vmanip; +bool MapgenV7Params::readParams(Settings *settings) { + bool success = + settings->getNoiseParams("mgv7_np_terrain_base", np_terrain_base) && + settings->getNoiseParams("mgv7_np_terrain_alt", np_terrain_alt) && + settings->getNoiseParams("mgv7_np_terrain_persist", np_terrain_persist) && + settings->getNoiseParams("mgv7_np_height_select", np_height_select) && + settings->getNoiseParams("mgv7_np_filler_depth", np_filler_depth) && + settings->getNoiseParams("mgv7_np_mount_height", np_mount_height) && + settings->getNoiseParams("mgv7_np_ridge_uwater", np_ridge_uwater) && + settings->getNoiseParams("mgv7_np_mountain", np_mountain) && + settings->getNoiseParams("mgv7_np_ridge", np_ridge); + return success; } -//}; // namespace mapgen - +void MapgenV7Params::writeParams(Settings *settings) { + settings->setNoiseParams("mgv7_np_terrain_base", np_terrain_base); + settings->setNoiseParams("mgv7_np_terrain_alt", np_terrain_alt); + settings->setNoiseParams("mgv7_np_terrain_persist", np_terrain_persist); + settings->setNoiseParams("mgv7_np_height_select", np_height_select); + settings->setNoiseParams("mgv7_np_filler_depth", np_filler_depth); + settings->setNoiseParams("mgv7_np_mount_height", np_mount_height); + settings->setNoiseParams("mgv7_np_ridge_uwater", np_ridge_uwater); + settings->setNoiseParams("mgv7_np_mountain", np_mountain); + settings->setNoiseParams("mgv7_np_ridge", np_ridge); +}