diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 93e2d5c9f..b6981582e 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -410,6 +410,18 @@ Currently supported flags: absheight Also produce this same ore between the height range of -height_max and -height_min. Useful for having ore in sky realms without having to duplicate ore entries. +Decoration types +------------------- +The varying types of decorations that can be placed. +The default value is simple, and is currently the only type supported. + +- simple + Creates a 1xHx1 column of a specified node (or a random node from a list, if a decoration + list is specified). Can specify a certain node it must spawn next to, such as water or lava, + for example. Can also generate a decoration of random height between a specified lower and + upper bound. This type of decoration is intended for placement of grass, flowers, cacti, + papyrus, and so on. + HUD element types ------------------- The position field is used for all element types. @@ -946,6 +958,7 @@ minetest.register_craftitem(name, item definition) minetest.register_alias(name, convert_to) minetest.register_craft(recipe) minetest.register_ore(ore definition) +minetest.register_decoration(decoration definition) Global callback registration functions: (Call these only at load time) minetest.register_globalstep(func(dtime)) @@ -1835,7 +1848,7 @@ Recipe for register_craft (furnace fuel) Ore definition (register_ore) { - ore_type = "scatter" -- See "Ore types" + ore_type = "scatter", -- See "Ore types" ore = "default:stone_with_coal", wherein = "default:stone", clust_scarcity = 8*8*8, @@ -1857,6 +1870,40 @@ Ore definition (register_ore) ^ Needed for sheet ore_type. Omit from scatter ore_type for a uniform ore distribution } +Decoration definition (register_decoration) +{ + deco_type = "simple", -- See "Decoration types" + place_on = "default:dirt_with_grass", + ^ Node that decoration can be placed on + divlen = 8, + ^ Number of divisions made in the chunk being generated + fill_ratio = 0.02, + ^ Ratio of the area to be uniformly filled by the decoration. + ^ Used only if noise_params is not specified. + noise_params = {offset=0, scale=.45, spread={x=100, y=100, z=100}, seed=354, octaves=3, persist=0.7}, + ^ NoiseParams structure describing the perlin noise used for decoration distribution. + ^ The result of this is multiplied by the 2d area of the division being decorated. + biomes = {"Oceanside", "Hills", "Plains"}, + ^ List of biomes in which this decoration occurs. Occurs in all biomes if this is omitted, + ^ and ignored if the Mapgen being used does not support biomes. + + ----- Simple-type parameters + decoration = "default:grass", + ^ The node name used as the decoration. + ^ If instead a list of strings, a randomly selected node from the list is placed as the decoration. + height = 1, + ^ Number of nodes high the decoration is made. + ^ If height_max is not 0, this is the lower bound of the randomly selected height. + height_max = 0, + ^ Number of nodes the decoration can be at maximum. + ^ If absent, the parameter 'height' is used as a constant. + spawn_by = "default:water", + ^ Node that the decoration only spawns next to, in a 1-node square radius. + num_spawn_by = 1, + ^ Number of spawn_by nodes that must be surrounding the decoration position to occur. + ^ If absent or -1, decorations occur next to any nodes. +} + Chatcommand definition (register_chatcommand) { params = " ", -- short parameter description diff --git a/src/biome.cpp b/src/biome.cpp index b50c562a0..bc84d4bc1 100644 --- a/src/biome.cpp +++ b/src/biome.cpp @@ -168,3 +168,13 @@ Biome *BiomeDefManager::getBiome(float heat, float humidity, s16 y) { return biome_closest ? biome_closest : biomes[0]; } + + +u8 BiomeDefManager::getBiomeIdByName(const char *name) { + for (size_t i = 0; i != biomes.size(); i++) { + if (!strcasecmp(name, biomes[i]->name.c_str())) + return i; + } + + return 0; +} diff --git a/src/biome.h b/src/biome.h index 17703db5a..535dc4989 100644 --- a/src/biome.h +++ b/src/biome.h @@ -84,6 +84,7 @@ public: void addBiome(Biome *b); void resolveNodeNames(INodeDefManager *ndef); + u8 getBiomeIdByName(const char *name); }; #endif diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index d2bed7ed8..71c283241 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -239,7 +239,7 @@ void set_default_settings(Settings *settings) settings->setDefault("mgv7_np_terrain_mod", "0, 1, (350, 350, 350), 85039, 5, 0.6"); settings->setDefault("mgv7_np_terrain_persist", "0, 1, (500, 500, 500), 539, 3, 0.6"); settings->setDefault("mgv7_np_height_select", "0.5, 0.5, (250, 250, 250), 4213, 5, 0.69"); - settings->setDefault("mgv7_np_ridge", "0.5, 1, (100, 100, 100), 6467, 4, 0.75"); + settings->setDefault("mgv7_np_ridge", "0, 1, (100, 100, 100), 6467, 4, 0.75"); settings->setDefault("mgindev_np_terrain_base", "-4, 20, (250, 250, 250), 82341, 5, 0.6, 10, 10"); settings->setDefault("mgindev_np_terrain_higher", "20, 16, (500, 500, 500), 85039, 5, 0.6, 10, 10"); diff --git a/src/emerge.cpp b/src/emerge.cpp index fd6c0e91f..2c94d46c1 100644 --- a/src/emerge.cpp +++ b/src/emerge.cpp @@ -101,6 +101,10 @@ EmergeManager::~EmergeManager() { for (unsigned int i = 0; i < ores.size(); i++) delete ores[i]; ores.clear(); + + for (unsigned int i = 0; i < decorations.size(); i++) + delete decorations[i]; + decorations.clear(); for (std::map::iterator iter = mglist.begin(); iter != mglist.end(); iter ++) { diff --git a/src/emerge.h b/src/emerge.h index b42e82d38..084956932 100644 --- a/src/emerge.h +++ b/src/emerge.h @@ -87,6 +87,7 @@ public: //Mapgen-related structures BiomeDefManager *biomedef; std::vector ores; + std::vector decorations; EmergeManager(IGameDef *gamedef); ~EmergeManager(); diff --git a/src/mapgen.cpp b/src/mapgen.cpp index 835c14be1..49ac827e1 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -87,7 +87,7 @@ void Ore::resolveNodeNames(INodeDefManager *ndef) { wherein = CONTENT_AIR; } } - + if (wherein == CONTENT_IGNORE) { wherein = ndef->getId(wherein_name); if (wherein == CONTENT_IGNORE) { @@ -110,9 +110,8 @@ void Ore::placeOre(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax) { return; resolveNodeNames(mg->ndef); - + int ymin, ymax; - if (in_range & ORE_RANGE_MIRROR) { ymin = MYMAX(nmin.Y, -height_max); ymax = MYMIN(nmax.Y, -height_min); @@ -122,7 +121,7 @@ void Ore::placeOre(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax) { } if (clust_size >= ymax - ymin + 1) return; - + nmin.Y = ymin; nmax.Y = ymax; generate(mg->vm, mg->seed, blockseed, nmin, nmax); @@ -130,7 +129,7 @@ void Ore::placeOre(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax) { void OreScatter::generate(ManualMapVoxelManipulator *vm, int seed, - u32 blockseed, v3s16 nmin, v3s16 nmax) { + u32 blockseed, v3s16 nmin, v3s16 nmax) { PseudoRandom pr(blockseed); MapNode n_ore(ore, 0, ore_param2); @@ -145,16 +144,16 @@ void OreScatter::generate(ManualMapVoxelManipulator *vm, int seed, 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; - + u32 i = vm->m_area.index(x0 + x1, y0 + y1, z0 + z1); if (vm->m_data[i].getContent() == wherein) vm->m_data[i] = n_ore; @@ -167,10 +166,10 @@ 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; @@ -178,14 +177,14 @@ void OreSheet::generate(ManualMapVoxelManipulator *vm, int seed, } 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; @@ -193,7 +192,7 @@ void OreSheet::generate(ManualMapVoxelManipulator *vm, int seed, u32 i = vm->m_area.index(x, y, z); if (!vm->m_area.contains(i)) continue; - + if (vm->m_data[i].getContent() == wherein) vm->m_data[i] = n_ore; } @@ -201,6 +200,316 @@ void OreSheet::generate(ManualMapVoxelManipulator *vm, int seed, } +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; + } +} + + +Decoration::~Decoration() { + delete np; +} + + +void Decoration::resolveNodeNames(INodeDefManager *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) { + resolveNodeNames(mg->ndef); + + PseudoRandom ps(blockseed + 53); + int carea_size = nmax.X - nmin.X + 1; + + // Divide area into parts + s16 sidelen = carea_size / divlen; + float 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 + ); + + // 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; + 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, 0, v3s16(x, y, z)); + } + } +} + + +#if 0 +void Decoration::placeCutoffs(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax) { + PseudoRandom pr(blockseed + 53); + std::vector handled_cutoffs; + + // 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 + + +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"; + 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"; + 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"; + c = CONTENT_AIR; + } + c_decolist.push_back(c); + } +} + + +void DecoSimple::generate(Mapgen *mg, PseudoRandom *pr, s16 max_y, s16 start_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 = 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; + + height = MYMIN(height, max_y - p.Y); + + v3s16 em = vm->m_area.getExtent(); + for (int i = start_y; i < height; i++) { + vm->m_area.add_y(em, vi, 1); + vm->m_data[vi] = MapNode(c); + } +} + + +int DecoSimple::getHeight() { + return (deco_height_max > 0) ? deco_height_max : deco_height; +} + + +std::string DecoSimple::getName() { + return deco_name; +} + + +/////////////////////////////////////////////////////////////////////////////// + + +Mapgen::Mapgen() { + seed = 0; + water_level = 0; + generating = false; + id = -1; + vm = NULL; + ndef = NULL; + heightmap = NULL; + biomemap = NULL; +} + + +// 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; + + vm->m_area.add_y(em, i, -1); + } + return (y >= y_nodes_min) ? y : y_nodes_min - 1; +} + + +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; + + vm->m_area.add_y(em, i, -1); + } + return y; +} + + +void Mapgen::updateHeightmap(v3s16 nmin, v3s16 nmax) { + if (!heightmap) + return; + + //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++) { + s16 y = findGroundLevel(v2s16(x, z), nmin.Y, nmax.Y); + heightmap[index++] = y; + } + } + //printf("updateHeightmap: %dus\n", t.stop()); +} + + void Mapgen::updateLiquid(UniqueQueue *trans_liquid, v3s16 nmin, v3s16 nmax) { bool isliquid, wasliquid; v3s16 em = vm->m_area.getExtent(); @@ -208,11 +517,11 @@ void Mapgen::updateLiquid(UniqueQueue *trans_liquid, v3s16 nmin, v3s16 nm for (s16 z = nmin.Z; z <= nmax.Z; z++) { for (s16 x = nmin.X; x <= nmax.X; x++) { wasliquid = true; - + u32 i = vm->m_area.index(x, nmax.Y, z); for (s16 y = nmax.Y; y >= nmin.Y; y--) { isliquid = ndef->get(vm->m_data[i]).isLiquid(); - + // there was a change between liquid and nonliquid, add to queue if (isliquid != wasliquid) trans_liquid->push_back(v3s16(x, y, z)); @@ -242,7 +551,7 @@ void Mapgen::setLighting(v3s16 nmin, v3s16 nmax, u8 light) { void Mapgen::lightSpread(VoxelArea &a, v3s16 p, u8 light) { if (light <= 1 || !a.contains(p)) return; - + u32 vi = vm->m_area.index(p); MapNode &nn = vm->m_data[vi]; @@ -250,9 +559,9 @@ void Mapgen::lightSpread(VoxelArea &a, v3s16 p, u8 light) { // should probably compare masked, but doesn't seem to make a difference if (light <= nn.param1 || !ndef->get(nn).light_propagates) return; - + nn.param1 = 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); @@ -282,7 +591,7 @@ void Mapgen::calcLighting(v3s16 nmin, v3s16 nmax) { continue; } 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) @@ -292,7 +601,7 @@ void Mapgen::calcLighting(v3s16 nmin, v3s16 nmax) { } } } - + // now spread the sunlight and light up any sources for (int z = a.MinEdge.Z; z <= a.MaxEdge.Z; z++) { for (int y = a.MinEdge.Y; y <= a.MaxEdge.Y; y++) { @@ -302,11 +611,11 @@ void Mapgen::calcLighting(v3s16 nmin, v3s16 nmax) { if (n.getContent() == CONTENT_IGNORE || !ndef->get(n).light_propagates) continue; - + 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); @@ -319,7 +628,7 @@ void Mapgen::calcLighting(v3s16 nmin, v3s16 nmax) { } } } - + //printf("updateLighting: %dms\n", t.stop()); } @@ -331,24 +640,24 @@ void Mapgen::calcLightingOld(v3s16 nmin, v3s16 nmax) { bool sunlight = !block_is_underground; ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG); - + for (int i = 0; i < 2; i++) { enum LightBank bank = banks[i]; std::set light_sources; std::map unlight_from; voxalgo::clearLightAndCollectSources(*vm, a, bank, ndef, - light_sources, unlight_from); + light_sources, unlight_from); voxalgo::propagateSunlight(*vm, a, sunlight, 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"); @@ -367,12 +676,12 @@ bool MapgenV6Params::readParams(Settings *settings) { 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); @@ -411,7 +720,6 @@ void MapgenV7Params::writeParams(Settings *settings) { /////////////////////////////////// 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; @@ -421,9 +729,9 @@ s16 Mapgen::find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision) { 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); - + 0.2+(float)p2d.X/250, 0.7+(float)p2d.Y/250, + seed+59420, 3, 0.50); + return (sandnoise > 0.15); } diff --git a/src/mapgen.h b/src/mapgen.h index 5d1e3bdf0..e8252cbbf 100644 --- a/src/mapgen.h +++ b/src/mapgen.h @@ -86,9 +86,15 @@ public: int id; ManualMapVoxelManipulator *vm; INodeDefManager *ndef; + s16 *heightmap; + u8 *biomemap; + Mapgen(); virtual ~Mapgen() {} + s16 findGroundLevelFull(v2s16 p2d); + s16 findGroundLevel(v2s16 p2d, s16 ymin, s16 ymax); + void updateHeightmap(v3s16 nmin, v3s16 nmax); void updateLiquid(UniqueQueue *trans_liquid, v3s16 nmin, v3s16 nmax); void setLighting(v3s16 nmin, v3s16 nmax, u8 light); void lightSpread(VoxelArea &a, v3s16 p, u8 light); @@ -166,5 +172,90 @@ class OreSheet : public Ore { Ore *createOre(OreType type); + +enum DecorationType { + DECO_SIMPLE, + DECO_SCHEMATIC, + DECO_LSYSTEM +}; + +#if 0 +struct CutoffData { + VoxelArea a; + Decoration *deco; + //v3s16 p; + //v3s16 size; + //s16 height; + + CutoffData(s16 x, s16 y, s16 z, s16 h) { + p = v3s16(x, y, z); + height = h; + } +}; +#endif + +class Decoration { +public: + int mapseed; + std::string place_on_name; + content_t c_place_on; + s16 divlen; + float fill_ratio; + NoiseParams *np; + + std::set biomes; + //std::list cutoffs; + //JMutex cutoff_mutex; + + virtual ~Decoration(); + + virtual void resolveNodeNames(INodeDefManager *ndef); + void placeDeco(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax); + void placeCutoffs(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax); + + virtual void generate(Mapgen *mg, PseudoRandom *pr, s16 max_y, + s16 start_y, v3s16 p) = 0; + virtual int getHeight() = 0; + virtual std::string getName() = 0; +}; + +class DecoSimple : public Decoration { +public: + std::string deco_name; + std::string spawnby_name; + content_t c_deco; + content_t c_spawnby; + s16 deco_height; + s16 deco_height_max; + s16 nspawnby; + + std::vector decolist_names; + std::vector c_decolist; + + ~DecoSimple() {} + + void resolveNodeNames(INodeDefManager *ndef); + virtual void generate(Mapgen *mg, PseudoRandom *pr, s16 max_y, + s16 start_y, v3s16 p); + virtual int getHeight(); + virtual std::string getName(); +}; + +/* +class DecoSchematic : public Decoration { +public: + virtual void generate(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax); +}; +*/ + +/* +class DecoLSystem : public Decoration { +public: + virtual void generate(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax); +}; +*/ + +Decoration *createDecoration(DecorationType type); + #endif diff --git a/src/mapgen_v6.cpp b/src/mapgen_v6.cpp index eaca33988..a411f966b 100644 --- a/src/mapgen_v6.cpp +++ b/src/mapgen_v6.cpp @@ -79,7 +79,7 @@ MapgenV6::MapgenV6(int mapgenid, MapgenV6Params *params, EmergeManager *emerge) this->freq_beach = params->freq_beach; this->ystride = csize.X; //////fix this - + np_cave = ¶ms->np_cave; np_humidity = ¶ms->np_humidity; np_trees = ¶ms->np_trees; @@ -108,23 +108,6 @@ MapgenV6::~MapgenV6() { //////////////////////// Some helper functions for the map generator -// Returns Y one under area minimum if not found -s16 MapgenV6::find_ground_level(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; - - vm->m_area.add_y(em, i, -1); - } - return (y >= y_nodes_min) ? y : y_nodes_min - 1; -} // Returns Y one under area minimum if not found s16 MapgenV6::find_stone_level(v2s16 p2d) { @@ -849,7 +832,7 @@ void MapgenV6::placeTreesAndJungleGrass() { s16 x = grassrandom.range(p2d_min.X, p2d_max.X); s16 z = grassrandom.range(p2d_min.Y, p2d_max.Y); - s16 y = find_ground_level(v2s16(x, z)); ////////////////optimize this! + s16 y = findGroundLevelFull(v2s16(x, z)); ////////////////optimize this! if (y < water_level || y < node_min.Y || y > node_max.Y) continue; @@ -866,7 +849,7 @@ void MapgenV6::placeTreesAndJungleGrass() { for (u32 i = 0; i < tree_count; i++) { s16 x = myrand_range(p2d_min.X, p2d_max.X); s16 z = myrand_range(p2d_min.Y, p2d_max.Y); - s16 y = find_ground_level(v2s16(x, z)); ////////////////////optimize this! + s16 y = findGroundLevelFull(v2s16(x, z)); ////////////////////optimize this! // Don't make a tree under water level // Don't make a tree so high that it doesn't fit if(y < water_level || y > node_max.Y - 6) diff --git a/src/mapgen_v6.h b/src/mapgen_v6.h index 8f456fd3f..f4ffd25f3 100644 --- a/src/mapgen_v6.h +++ b/src/mapgen_v6.h @@ -132,7 +132,6 @@ public: virtual float baseTerrainLevelFromMap(v2s16 p); virtual float baseTerrainLevelFromMap(int index); - s16 find_ground_level(v2s16 p2d); s16 find_stone_level(v2s16 p2d); bool block_is_underground(u64 seed, v3s16 blockpos); s16 find_ground_level_from_noise(u64 seed, v2s16 p2d, s16 precision); diff --git a/src/mapgen_v7.cpp b/src/mapgen_v7.cpp index 6daa5fc6a..2439c95b3 100644 --- a/src/mapgen_v7.cpp +++ b/src/mapgen_v7.cpp @@ -52,7 +52,7 @@ NoiseParams nparams_v7_def_terrain_persist = NoiseParams nparams_v7_def_height_select = {0.5, 0.5, v3f(250.0, 250.0, 250.0), 4213, 5, 0.69}; NoiseParams nparams_v7_def_ridge = - {0.5, 1.0, v3f(100.0, 100.0, 100.0), 6467, 4, 0.75}; + {0, 1.0, v3f(100.0, 100.0, 100.0), 6467, 4, 0.75}; /* NoiseParams nparams_v6_def_beach = {0.0, 1.0, v3f(250.0, 250.0, 250.0), 59420, 3, 0.50}; @@ -121,15 +121,19 @@ int MapgenV7::getGroundLevelAtPoint(v2s16 p) { Biome *b = bmgr->getBiome(heat, humidity, groundlevel); s16 y = groundlevel; - if (y > water_level) { - int iters = 1024; // don't even bother iterating more than 1024 times.. - while (iters--) { - float ridgenoise = NoisePerlin3D(noise_ridge->np, p.X, y, p.Y, seed); - if (ridgenoise * (float)(y * y) < 15.0) - break; - y--; - } + int iters = 1024; // don't even bother iterating more than 64 times.. + while (iters--) { + if (y <= water_level) + break; + + float ridgenoise = NoisePerlin3D(noise_ridge->np, p.X, y, p.Y, seed); + if (ridgenoise * (float)(y * y) < 15.0) + break; + + y--; } + if (iters == 0) + printf("iters exhausted at %d %d\n", p.X, p.Y); return y + b->top_depth; } @@ -182,15 +186,24 @@ void MapgenV7::makeChunk(BlockMakeData *data) { generateTerrain(); carveRidges(); + + if (flags & MG_CAVES) + generateCaves(stone_surface_max_y); - generateCaves(stone_surface_max_y); addTopNodes(); + + updateHeightmap(node_min, node_max); if (flags & MG_DUNGEONS) { DungeonGen dgen(ndef, data->seed, water_level); dgen.generate(vm, blockseed, full_node_min, full_node_max); } + for (size_t i = 0; i != emerge->decorations.size(); i++) { + Decoration *deco = emerge->decorations[i]; + deco->placeDeco(this, blockseed + i, node_min, node_max); + } + for (size_t i = 0; i != emerge->ores.size(); i++) { Ore *ore = emerge->ores[i]; ore->placeOre(this, blockseed + i, node_min, node_max); diff --git a/src/mapgen_v7.h b/src/mapgen_v7.h index b6b03689d..d7177862d 100644 --- a/src/mapgen_v7.h +++ b/src/mapgen_v7.h @@ -67,9 +67,7 @@ public: v3s16 full_node_min; v3s16 full_node_max; - s16 *heightmap; s16 *ridge_heightmap; - u8 *biomemap; Noise *noise_terrain_base; Noise *noise_terrain_alt; diff --git a/src/script/lua_api/luaapi.cpp b/src/script/lua_api/luaapi.cpp index 180a44b26..75139861b 100644 --- a/src/script/lua_api/luaapi.cpp +++ b/src/script/lua_api/luaapi.cpp @@ -43,6 +43,14 @@ struct EnumString ModApiBasic::es_OreType[] = {0, NULL}, }; +struct EnumString ModApiBasic::es_DecorationType[] = +{ + {DECO_SIMPLE, "simple"}, + {DECO_SCHEMATIC, "schematic"}, + {DECO_LSYSTEM, "lsystem"}, + {0, NULL}, +}; + ModApiBasic::ModApiBasic() : ModApiBase() { } @@ -92,6 +100,7 @@ bool ModApiBasic::Initialize(lua_State* L,int top) { retval &= API_FCT(rollback_revert_actions_by); retval &= API_FCT(register_ore); + retval &= API_FCT(register_decoration); return retval; } @@ -162,21 +171,21 @@ int ModApiBasic::l_register_biome(lua_State *L) } enum BiomeTerrainType terrain = (BiomeTerrainType)getenumfield(L, index, - "terrain_type", es_BiomeTerrainType, BIOME_TERRAIN_NORMAL); + "terrain_type", es_BiomeTerrainType, BIOME_TERRAIN_NORMAL); Biome *b = bmgr->createBiome(terrain); - b->name = getstringfield_default(L, index, "name", ""); - b->top_nodename = getstringfield_default(L, index, "top_node", ""); - b->top_depth = getintfield_default(L, index, "top_depth", 0); + b->name = getstringfield_default(L, index, "name", ""); + b->top_nodename = getstringfield_default(L, index, "top_node", ""); + b->top_depth = getintfield_default(L, index, "top_depth", 0); b->filler_nodename = getstringfield_default(L, index, "filler_node", ""); - b->filler_height = getintfield_default(L, index, "filler_height", 0); - b->height_min = getintfield_default(L, index, "height_min", 0); - b->height_max = getintfield_default(L, index, "height_max", 0); - b->heat_point = getfloatfield_default(L, index, "heat_point", 0.); - b->humidity_point = getfloatfield_default(L, index, "humidity_point", 0.); + b->filler_height = getintfield_default(L, index, "filler_height", 0); + b->height_min = getintfield_default(L, index, "height_min", 0); + b->height_max = getintfield_default(L, index, "height_max", 0); + b->heat_point = getfloatfield_default(L, index, "heat_point", 0.); + b->humidity_point = getfloatfield_default(L, index, "humidity_point", 0.); - b->flags = 0; //reserved - b->c_top = CONTENT_IGNORE; + b->flags = 0; //reserved + b->c_top = CONTENT_IGNORE; b->c_filler = CONTENT_IGNORE; verbosestream << "register_biome: " << b->name << std::endl; bmgr->addBiome(b); @@ -184,8 +193,6 @@ int ModApiBasic::l_register_biome(lua_State *L) return 0; } - - // setting_set(name, value) int ModApiBasic::l_setting_set(lua_State *L) { @@ -650,4 +657,111 @@ int ModApiBasic::l_register_ore(lua_State *L) return 0; } +// register_decoration({lots of stuff}) +int ModApiBasic::l_register_decoration(lua_State *L) +{ + int index = 1; + luaL_checktype(L, index, LUA_TTABLE); + + EmergeManager *emerge = getServer(L)->getEmergeManager(); + BiomeDefManager *bdef = emerge->biomedef; + + enum DecorationType decotype = (DecorationType)getenumfield(L, index, + "deco_type", es_DecorationType, -1); + if (decotype == -1) { + errorstream << "register_decoration: unrecognized " + "decoration placement type"; + return 0; + } + + Decoration *deco = createDecoration(decotype); + if (!deco) { + errorstream << "register_decoration: decoration placement type " + << decotype << " not implemented"; + return 0; + } + + deco->c_place_on = CONTENT_IGNORE; + deco->place_on_name = getstringfield_default(L, index, "place_on", "ignore"); + deco->divlen = getintfield_default(L, index, "divlen", 8); + deco->fill_ratio = getfloatfield_default(L, index, "fill_ratio", 0.02); + + lua_getfield(L, index, "noise_params"); + deco->np = read_noiseparams(L, -1); + lua_pop(L, 1); + + lua_getfield(L, index, "biomes"); + if (lua_istable(L, -1)) { + lua_pushnil(L); + while (lua_next(L, -2)) { + const char *s = lua_tostring(L, -1); + u8 biomeid = bdef->getBiomeIdByName(s); + if (biomeid) + deco->biomes.insert(biomeid); + + lua_pop(L, 1); + } + lua_pop(L, 1); + } + + switch (decotype) { + case DECO_SIMPLE: { + DecoSimple *dsimple = (DecoSimple *)deco; + dsimple->c_deco = CONTENT_IGNORE; + dsimple->c_spawnby = CONTENT_IGNORE; + dsimple->spawnby_name = getstringfield_default(L, index, "spawn_by", "air"); + dsimple->deco_height = getintfield_default(L, index, "height", 1); + dsimple->deco_height_max = getintfield_default(L, index, "height_max", 0); + dsimple->nspawnby = getintfield_default(L, index, "num_spawn_by", -1); + + lua_getfield(L, index, "decoration"); + if (lua_istable(L, -1)) { + lua_pushnil(L); + while (lua_next(L, -2)) { + const char *s = lua_tostring(L, -1); + std::string str(s); + dsimple->decolist_names.push_back(str); + + lua_pop(L, 1); + } + } else if (lua_isstring(L, -1)) { + dsimple->deco_name = std::string(lua_tostring(L, -1)); + } else { + dsimple->deco_name = std::string("air"); + } + lua_pop(L, 1); + + if (dsimple->deco_height <= 0) { + errorstream << "register_decoration: simple decoration height" + " must be greater than 0" << std::endl; + delete dsimple; + return 0; + } + + break; } + case DECO_SCHEMATIC: { + //DecoSchematic *decoschematic = (DecoSchematic *)deco; + + break; } + case DECO_LSYSTEM: { + //DecoLSystem *decolsystem = (DecoLSystem *)deco; + + break; } + } + + if (deco->divlen <= 0) { + errorstream << "register_decoration: divlen must be " + "greater than 0" << std::endl; + delete deco; + return 0; + } + + emerge->decorations.push_back(deco); + + verbosestream << "register_decoration: decoration '" << deco->getName() + << "' registered" << std::endl; + return 0; +} + + ModApiBasic modapibasic_prototype; diff --git a/src/script/lua_api/luaapi.h b/src/script/lua_api/luaapi.h index 9623502c2..d03c14117 100644 --- a/src/script/lua_api/luaapi.h +++ b/src/script/lua_api/luaapi.h @@ -45,9 +45,6 @@ private: // get_server_status() static int l_get_server_status(lua_State *L); - // register_biome_groups({frequencies}) - static int l_register_biome_groups(lua_State *L); - // register_biome({lots of stuff}) static int l_register_biome(lua_State *L); @@ -130,9 +127,15 @@ private: // rollback_revert_actions_by(actor, seconds) -> bool, log messages static int l_rollback_revert_actions_by(lua_State *L); + // register_ore(oredesc) static int l_register_ore(lua_State *L); + + // register_decoration(deco) + static int l_register_decoration(lua_State *L); static struct EnumString es_OreType[]; + static struct EnumString es_DecorationType[]; + };