X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fmapgen.cpp;h=95a2f4029e326133735af3ebb302edac50a163b6;hb=2a01050a0cf0826f25240e2cb407535394ee360f;hp=34ac3a0173f5dc672138cbf31480817e23c8978b;hpb=dca28b9e12f74eae47e263d36a92078e875413e4;p=minetest.git diff --git a/src/mapgen.cpp b/src/mapgen.cpp index 34ac3a017..95a2f4029 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -1,18 +1,18 @@ /* -Minetest-c55 -Copyright (C) 2010-2011 celeron55, Perttu Ahola +Minetest +Copyright (C) 2010-2013 celeron55, Perttu Ahola This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. +GNU Lesser General Public License for more details. -You should have received a copy of the GNU General Public License along +You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ @@ -20,7 +20,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapgen.h" #include "voxel.h" #include "noise.h" +#include "biome.h" #include "mapblock.h" +#include "mapnode.h" #include "map.h" //#include "serverobject.h" #include "content_sao.h" @@ -28,2919 +30,1095 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "content_mapnode.h" // For content_mapnode_get_new_name #include "voxelalgorithms.h" #include "profiler.h" +#include "settings.h" // For g_settings #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}, + {"flat", MG_FLAT}, + {"light", MG_LIGHT}, + {NULL, 0} +}; -namespace mapgen -{ +FlagDesc flagdesc_ore[] = { + {"absheight", OREFLAG_ABSHEIGHT}, + {"scatter_noisedensity", OREFLAG_DENSITY}, + {"claylike_nodeisnt", OREFLAG_NODEISNT}, + {NULL, 0} +}; -/* - Some helper functions for the map generator -*/ +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} +}; -#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; +FlagDesc flagdesc_gennotify[] = { + {"dungeon", 1 << GENNOTIFY_DUNGEON}, + {"temple", 1 << GENNOTIFY_TEMPLE}, + {"cave_begin", 1 << GENNOTIFY_CAVE_BEGIN}, + {"cave_end", 1 << GENNOTIFY_CAVE_END}, + {"large_cave_begin", 1 << GENNOTIFY_LARGECAVE_BEGIN}, + {"large_cave_end", 1 << GENNOTIFY_LARGECAVE_END}, + {NULL, 0} +}; - vmanip.m_area.add_y(em, i, -1); - } - if(y >= y_nodes_min) - return y; - else - return y_nodes_min - 1; -} +/////////////////////////////////////////////////////////////////////////////// -#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); +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; } - if(y >= y_nodes_min) - return y; - else - return y_nodes_min - 1; } -#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); - } - if(y >= y_nodes_min) - return y; - else - return y_nodes_min - 1; +Ore::~Ore() { + delete np; + delete noise; } -#endif - -void make_tree(ManualMapVoxelManipulator &vmanip, v3s16 p0, - bool is_apple_tree, INodeDefManager *ndef) -{ - MapNode treenode(ndef->getId("mapgen_tree")); - MapNode leavesnode(ndef->getId("mapgen_leaves")); - MapNode applenode(ndef->getId("mapgen_apple")); - - s16 trunk_h = myrand_range(4, 5); - v3s16 p1 = p0; - for(s16 ii=0; ii leaves_d(new u8[leaves_a.getVolume()]); - Buffer leaves_d(leaves_a.getVolume()); - for(s32 i=0; igetId(ore_name); + if (ore == CONTENT_IGNORE) { + errorstream << "Ore::resolveNodeNames: ore node '" + << ore_name << "' not defined"; + ore = CONTENT_AIR; + wherein.push_back(CONTENT_AIR); + return; } } - // Blit leaves to vmanip - for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++) - for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++) - for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++) - { - v3s16 p(x,y,z); - p += p1; - if(vmanip.m_area.contains(p) == false) - continue; - u32 vi = vmanip.m_area.index(p); - if(vmanip.m_data[vi].getContent() != CONTENT_AIR - && vmanip.m_data[vi].getContent() != CONTENT_IGNORE) - continue; - u32 i = leaves_a.index(x,y,z); - if(leaves_d[i] == 1) { - bool is_apple = myrand_range(0,99) < 10; - if(is_apple_tree && is_apple) { - vmanip.m_data[vi] = applenode; - } else { - vmanip.m_data[vi] = leavesnode; - } + 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); } } } -#if 0 -static void make_jungletree(VoxelManipulator &vmanip, v3s16 p0, - INodeDefManager *ndef) -{ - MapNode treenode(ndef->getId("mapgen_jungletree")); - MapNode leavesnode(ndef->getId("mapgen_leaves")); - - for(s16 x=-1; x<=1; x++) - for(s16 z=-1; z<=1; z++) - { - if(myrand_range(0, 2) == 0) - continue; - v3s16 p1 = p0 + v3s16(x,0,z); - v3s16 p2 = p0 + v3s16(x,-1,z); - if(vmanip.m_area.contains(p2) - && vmanip.m_data[vmanip.m_area.index(p2)] == CONTENT_AIR) - vmanip.m_data[vmanip.m_area.index(p2)] = treenode; - else if(vmanip.m_area.contains(p1)) - vmanip.m_data[vmanip.m_area.index(p1)] = treenode; - } - - s16 trunk_h = myrand_range(8, 12); - v3s16 p1 = p0; - for(s16 ii=0; ii leaves_d(new u8[leaves_a.getVolume()]); - Buffer leaves_d(leaves_a.getVolume()); - for(s32 i=0; i= height_min); + if (flags & OREFLAG_ABSHEIGHT) + in_range |= (nmin.Y >= -height_max && nmax.Y <= -height_min) << 1; + if (!in_range) + return; - // Force leaves at near the end of the trunk - { - s16 d = 1; - for(s16 z=-d; z<=d; z++) - for(s16 y=-d; y<=d; y++) - for(s16 x=-d; x<=d; x++) - { - leaves_d[leaves_a.index(v3s16(x,y,z))] = 1; - } + 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; - // Add leaves randomly - for(u32 iii=0; iii<30; iii++) - { - s16 d = 1; + nmin.Y = ymin; + nmax.Y = ymax; + generate(mg->vm, mg->seed, blockseed, nmin, nmax); +} - v3s16 p( - myrand_range(leaves_a.MinEdge.X, leaves_a.MaxEdge.X-d), - myrand_range(leaves_a.MinEdge.Y, leaves_a.MaxEdge.Y-d), - myrand_range(leaves_a.MinEdge.Z, leaves_a.MaxEdge.Z-d) - ); - for(s16 z=0; z<=d; z++) - for(s16 y=0; y<=d; y++) - for(s16 x=0; x<=d; x++) - { - leaves_d[leaves_a.index(p+v3s16(x,y,z))] = 1; - } - } +void OreScatter::generate(ManualMapVoxelManipulator *vm, int seed, + u32 blockseed, v3s16 nmin, v3s16 nmax) { + PseudoRandom pr(blockseed); + MapNode n_ore(ore, 0, ore_param2); - // Blit leaves to vmanip - for(s16 z=leaves_a.MinEdge.Z; z<=leaves_a.MaxEdge.Z; z++) - for(s16 y=leaves_a.MinEdge.Y; y<=leaves_a.MaxEdge.Y; y++) - for(s16 x=leaves_a.MinEdge.X; x<=leaves_a.MaxEdge.X; x++) - { - v3s16 p(x,y,z); - p += p1; - if(vmanip.m_area.contains(p) == false) - continue; - u32 vi = vmanip.m_area.index(p); - if(vmanip.m_data[vi].getContent() != CONTENT_AIR - && vmanip.m_data[vi].getContent() != CONTENT_IGNORE) + 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; - u32 i = leaves_a.index(x,y,z); - if(leaves_d[i] == 1) - vmanip.m_data[vi] = leavesnode; - } -} -static void make_papyrus(VoxelManipulator &vmanip, v3s16 p0, - INodeDefManager *ndef) -{ - MapNode papyrusnode(ndef->getId("mapgen_papyrus")); + 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; - s16 trunk_h = myrand_range(2, 3); - v3s16 p1 = p0; - for(s16 ii=0; iim_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; + } } } -static void make_cactus(VoxelManipulator &vmanip, v3s16 p0, - INodeDefManager *ndef) -{ - MapNode cactusnode(ndef->getId("mapgen_cactus")); - s16 trunk_h = 3; - v3s16 p1 = p0; - for(s16 ii=0; iigetId("mapgen_cobble")); - } - { - v3s16 p = roomplace + v3s16(roomsize.X-1,y,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")); - } + if (!noise) { + int sx = nmax.X - nmin.X + 1; + int sz = nmax.Z - nmin.Z + 1; + noise = new Noise(np, 0, sx, sz); } - - // 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")); - } - } - - // 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) + 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; - vmanip.m_data[vi] = MapNode(ndef->getId("mapgen_cobble")); + + for (size_t ii = 0; ii < wherein.size(); ii++) + if (vm->m_data[i].getContent() == wherein[ii]) + vm->m_data[i] = n_ore; } } - - // Fill with air - for(s16 z=1; zgetId("mapgen_torch")); +Decoration::Decoration() { + mapseed = 0; + np = NULL; + fill_ratio = 0; + sidelen = 1; } -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; +Decoration::~Decoration() { + delete np; } -static v3s16 random_turn(PseudoRandom &random, v3s16 olddir) -{ - int turn = random.range(0,2); - v3s16 dir; - if(turn == 0) - { - // Go straight - dir = olddir; - } - else if(turn == 1) - // Turn right - dir = turn_xz(olddir, 0); - else - // Turn left - dir = turn_xz(olddir, 1); - return dir; + +void Decoration::resolveNodeNames(INodeDefManager *ndef) { + this->ndef = ndef; + + if (c_place_on == CONTENT_IGNORE) + c_place_on = ndef->getId(place_on_name); } -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; - } +void Decoration::placeDeco(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax) { + PseudoRandom ps(blockseed + 53); + int carea_size = nmax.X - nmin.X + 1; - partcount++; - if(partcount >= partlength) - { - partcount = 0; - - dir = random_turn(random, dir); - - partlength = random.range(1,length); - - make_stairs = 0; - if(random.next()%2 == 0 && partlength >= 3) - make_stairs = random.next()%2 ? 1 : -1; - } + // 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; } - result_place = p0; - result_dir = dir; -} -class RoomWalker -{ -public: + s16 divlen = carea_size / sidelen; + int area = sidelen * sidelen; - RoomWalker(VoxelManipulator &vmanip_, v3s16 pos, PseudoRandom &random, - INodeDefManager *ndef): - vmanip(vmanip_), - m_pos(pos), - m_random(random), - m_ndef(ndef) - { - randomizeDir(); - } + 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 + ); - void randomizeDir() - { - m_dir = rand_ortho_dir(m_random); - } + // 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); - void setPos(v3s16 pos) - { - m_pos = pos; - } + 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); - 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(); + 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; - } - 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(); + + 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; + } } - // Move there - m_pos = p; + + generate(mg, &ps, max_y, v3s16(x, y, z)); } - return false; } +} + + +#if 0 +void Decoration::placeCutoffs(Mapgen *mg, u32 blockseed, v3s16 nmin, v3s16 nmax) { + PseudoRandom pr(blockseed + 53); + std::vector handled_cutoffs; - bool findPlaceForRoomDoor(v3s16 roomsize, v3s16 &result_doorplace, - v3s16 &result_doordir, v3s16 &result_roomplace) + // Copy over the cutoffs we're interested in so we don't needlessly hold a lock { - for(s16 trycount=0; trycount<30; trycount++) - { - v3s16 doorplace; - v3s16 doordir; - bool r = findPlaceForDoor(doorplace, doordir); - if(r == false) + 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; - 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; z nmax.Y) continue; - } - result_doorplace = doorplace; - result_doordir = doordir; - result_roomplace = roomplace; - return true; + + handled_cutoffs.push_back(cutoff); } - return false; } -private: - VoxelManipulator &vmanip; - v3s16 m_pos; - v3s16 m_dir; - PseudoRandom &m_random; - INodeDefManager *m_ndef; -}; + // 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)); + } -static void make_dungeon1(VoxelManipulator &vmanip, PseudoRandom &random, - INodeDefManager *ndef) -{ - v3s16 areasize = vmanip.m_area.getExtent(); - v3s16 roomsize; - v3s16 roomplace; - - /* - Find place for first room - */ - bool fits = false; - for(u32 i=0; i<100; i++) + // Remove cutoffs that were handled from the cutoff list { - roomsize = v3s16(random.range(4,8),random.range(4,6),random.range(4,8)); - roomplace = vmanip.m_area.MinEdge + v3s16( - random.range(0,areasize.X-roomsize.X-1), - random.range(0,areasize.Y-roomsize.Y-1), - random.range(0,areasize.Z-roomsize.Z-1)); - /* - Check that we're not putting the room to an unknown place, - otherwise it might end up floating in the air - */ - fits = true; - for(s16 z=1; z::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); } } - if(fits) - break; } - // No place found - if(fits == false) - return; - - /* - Stores the center position of the last room made, so that - a new corridor can be started from the last room instead of - the new room, if chosen so. - */ - v3s16 last_room_center = roomplace+v3s16(roomsize.X/2,1,roomsize.Z/2); - - u32 room_count = random.range(2,7); - for(u32 i=0; igetId("mapgen_torch")); - // Quit if last room - if(i == room_count-1) - 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; +void DecoSimple::resolveNodeNames(INodeDefManager *ndef) { + Decoration::resolveNodeNames(ndef); - if(start_in_last_room) - { - walker_start_place = last_room_center; + if (c_deco == CONTENT_IGNORE && !decolist_names.size()) { + 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; } - else - { - walker_start_place = room_center; - // Store center of current room as the last one - last_room_center = room_center; + } + if (c_spawnby == CONTENT_IGNORE) { + c_spawnby = ndef->getId(spawnby_name); + if (c_spawnby == CONTENT_IGNORE) { + errorstream << "DecoSimple::resolveNodeNames: spawnby node '" + << spawnby_name << "' not defined" << std::endl; + nspawnby = -1; + c_spawnby = CONTENT_AIR; } - - // 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; - + 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); } } -#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; + +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; } - 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")); + + 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; + + 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); } } -#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); +int DecoSimple::getHeight() { + return (deco_height_max > 0) ? deco_height_max : deco_height; } -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); +std::string DecoSimple::getName() { + return deco_name; } -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; + +DecoSchematic::DecoSchematic() { + node_names = NULL; + schematic = NULL; + slice_probs = NULL; + flags = 0; + size = v3s16(0, 0, 0); } -/* - 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); +DecoSchematic::~DecoSchematic() { + delete node_names; + delete []schematic; + delete []slice_probs; } -/* - 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); -} +void DecoSchematic::resolveNodeNames(INodeDefManager *ndef) { + Decoration::resolveNodeNames(ndef); -#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; -} + if (filename.empty()) + return; -/* - 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; - } - } + 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); } - - // This is more like the actual ground level - level += dec[i-1]/2; - return level; -} + for (int i = 0; i != size.X * size.Y * size.Z; i++) + schematic[i].setContent(c_nodes[schematic[i].getContent()]); -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; + delete node_names; + node_names = NULL; } -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; -} -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 +void DecoSchematic::generate(Mapgen *mg, PseudoRandom *pr, s16 max_y, v3s16 p) { + ManualMapVoxelManipulator *vm = mg->vm; -// 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; -} + 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; -#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 = pow(b, 7); - 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; - } - //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;\ + s16 sx = size.X; + s16 sy = size.Y; + s16 sz = size.Z; + + int i_start, i_step_x, i_step_z; + switch (rot) { + case ROTATE_90: + i_start = sx - 1; + i_step_x = zstride; + i_step_z = -xstride; + SWAP(s16, sx, sz); + break; + case ROTATE_180: + i_start = zstride * (sz - 1) + sx - 1; + i_step_x = -xstride; + i_step_z = -zstride; + break; + case ROTATE_270: + i_start = zstride * (sz - 1); + i_step_x = -zstride; + i_step_z = xstride; + SWAP(s16, sx, sz); + break; + default: + i_start = 0; + i_step_x = xstride; + i_step_z = zstride; } - 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) - 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); - } + s16 y_map = p.Y; + for (s16 y = 0; y != sy; y++) { + if (slice_probs[y] != MTSCHEM_PROB_ALWAYS && + myrand_range(1, 255) > slice_probs[y]) + continue; + + for (s16 z = 0; z != sz; z++) { + u32 i = z * i_step_z + y * ystride + i_start; + for (s16 x = 0; x != sx; x++, i += i_step_x) { + u32 vi = vm->m_area.index(p.X + x, y_map, p.Z + z); + if (!vm->m_area.contains(vi)) + continue; + + if (schematic[i].getContent() == CONTENT_IGNORE) + continue; + + if (schematic[i].param1 == MTSCHEM_PROB_NEVER) + continue; + + if (!force_placement) { + content_t c = vm->m_data[vi].getContent(); + if (c != CONTENT_AIR && c != CONTENT_IGNORE) + continue; } - vmanip.m_area.add_y(em, i, 1); + + if (schematic[i].param1 != MTSCHEM_PROB_ALWAYS && + myrand_range(1, 255) > schematic[i].param1) + continue; + + vm->m_data[vi] = schematic[i]; + vm->m_data[vi].param1 = 0; + + if (rot) + vm->m_data[vi].rotateAlongYAxis(ndef, rot); } } + y_map++; } -#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 = "<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; + 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; - MapNode addnode(c_dirt); - BiomeType bt = get_biome(data->seed, p2d); + v3s16 bp1 = getNodeBlockPos(p); + v3s16 bp2 = getNodeBlockPos(p + s - v3s16(1,1,1)); + vm->initialEmerge(bp1, bp2); - if(bt == BT_DESERT) - addnode = MapNode(c_desert_sand); + blitToVManip(p, vm, rot, true); - 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); - } - } + std::map lighting_modified_blocks; + std::map modified_blocks; + vm->blitBackAll(&modified_blocks); - /* - 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); - } + // 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); - /* - 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++; + 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); - vmanip.m_area.add_y(em, i, 1); - } - } + map->dispatchEvent(&event); +} - } - }//timer1 -#endif +bool DecoSchematic::loadSchematicFile() { + content_t cignore = CONTENT_IGNORE; + bool have_cignore = false; - /* - 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; x1 MTSCHEM_FILE_VER_HIGHEST_READ) { + errorstream << "loadSchematicFile: unsupported schematic " + "file version" << std::endl; + return false; + } - 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); - } + size = readV3S16(is); - // 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; - - vmanip.m_area.add_y(em, i, -1); - } + delete []slice_probs; + slice_probs = new u8[size.Y]; + if (version >= 3) { + for (int y = 0; y != size.Y; y++) + slice_probs[y] = readU8(is); + } else { + for (int y = 0; y != size.Y; y++) + slice_probs[y] = MTSCHEM_PROB_ALWAYS; + } - // 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; - } - } + int nodecount = size.X * size.Y * size.Z; - /*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; - } + u16 nidmapcount = readU16(is); - // Drop mud on side - - 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) - 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; - } + 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); + } + + delete []schematic; + schematic = new MapNode[nodecount]; + MapNode::deSerializeBulk(is, SER_FMT_VER_HIGHEST_READ, schematic, + nodecount, 2, 2, true); + + if (version == 1) { // fix up the probability values + for (int i = 0; i != nodecount; i++) { + if (schematic[i].param1 == 0) + schematic[i].param1 = MTSCHEM_PROB_ALWAYS; + if (have_cignore && schematic[i].getContent() == cignore) + schematic[i].param1 = MTSCHEM_PROB_NEVER; } } - + + return true; +} + + +/* + Minetest Schematic File Format + + All values are stored in big-endian byte order. + [u32] signature: 'MTSM' + [u16] version: 3 + [u16] size X + [u16] size Y + [u16] size Z + For each Y: + [u8] slice probability value + [Name-ID table] Name ID Mapping Table + [u16] name-id count + For each name-id mapping: + [u16] name length + [u8[]] name + ZLib deflated { + For each node in schematic: (for z, y, x) + [u16] content + For each node in schematic: + [u8] probability of occurance (param1) + For each node in schematic: + [u8] param2 } - }//timer1 -#endif + Version changes: + 1 - Initial version + 2 - Fixed messy never/always place; 0 probability is now never, 0xFF is always + 3 - Added y-slice probabilities; this allows for variable height structures +*/ +void DecoSchematic::saveSchematicFile(INodeDefManager *ndef) { + std::ostringstream ss(std::ios_base::binary); - } // Aging loop - /*********************** - END OF AGING LOOP - ************************/ + writeU32(ss, MTSCHEM_FILE_SIGNATURE); // signature + writeU16(ss, MTSCHEM_FILE_VER_HIGHEST_WRITE); // version + writeV3S16(ss, size); // schematic size - /* - Add top and bottom side of water to transforming_liquid queue - */ + for (int y = 0; y != size.Y; y++) // Y slice probabilities + writeU8(ss, slice_probs[y]); - 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; - } - } + std::vector usednodes; + int nodecount = size.X * size.Y * size.Z; + build_nnlist_and_update_ids(schematic, nodecount, &usednodes); - vmanip.m_area.add_y(em, i, -1); - } - } - } + 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 - /* - Grow grass - */ + // compressed bulk node data + MapNode::serializeBulk(ss, SER_FMT_VER_HIGHEST_WRITE, schematic, + nodecount, 2, 2, true); - 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) - break; - vmanip.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); - } - } + fs::safeWriteToFile(filename, ss.str()); +} - /* - Generate some trees - */ - assert(central_area_size.X == central_area_size.Z); - { - // 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; i 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 - make_tree(vmanip, p, false, ndef); - } + +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(); + + 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); } +} -#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); - } +bool DecoSchematic::getSchematicFromMap(Map *map, v3s16 p1, v3s16 p2) { + ManualMapVoxelManipulator *vm = new ManualMapVoxelManipulator(map); + + v3s16 bp1 = getNodeBlockPos(p1); + v3s16 bp2 = getNodeBlockPos(p2); + vm->initialEmerge(bp1, bp2); + + size = p2 - p1 + 1; + + slice_probs = new u8[size.Y]; + for (s16 y = 0; y != size.Y; y++) + slice_probs[y] = MTSCHEM_PROB_ALWAYS; + + 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; } } - /* - Add mud and sand and others underground (in place of stone) - */ + delete vm; + return true; +} - 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); - } +void DecoSchematic::applyProbabilities(v3s16 p0, + std::vector > *plist, + std::vector > *splist) { + + 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); } } - /* - 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); - } - } - } + for (size_t i = 0; i != splist->size(); i++) { + s16 y = (*splist)[i].first - p0.Y; + slice_probs[y] = (*splist)[i].second; } +} - /* - Add NC - */ - { - PseudoRandom ncrandom(blockseed+9324342); - if(ncrandom.range(0, 1000) == 0 && blockpos.Y <= -3) - { - make_nc(vmanip, ncrandom, ndef); - } + +/////////////////////////////////////////////////////////////////////////////// + + +Mapgen::Mapgen() { + seed = 0; + water_level = 0; + generating = false; + id = -1; + vm = NULL; + ndef = NULL; + heightmap = NULL; + biomemap = NULL; + + for (unsigned int i = 0; i != NUM_GEN_NOTIFY; i++) + gen_notifications[i] = new std::vector; +} + + +Mapgen::~Mapgen() { + for (unsigned int i = 0; i != NUM_GEN_NOTIFY; i++) + delete gen_notifications[i]; +} + + +// 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); } - - /* - Add top and bottom side of water to transforming_liquid queue - */ + return (y >= y_nodes_min) ? y : y_nodes_min - 1; +} - 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); - } - } +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; +} - /* - 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) - { - // Determine whether to have clay in the sand here - double claynoise = noise2d_perlin( - 0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500, - data->seed+4321, 6, 0.95) + 0.5; - - have_clay = (y <= WATER_LEVEL) && (y >= WATER_LEVEL-2) && ( - ((claynoise > 0) && (claynoise < 0.04) && (current_depth == 0)) || - ((claynoise > 0) && (claynoise < 0.12) && (current_depth == 1)) - ); - if (have_clay) - vmanip.m_data[i] = MapNode(c_clay); - else - 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); - } - } - } +void Mapgen::updateHeightmap(v3s16 nmin, v3s16 nmax) { + if (!heightmap) + return; - /* - 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) + //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; - /* - 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) + if (y == nmin.Y - 1 && heightmap[index] < nmin.Y) continue; - { - u32 i = vmanip->m_area.index(p); - MapNode *n = &vmanip->m_data[i]; + heightmap[index] = y; + } + } + //printf("updateHeightmap: %dus\n", t.stop()); +} - 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); - } - } - } +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; - /* - 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; + 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. no need to add every with liquid_finite + if (isliquid != wasliquid && (!rare || !(rarecnt++ % 36))) + trans_liquid->push_back(v3s16(x, y, z)); + + wasliquid = isliquid; + vm->m_area.add_y(em, i, -1); } } + } +} -#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); +void Mapgen::setLighting(v3s16 nmin, v3s16 nmax, u8 light) { + ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG); + VoxelArea a(nmin, nmax); + + 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; } -#endif } +} - /* - 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; - } - } - } +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]; + + 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); + lightSpread(a, p - v3s16(0, 0, 1), light); + lightSpread(a, p - v3s16(0, 1, 0), light); + lightSpread(a, p - v3s16(1, 0, 0), light); +} + + +void Mapgen::calcLighting(v3s16 nmin, v3s16 nmax) { + VoxelArea a(nmin, nmax); + bool block_is_underground = (water_level >= nmax.Y); + + ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG); + //TimeTaker t("updateLighting"); + + // 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; + } else if ((vm->m_data[i].param1 & 0x0F) != LIGHT_SUN) { + continue; } - } - /* - 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; im_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; + n.param1 = LIGHT_SUN; + vm->m_area.add_y(em, i, -1); } } - /* - Add iron - */ - u16 iron_amount = 8; - u16 iron_rareness = 60 / iron_amount; - if(iron_rareness == 0) - iron_rareness = 1; - if(mineralrandom.next()%iron_rareness == 0) - { - u16 a = mineralrandom.next() % 16; - u16 amount = iron_amount * a*a*a / 1000; - for(s16 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; + + 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); } } } } -#endif - /* - Calculate lighting - */ - { - ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", - SPT_AVG); - //VoxelArea a(node_min, node_max); - VoxelArea a(node_min-v3s16(1,0,1)*MAP_BLOCKSIZE, - node_max+v3s16(1,0,1)*MAP_BLOCKSIZE); - /*VoxelArea a(node_min-v3s16(1,0,1)*MAP_BLOCKSIZE/2, - node_max+v3s16(1,0,1)*MAP_BLOCKSIZE/2);*/ - enum LightBank banks[2] = {LIGHTBANK_DAY, LIGHTBANK_NIGHT}; - for(int i=0; i<2; i++) - { - enum LightBank bank = banks[i]; + //printf("updateLighting: %dms\n", t.stop()); +} - core::map light_sources; - core::map unlight_from; - 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 +void Mapgen::calcLightingOld(v3s16 nmin, v3s16 nmax) { + enum LightBank banks[2] = {LIGHTBANK_DAY, LIGHTBANK_NIGHT}; + VoxelArea a(nmin, nmax); + bool block_is_underground = (water_level > nmax.Y); + bool sunlight = !block_is_underground; - vmanip.unspreadLight(bank, unlight_from, light_sources, ndef); + ScopeProfiler sp(g_profiler, "EmergeThread: mapgen lighting update", SPT_AVG); - vmanip.spreadLight(bank, light_sources, ndef); - } - } -} + for (int i = 0; i < 2; i++) { + enum LightBank bank = banks[i]; + std::set light_sources; + std::map unlight_from; -BlockMakeData::BlockMakeData(): - no_op(false), - vmanip(NULL), - seed(0), - nodedef(NULL) -{} + voxalgo::clearLightAndCollectSources(*vm, a, bank, ndef, + light_sources, unlight_from); + voxalgo::propagateSunlight(*vm, a, sunlight, light_sources, ndef); -BlockMakeData::~BlockMakeData() -{ - delete vmanip; + vm->unspreadLight(bank, unlight_from, light_sources, ndef); + vm->spreadLight(bank, light_sources, ndef); + } } - -}; // namespace mapgen - -