void MapgenV6::growGrass() // Add surface nodes { MapNode n_dirt_with_grass(c_dirt_with_grass); MapNode n_dirt_with_snow(c_dirt_with_snow); MapNode n_snowblock(c_snowblock); MapNode n_snow(c_snow); MapNode n_dirt(c_dirt); v3s16 em = vm->m_area.getExtent(); u32 index = 0; for (s16 z = full_node_min.Z; z <= full_node_max.Z; z++) for (s16 x = full_node_min.X; x <= full_node_max.X; x++, index++) { // 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; { u32 i = vm->m_area.index(x, node_max.Y, z); s16 y; // Go to ground level for (y = node_max.Y; y >= full_node_min.Y; y--) { MapNode &n = vm->m_data[i]; if (ndef->get(n).param_type != CPT_LIGHT || ndef->get(n).liquid_type != LIQUID_NONE || n.getContent() == c_ice) break; vm->m_area.add_y(em, i, -1); } surface_y = (y >= full_node_min.Y) ? y : full_node_min.Y; } BiomeV6Type bt = getBiome(index, v3POS(x, surface_y, z)); u32 i = vm->m_area.index(x, surface_y, z); content_t c = vm->m_data[i].getContent(); if (m_emerge->env->m_use_weather && c == c_dirt) { int heat = m_emerge->env->getServerMap().updateBlockHeat(m_emerge->env, v3POS(x, surface_y, z), nullptr, &heat_cache); vm->m_data[i] = (heat < -10 ? n_dirt_with_snow : (heat < -5 || heat > 50) ? n_dirt : n_dirt_with_grass); } else if (surface_y >= water_level - 20) { if (bt == BT_TAIGA && c == c_dirt) { vm->m_data[i] = n_snowblock; vm->m_area.add_y(em, i, -1); vm->m_data[i] = n_dirt_with_snow; } else if (bt == BT_TUNDRA) { if (c == c_dirt) { vm->m_data[i] = n_dirt_with_snow; } else if (c == c_stone && surface_y < node_max.Y) { vm->m_area.add_y(em, i, 1); vm->m_data[i] = n_snow; } } else if (c == c_dirt) { vm->m_data[i] = n_dirt_with_grass; } } } }
virtual void trigger(ServerEnvironment *env, v3POS p, MapNode n, u32 active_object_count, u32 active_object_count_wider, MapNode neighbor, bool activate) { ServerMap *map = &env->getServerMap(); if (map->transforming_liquid_size() > map->m_liquid_step_flow) return; if ( map->getNodeTry(p - v3POS(0, 1, 0 )).getContent() != CONTENT_AIR // below && map->getNodeTry(p - v3POS(1, 0, 0 )).getContent() != CONTENT_AIR // right && map->getNodeTry(p - v3POS(-1, 0, 0 )).getContent() != CONTENT_AIR // left && map->getNodeTry(p - v3POS(0, 0, 1 )).getContent() != CONTENT_AIR // back && map->getNodeTry(p - v3POS(0, 0, -1)).getContent() != CONTENT_AIR // front ) return; map->transforming_liquid_push_back(p); }
void MapgenV6::addMud() { // 15ms @cs=8 //TimeTaker timer1("add mud"); MapNode n_dirt(c_dirt), n_gravel(c_gravel); MapNode n_sand(c_sand), n_desert_sand(c_desert_sand); MapNode addnode; u32 index = 0; for (s16 z = node_min.Z; z <= node_max.Z; z++) for (s16 x = node_min.X; x <= node_max.X; x++, index++) { // Randomize mud amount s16 mud_add_amount = getMudAmount(index) / 2.0 + 0.5; // Find ground level s16 surface_y = find_stone_level(v2s16(x, z)); /////////////////optimize this! // Handle area not found if (surface_y == vm->m_area.MinEdge.Y - 1) continue; BiomeV6Type bt = getBiome(v3POS(x, surface_y, z)); addnode = (bt == BT_DESERT) ? n_desert_sand : n_dirt; if (bt == BT_DESERT && surface_y + mud_add_amount <= water_level + 1) { addnode = n_sand; } else if (mud_add_amount <= 0) { mud_add_amount = 1 - mud_add_amount; addnode = n_gravel; } else if (bt != BT_DESERT && getHaveBeach(index) && surface_y + mud_add_amount <= water_level + 2) { addnode = n_sand; } if ((bt == BT_DESERT || bt == BT_TUNDRA) && surface_y > 20) mud_add_amount = MYMAX(0, mud_add_amount - (surface_y - 20) / 5); /* If topmost node is grass, change it to mud. It might be if it was // flown to there from a neighboring chunk and then converted. u32 i = vm->m_area.index(x, surface_y, z); if (vm->m_data[i].getContent() == c_dirt_with_grass) vm->m_data[i] = n_dirt;*/ // Add mud on ground s16 mudcount = 0; v3s16 em = vm->m_area.getExtent(); s16 y_start = surface_y + 1; u32 i = vm->m_area.index(x, y_start, z); for (s16 y = y_start; y <= node_max.Y; y++) { if (mudcount >= mud_add_amount) break; vm->m_data[i] = addnode; mudcount++; vm->m_area.add_y(em, i, 1); } } }
CircuitElement::CircuitElement(u32 element_id) : m_pos(v3POS(0, 0, 0)), m_prev_input_state(0), m_current_input_state(0), m_next_input_state(0), m_current_output_state(0) { m_element_id = element_id; for(int i = 0; i < 6; ++i) { m_faces[i].is_connected = false; } }
int MapgenIndev::generateGround() { //TimeTaker timer1("Generating ground level"); MapNode n_air(CONTENT_AIR), n_water_source(c_water_source); MapNode n_stone(c_stone), n_desert_stone(c_desert_stone); MapNode n_ice(c_ice), n_dirt(c_dirt),n_sand(c_sand), n_gravel(c_gravel), n_lava_source(c_lava_source); int stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT; u32 index = 0; for (s16 z = node_min.Z; z <= node_max.Z; z++) for (s16 x = node_min.X; x <= node_max.X; x++, index++) { // Surface height s16 surface_y = (s16)baseTerrainLevelFromMap(index); // Log it if (surface_y > stone_surface_max_y) stone_surface_max_y = surface_y; auto bt = getBiome(index, v3POS(x, surface_y, z)); s16 heat = m_emerge->env->m_use_weather ? m_emerge->env->getServerMap().updateBlockHeat(m_emerge->env, v3POS(x,node_max.Y,z), nullptr, &heat_cache) : 0; // Fill ground with stone v3POS em = vm->m_area.getExtent(); u32 i = vm->m_area.index(x, node_min.Y, z); for (s16 y = node_min.Y; y <= node_max.Y; y++) { if (!vm->m_data[i]) { if (y <= surface_y) { int index3 = (z - node_min.Z) * zstride + (y - node_min.Y) * ystride + (x - node_min.X) * xstride; if (cave_noise_threshold && noise_cave_indev->result[index3] > cave_noise_threshold) { vm->m_data[i] = n_air; } else { auto n = (y > water_level - surface_y && bt == BT_DESERT) ? n_desert_stone : layers_get(index3); bool protect = n.getContent() != CONTENT_AIR; if (cave_noise_threshold && noise_cave_indev->result[index3] > cave_noise_threshold - 50) { vm->m_data[i] = protect ? n_stone : n; //cave shell without layers protect = true; } else { vm->m_data[i] = n; } if (protect) vm->m_flags[i] |= VOXELFLAG_CHECKED2; // no cave liquid } } else if (y <= water_level) { vm->m_data[i] = (heat < 0 && y > heat/3) ? n_ice : n_water_source; if (liquid_pressure && y <= 0) vm->m_data[i].addLevel(m_emerge->ndef, water_level - y, 1); } else { vm->m_data[i] = n_air; } } vm->m_area.add_y(em, i, 1); } } return stone_surface_max_y; }
virtual void trigger(ServerEnvironment *env, v3POS p, MapNode n, u32 active_object_count, u32 active_object_count_wider, MapNode neighbor, bool activate) { ServerMap *map = &env->getServerMap(); INodeDefManager *ndef = env->getGameDef()->ndef(); float heat = map->updateBlockHeat(env, p); content_t c = map->getNodeTry(p - v3POS(0, -1, 0 )).getContent(); // top int melt = ((ItemGroupList) ndef->get(n).groups)["melt"]; if (heat >= melt + 1 && (activate || heat >= melt + 40 || ((myrand_range(heat, melt + 40)) >= (c == CONTENT_AIR ? melt + 10 : melt + 20)))) { if (ndef->get(n.getContent()).liquid_type == LIQUID_FLOWING || ndef->get(n.getContent()).liquid_type == LIQUID_SOURCE) { c = map->getNodeTry(p - v3POS(0, 1, 0 )).getContent(); // below if (c == CONTENT_AIR || c == CONTENT_IGNORE) return; // do not melt when falling (dirt->dirt_with_grass on air) } n.freeze_melt(ndef, +1); map->setNode(p, n); env->nodeUpdate(p, 2); //enable after making FAST nodeupdate } }
void Map::copy_27_blocks_to_vm(MapBlock * block, VoxelManipulator & vmanip) { v3POS blockpos = block->getPos(); v3POS blockpos_nodes = blockpos * MAP_BLOCKSIZE; // Allocate this block + neighbors vmanip.clear(); VoxelArea voxel_area(blockpos_nodes - v3POS(1, 1, 1) * MAP_BLOCKSIZE, blockpos_nodes + v3POS(1, 1, 1) * MAP_BLOCKSIZE * 2 - v3POS(1, 1, 1)); vmanip.addArea(voxel_area); block->copyTo(vmanip); auto * map = block->getParent(); for(u16 i = 0; i < 26; i++) { v3POS bp = blockpos + g_26dirs[i]; MapBlock *b = map->getBlockNoCreateNoEx(bp); if(b) b->copyTo(vmanip); } }
int MapgenV6::generateGround() { //TimeTaker timer1("Generating ground level"); MapNode n_air(CONTENT_AIR), n_water_source(c_water_source); MapNode n_stone(c_stone), n_desert_stone(c_desert_stone); MapNode n_ice(c_ice); int stone_surface_max_y = -MAX_MAP_GENERATION_LIMIT; u32 index = 0; for (s16 z = node_min.Z; z <= node_max.Z; z++) for (s16 x = node_min.X; x <= node_max.X; x++, index++) { // Surface height s16 surface_y = (s16)baseTerrainLevelFromMap(index); // Log it if (surface_y > stone_surface_max_y) stone_surface_max_y = surface_y; BiomeV6Type bt = getBiome(v3POS(x, node_min.Y, z)); s16 heat = m_emerge->env->m_use_weather ? m_emerge->env->getServerMap().updateBlockHeat(m_emerge->env, v3POS(x,node_max.Y,z), nullptr, &heat_cache) : 0; // Fill ground with stone v3s16 em = vm->m_area.getExtent(); u32 i = vm->m_area.index(x, node_min.Y, z); for (s16 y = node_min.Y; y <= node_max.Y; y++) { if (vm->m_data[i].getContent() == CONTENT_IGNORE) { if (y <= surface_y) { vm->m_data[i] = (y >= MGV6_DESERT_STONE_BASE - surface_y && bt == BT_DESERT) ? n_desert_stone : n_stone; } else if (y <= water_level) { vm->m_data[i] = ((heat < 0 && y > heat/3) || (y >= MGV6_ICE_BASE && bt == BT_TUNDRA)) ? n_ice : n_water_source; if (liquid_pressure && y <= 0) vm->m_data[i].addLevel(m_emerge->ndef, water_level - y, 1); } else { vm->m_data[i] = n_air; } } vm->m_area.add_y(em, i, 1); } } return stone_surface_max_y; }
int Mapgen_features::float_islands_generate(const v3POS & node_min, const v3POS & node_max, int min_y, MMVManip *vm) { int generated = 0; if (node_min.Y < min_y) return generated; // originally from http://forum.minetest.net/viewtopic.php?id=4776 float RAR = 0.8 * farscale(0.4, node_min.Y); // 0.4; // Island rarity in chunk layer. -0.4 = thick layer with holes, 0 = 50%, 0.4 = desert rarity, 0.7 = very rare. float AMPY = 24; // 24; // Amplitude of island centre y variation. float TGRAD = 24; // 24; // Noise gradient to create top surface. Tallness of island top. float BGRAD = 24; // 24; // Noise gradient to create bottom surface. Tallness of island bottom. v3POS p0(node_min.X, node_min.Y, node_min.Z); float xl = node_max.X - node_min.X; float yl = node_max.Y - node_min.Y; float zl = node_max.Z - node_min.Z; u32 zstride = xl + y_offset; float midy = node_min.Y + yl * 0.5; u32 index = 0; for (int z1 = 0; z1 <= zl; ++z1) for (int y1 = 0; y1 <= yl; ++y1) for (int x1 = 0; x1 <= xl; ++x1, ++index) { int y = y1 + node_min.Y; u32 index2d = z1 * zstride + x1; float noise3 = noise_float_islands3->result[index2d]; float pmidy = midy + noise3 / 1.5 * AMPY; float noise1 = noise_float_islands1->result[index]; float offset = y > pmidy ? (y - pmidy) / TGRAD : (pmidy - y) / BGRAD; float noise1off = noise1 - offset - RAR; if (noise1off > 0 && noise1off < 0.7) { float noise2 = noise_float_islands2->result[index]; if (noise2 - noise1off > -0.7) { v3POS p = p0 + v3POS(x1, y1, z1); u32 i = vm->m_area.index(p); if (!vm->m_area.contains(i)) continue; // Cancel if not air if (vm->m_data[i].getContent() != CONTENT_AIR) continue; vm->m_data[i] = layers_get(index); ++generated; } } } return generated; }
virtual void trigger(ServerEnvironment *env, v3POS p, MapNode n, u32 active_object_count, u32 active_object_count_wider, MapNode neighbor, bool activate) { ServerMap *map = &env->getServerMap(); INodeDefManager *ndef = env->getGameDef()->ndef(); float heat = map->updateBlockHeat(env, p); //heater = rare content_t c = map->getNodeTry(p - v3POS(0, -1, 0 )).getContent(); // top //more chance to freeze if air at top static int water_level = g_settings->getS16("water_level"); bool top_liquid = ndef->get(n).liquid_type > LIQUID_NONE && p.Y > water_level; int freeze = ((ItemGroupList) ndef->get(n).groups)["freeze"]; if (heat <= freeze - 1 && ((!top_liquid && (activate || (heat <= freeze - 50))) || heat <= freeze - 50 || (myrand_range(freeze - 50, heat) <= (freeze + (top_liquid ? -42 : c == CONTENT_AIR ? -10 : -40))))) { content_t c_self = n.getContent(); // making freeze not annoying, do not freeze random blocks in center of ocean // todo: any block not water (dont freeze _source near _flowing) bool allow = activate || heat < freeze - 40; // todo: make for(...) if (!allow) { c = map->getNodeTry(p - v3POS(0, 1, 0 )).getContent(); // below if (c == CONTENT_AIR || c == CONTENT_IGNORE) if (ndef->get(n.getContent()).liquid_type == LIQUID_FLOWING || ndef->get(n.getContent()).liquid_type == LIQUID_SOURCE) return; // do not freeze when falling if (c != c_self && c != CONTENT_IGNORE) allow = 1; if (!allow) { c = map->getNodeTry(p - v3POS(1, 0, 0 )).getContent(); // right if (c != c_self && c != CONTENT_IGNORE) allow = 1; if (!allow) { c = map->getNodeTry(p - v3POS(-1, 0, 0 )).getContent(); // left if (c != c_self && c != CONTENT_IGNORE) allow = 1; if (!allow) { c = map->getNodeTry(p - v3POS(0, 0, 1 )).getContent(); // back if (c != c_self && c != CONTENT_IGNORE) allow = 1; if (!allow) { c = map->getNodeTry(p - v3POS(0, 0, -1)).getContent(); // front if (c != c_self && c != CONTENT_IGNORE) allow = 1; } } } } } if (allow) { n.freeze_melt(ndef, -1); map->setNode(p, n); } } }
void MapgenV7::generateBaseTerrain(s16 *stone_surface_min_y, s16 *stone_surface_max_y) { MapNode n_air(CONTENT_AIR); MapNode n_stone(c_stone); MapNode n_water(c_water_source); MapNode n_ice(c_ice); v3s16 em = vm->m_area.getExtent(); s16 surface_min_y = MAX_MAP_GENERATION_LIMIT; s16 surface_max_y = -MAX_MAP_GENERATION_LIMIT; u32 index = 0; for (s16 z = node_min.Z; z <= node_max.Z; z++) for (s16 x = node_min.X; x <= node_max.X; x++, index++) { float surface_height = baseTerrainLevelFromMap(index); s16 surface_y = (s16)surface_height; heightmap[index] = surface_y; ridge_heightmap[index] = surface_y; if (surface_y < surface_min_y) surface_min_y = surface_y; if (surface_y > surface_max_y) surface_max_y = surface_y; s16 heat = m_emerge->env->m_use_weather ? m_emerge->env->getServerMap().updateBlockHeat(m_emerge->env, v3POS(x,node_max.Y,z), nullptr, &heat_cache) : 0; u32 i = vm->m_area.index(x, node_min.Y - 1, z); for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) { if (vm->m_data[i].getContent() == CONTENT_IGNORE) { if (y <= surface_y) { int index3 = (z - node_min.Z) * zstride + (y - node_min.Y + 1) * ystride + (x - node_min.X); vm->m_data[i] = layers_get(index3); } else if (y <= water_level) { vm->m_data[i] = (heat < 0 && y > heat/3) ? n_ice : n_water; if (liquid_pressure && y <= 0) vm->m_data[i].addLevel(m_emerge->ndef, water_level - y, 1); } else vm->m_data[i] = n_air; } vm->m_area.add_y(em, i, 1); } } *stone_surface_min_y = surface_min_y; *stone_surface_max_y = surface_max_y; }
bool Map::propagateSunlight(v3POS pos, std::set<v3POS> & light_sources, bool remove_light) { MapBlock *block = getBlockNoCreateNoEx(pos); INodeDefManager *nodemgr = m_gamedef->ndef(); // Whether the sunlight at the top of the bottom block is valid bool block_below_is_valid = true; v3POS pos_relative = block->getPosRelative(); for(s16 x = 0; x < MAP_BLOCKSIZE; ++x) { for(s16 z = 0; z < MAP_BLOCKSIZE; ++z) { bool no_sunlight = false; // Check if node above block has sunlight MapNode n = getNode(pos_relative + v3POS(x, MAP_BLOCKSIZE, z)); if (n) { if(n.getLight(LIGHTBANK_DAY, m_gamedef->ndef()) != LIGHT_SUN) { no_sunlight = true; } } else { // NOTE: This makes over-ground roofed places sunlighted // Assume sunlight, unless is_underground==true if(block->getIsUnderground()) { no_sunlight = true; } else { MapNode n = block->getNode(v3POS(x, MAP_BLOCKSIZE - 1, z)); if(n && m_gamedef->ndef()->get(n).sunlight_propagates == false) no_sunlight = true; } // NOTE: As of now, this just would make everything dark. // No sunlight here //no_sunlight = true; } s16 y = MAP_BLOCKSIZE - 1; // This makes difference to diminishing in water. //bool stopped_to_solid_object = false; u8 current_light = no_sunlight ? 0 : LIGHT_SUN; for(; y >= 0; --y) { v3POS pos(x, y, z); MapNode n = block->getNode(pos); if(current_light == 0) { // Do nothing } else if(current_light == LIGHT_SUN && nodemgr->get(n).sunlight_propagates) { // Do nothing: Sunlight is continued } else if(nodemgr->get(n).light_propagates == false) { // A solid object is on the way. //stopped_to_solid_object = true; // Light stops. current_light = 0; } else { // Diminish light current_light = diminish_light(current_light); } u8 old_light = n.getLight(LIGHTBANK_DAY, nodemgr); if(current_light > old_light || remove_light) { n.setLight(LIGHTBANK_DAY, current_light, nodemgr); block->setNode(pos, n); } if(diminish_light(current_light) != 0) { light_sources.insert(pos_relative + pos); } } // Whether or not the block below should see LIGHT_SUN bool sunlight_should_go_down = (current_light == LIGHT_SUN); /* If the block below hasn't already been marked invalid: Check if the node below the block has proper sunlight at top. If not, the block below is invalid. Ignore non-transparent nodes as they always have no light */ if(block_below_is_valid) { MapNode n = getNode(pos_relative + v3POS(x, -1, z)); if (n) { if(nodemgr->get(n).light_propagates) { if(n.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN && sunlight_should_go_down == false) block_below_is_valid = false; else if(n.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN && sunlight_should_go_down == true) block_below_is_valid = false; } } else { // Just no block below, no need to panic. } } } } return block_below_is_valid; }
int MapgenMath::generateTerrain() { MapNode n_ice(c_ice); u32 index = 0; v3POS em = vm->m_area.getExtent(); auto zstride_1d = csize.X * (csize.Y + 1); /* debug v3f vec0 = (v3f(node_min.X, node_min.Y, node_min.Z) - center) * scale ; errorstream << " X=" << node_min.X << " Y=" << node_min.Y << " Z=" << node_min.Z //<< " N="<< mengersponge(vec0.X, vec0.Y, vec0.Z, distance, iterations) << " N=" << (*func)(vec0.X, vec0.Y, vec0.Z, distance, iterations) << " Sc=" << scale << " gen=" << params["generator"].asString() << " J=" << Json::FastWriter().write(params) << std::endl; */ //errorstream << Json::StyledWriter().write( mg_params->params ).c_str()<< std::endl; //errorstream << " iterations="<<iterations<< " scale="<<scale <<" invert="<<invert<< std::endl; #if USE_MANDELBULBER v3f vec0(node_min.X, node_min.Y, node_min.Z); vec0 = (vec0 - center) * scale; /* errorstream << " X=" << node_min.X << " Y=" << node_min.Y << " Z=" << node_min.Z << " N=" << Compute<normal_mode>(CVector3(vec0.X, vec0.Y, vec0.Z), mg_params->par) //<<" F="<< Compute<fake_AO>(CVector3(node_min.X,node_min.Y,node_min.Z), par) //<<" L="<<node_min.getLength()<< " -="<<node_min.getLength() - Compute<normal_mode>(CVector3(node_min.X,node_min.Y,node_min.Z), par) << " Sc=" << scale << " internal=" << internal << std::endl; */ #endif double d = 0; for (s16 z = node_min.Z; z <= node_max.Z; z++) { for (s16 x = node_min.X; x <= node_max.X; x++, index++) { s16 heat = m_emerge->env->m_use_weather ? m_emerge->env->getServerMap().updateBlockHeat(m_emerge->env, v3POS(x,node_max.Y,z), nullptr, &heat_cache) : 0; u32 i = vm->m_area.index(x, node_min.Y, z); for (s16 y = node_min.Y; y <= node_max.Y; y++) { v3f vec = (v3f(x, y, z) - center) * scale ; if (invert_xy) std::swap(vec.X, vec.Y); if (invert_yz) std::swap(vec.Y, vec.Z); #if USE_MANDELBULBER if (!internal) d = Compute<normal_mode>(CVector3(vec.X, vec.Y, vec.Z), mg_params->par); else #endif if (internal) d = (*func)(vec.X, vec.Y, vec.Z, scale.X, iterations); if ((!invert && d > 0) || (invert && d == 0) ) { if (!vm->m_data[i]) { //vm->m_data[i] = (y > water_level + biome->filler) ? // MapNode(biome->c_filler) : n_stone; if (invert) { int index3 = (z - node_min.Z) * zstride_1d + (y - node_min.Y) * ystride + (x - node_min.X); vm->m_data[i] = Mapgen_features::layers_get(index3); } else { vm->m_data[i] = layers_get(d, result_max); } // vm->m_data[i] = (y > water_level + biome->filler) ? // MapNode(biome->c_filler) : layers_get(d, result_max); } } else if (y <= water_level) { vm->m_data[i] = (heat < 0 && y > heat/3) ? n_ice : n_water; } else { vm->m_data[i] = n_air; } vm->m_area.add_y(em, i, 1); } } } return 0; }
}; struct NodeNeighbor { MapNode node; NeighborType type; v3POS pos; content_t content; bool liquid; //can liquid bool infinity; int weight; int drop; //drop by liquid }; const v3POS liquid_flow_dirs[7] = { // +right, +top, +back v3POS( 0, -1, 0), // 0 bottom v3POS( 0, 0, 0), // 1 self v3POS( 0, 0, 1), // 2 back v3POS( 0, 0, -1), // 3 front v3POS( 1, 0, 0), // 4 right v3POS(-1, 0, 0), // 5 left v3POS( 0, 1, 0) // 6 top }; // when looking around we must first check self node for correct type definitions const s8 liquid_explore_map[7] = {1, 0, 6, 2, 3, 4, 5}; const s8 liquid_random_map[4][7] = { {0, 1, 2, 3, 4, 5, 6}, {0, 1, 4, 3, 5, 2, 6}, {0, 1, 3, 5, 4, 2, 6}, {0, 1, 5, 3, 2, 4, 6}
MgStoneType MapgenV7::generateBiomes(float *heat_map, float *humidity_map) { v3s16 em = vm->m_area.getExtent(); u32 index = 0; MgStoneType stone_type = STONE; for (s16 z = node_min.Z; z <= node_max.Z; z++) for (s16 x = node_min.X; x <= node_max.X; x++, index++) { Biome *biome = NULL; u16 depth_top = 0; u16 base_filler = 0; u16 depth_water_top = 0; u32 vi = vm->m_area.index(x, node_max.Y, z); // Check node at base of mapchunk above, either a node of a previously // generated mapchunk or if not, a node of overgenerated base terrain. content_t c_above = vm->m_data[vi + em.X].getContent(); bool air_above = c_above == CONTENT_AIR; bool water_above = c_above == c_water_source; // If there is air or water above enable top/filler placement, otherwise force // nplaced to stone level by setting a number exceeding any possible filler depth. u16 nplaced = (air_above || water_above) ? 0 : U16_MAX; s16 heat = m_emerge->env->m_use_weather ? m_emerge->env->getServerMap().updateBlockHeat(m_emerge->env, v3POS(x,node_max.Y,z), NULL, &heat_cache) : 0; for (s16 y = node_max.Y; y >= node_min.Y; y--) { content_t c = vm->m_data[vi].getContent(); bool cc_stone = (c != CONTENT_AIR && c != c_water_source && c != CONTENT_IGNORE); // Biome is recalculated each time an upper surface is detected while // working down a column. The selected biome then remains in effect for // all nodes below until the next surface and biome recalculation. // Biome is recalculated: // 1. At the surface of stone below air or water. // 2. At the surface of water below air. // 3. When stone or water is detected but biome has not yet been calculated. if ((cc_stone && (air_above || water_above || !biome)) || (c == c_water_source && (air_above || !biome))) { biome = bmgr->getBiome(heat_map[index], humidity_map[index], y); depth_top = biome->depth_top; base_filler = MYMAX(depth_top + biome->depth_filler + noise_filler_depth->result[index], 0); depth_water_top = biome->depth_water_top; // Detect stone type for dungeons during every biome calculation. // This is more efficient than detecting per-node and will not // miss any desert stone or sandstone biomes. if (biome->c_stone == c_desert_stone) stone_type = DESERT_STONE; else if (biome->c_stone == c_sandstone) stone_type = SANDSTONE; } if (cc_stone && biome && (c == biome->c_ice || c == biome->c_water || c == biome->c_water_top)) cc_stone = false; if (cc_stone) { content_t c_below = vm->m_data[vi - em.X].getContent(); // If the node below isn't solid, make this node stone, so that // any top/filler nodes above are structurally supported. // This is done by aborting the cycle of top/filler placement // immediately by forcing nplaced to stone level. if (c_below == CONTENT_AIR || c_below == c_water_source) nplaced = U16_MAX; if (nplaced < depth_top) { vm->m_data[vi] = MapNode( ((y < water_level) /* && (biome->c_top == c_dirt_with_grass)*/ ) ? biome->c_top : heat < -3 ? biome->c_top_cold : biome->c_top); nplaced++; } else if (nplaced < base_filler) { vm->m_data[vi] = MapNode(biome->c_filler); nplaced++; } else if (nplaced < (depth_top+base_filler+depth_water_top)){ vm->m_data[vi] = MapNode(biome->c_stone); } air_above = false; water_above = false; } else if (c == c_water_source) { bool ice = heat < 0 && y > water_level + heat/4; vm->m_data[vi] = MapNode((y > (s32)(water_level - depth_water_top)) ? (ice ? biome->c_ice : biome->c_water_top) : ice ? biome->c_ice : biome->c_water); nplaced = 0; // Enable top/filler placement for next surface air_above = false; water_above = true; } else if (c == CONTENT_AIR) { nplaced = 0; // Enable top/filler placement for next surface air_above = true; water_above = false; } else { // Possible various nodes overgenerated from neighbouring mapchunks nplaced = U16_MAX; // Disable top/filler placement air_above = false; water_above = false; } vm->m_area.add_y(em, vi, -1); } } return stone_type; }
void MapgenV7::generateRidgeTerrain() { if (node_max.Y < water_level) return; MapNode n_water(c_water_source); MapNode n_ice(c_ice); MapNode n_air(CONTENT_AIR); u32 index = 0; float width = 0.2; // TODO: figure out acceptable perlin noise values for (s16 z = node_min.Z; z <= node_max.Z; z++) for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) { u32 vi = vm->m_area.index(node_min.X, y, z); for (s16 x = node_min.X; x <= node_max.X; x++, index++, vi++) { int j = (z - node_min.Z) * csize.X + (x - node_min.X); if (heightmap[j] < water_level - 16) continue; float uwatern = noise_ridge_uwater->result[j] * 2; if (fabs(uwatern) > width) continue; float altitude = y - water_level; float height_mod = (altitude + 17) / 2.5; float width_mod = width - fabs(uwatern); float nridge = noise_ridge->result[index] * MYMAX(altitude, 0) / 7.0; if (nridge + width_mod * height_mod < 0.6) continue; if (y < ridge_heightmap[j]) ridge_heightmap[j] = y - 1; s16 heat = m_emerge->env->m_use_weather ? m_emerge->env->getServerMap().updateBlockHeat(m_emerge->env, v3POS(x,node_max.Y,z), NULL, &heat_cache) : 0; MapNode n_water_or_ice = (heat < 0 && y > water_level + heat/4) ? n_ice : n_water; vm->m_data[vi] = (y > water_level) ? n_air : n_water_or_ice; } } }
int RemoteClient::GetNextBlocks ( ServerEnvironment *env, EmergeManager * emerge, float dtime, double m_uptime, std::vector<PrioritySortedBlockTransfer> &dest) { DSTACK(FUNCTION_NAME); auto lock = lock_unique_rec(); if (!lock->owns_lock()) return 0; // Increment timers m_nothing_to_send_pause_timer -= dtime; m_nearest_unsent_reset_timer += dtime; m_time_from_building += dtime; if (m_nearest_unsent_reset) { m_nearest_unsent_reset = 0; m_nearest_unsent_reset_timer = 999; m_nothing_to_send_pause_timer = 0; m_time_from_building = 999; } if(m_nothing_to_send_pause_timer >= 0) return 0; Player *player = env->getPlayer(peer_id); // This can happen sometimes; clients and players are not in perfect sync. if(player == NULL) return 0; v3f playerpos = player->getPosition(); v3f playerspeed = player->getSpeed(); if(playerspeed.getLength() > 1000.0*BS) //cheater or bug, ignore him return 0; v3f playerspeeddir(0,0,0); if(playerspeed.getLength() > 1.0*BS) playerspeeddir = playerspeed / playerspeed.getLength(); // Predict to next block v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS; v3s16 center_nodepos = floatToInt(playerpos_predicted, BS); v3s16 center = getNodeBlockPos(center_nodepos); // Camera position and direction v3f camera_pos = player->getEyePosition(); v3f camera_dir = v3f(0,0,1); camera_dir.rotateYZBy(player->getPitch()); camera_dir.rotateXZBy(player->getYaw()); //infostream<<"camera_dir=("<<camera_dir<<")"<< " camera_pos="<<camera_pos<<std::endl; /* Get the starting value of the block finder radius. */ if(m_last_center != center) { m_last_center = center; m_nearest_unsent_reset_timer = 999; } if (m_last_direction.getDistanceFrom(camera_dir)>0.4) { // 1 = 90deg m_last_direction = camera_dir; m_nearest_unsent_reset_timer = 999; } /*infostream<<"m_nearest_unsent_reset_timer=" <<m_nearest_unsent_reset_timer<<std::endl;*/ // Reset periodically to workaround for some bugs or stuff if(m_nearest_unsent_reset_timer > 120.0) { m_nearest_unsent_reset_timer = 0; m_nearest_unsent_d = 0; m_nearest_unsent_reset = 0; //infostream<<"Resetting m_nearest_unsent_d for "<<peer_id<<std::endl; } //s16 last_nearest_unsent_d = m_nearest_unsent_d; s16 d_start = m_nearest_unsent_d; //infostream<<"d_start="<<d_start<<std::endl; static const u16 max_simul_sends_setting = g_settings->getU16 ("max_simultaneous_block_sends_per_client"); static const u16 max_simul_sends_usually = max_simul_sends_setting; /* Check the time from last addNode/removeNode. Decrease send rate if player is building stuff. */ static const auto full_block_send_enable_min_time_from_building = g_settings->getFloat("full_block_send_enable_min_time_from_building"); if(m_time_from_building < full_block_send_enable_min_time_from_building) { /* max_simul_sends_usually = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS; */ if(d_start<=1) d_start=2; m_nearest_unsent_reset_timer = 999; //magical number more than ^ other number 120 - need to reset d on next iteration } /* Number of blocks sending + number of blocks selected for sending */ u32 num_blocks_selected = 0; u32 num_blocks_sending = 0; /* next time d will be continued from the d from which the nearest unsent block was found this time. This is because not necessarily any of the blocks found this time are actually sent. */ s32 new_nearest_unsent_d = -1; static const auto max_block_send_distance = g_settings->getS16("max_block_send_distance"); s16 full_d_max = max_block_send_distance; if (wanted_range) { s16 wanted_blocks = wanted_range / MAP_BLOCKSIZE + 1; if (wanted_blocks < full_d_max) full_d_max = wanted_blocks; } s16 d_max = full_d_max; static const s16 d_max_gen = g_settings->getS16("max_block_generate_distance"); // Don't loop very much at a time s16 max_d_increment_at_time = 10; if(d_max > d_start + max_d_increment_at_time) d_max = d_start + max_d_increment_at_time; /*if(d_max_gen > d_start+2) d_max_gen = d_start+2;*/ //infostream<<"Starting from "<<d_start<<std::endl; s32 nearest_emerged_d = -1; s32 nearest_emergefull_d = -1; s32 nearest_sent_d = -1; //bool queue_is_full = false; f32 speed_in_blocks = (playerspeed/(MAP_BLOCKSIZE*BS)).getLength(); int blocks_occlusion_culled = 0; static const bool server_occlusion = g_settings->getBool("server_occlusion"); bool occlusion_culling_enabled = server_occlusion; auto cam_pos_nodes = floatToInt(playerpos, BS); auto nodemgr = env->getGameDef()->getNodeDefManager(); MapNode n; { #if !ENABLE_THREADS auto lock = env->getServerMap().m_nothread_locker.lock_shared_rec(); #endif n = env->getMap().getNodeTry(cam_pos_nodes); } if(n && nodemgr->get(n).solidness == 2) occlusion_culling_enabled = false; unordered_map_v3POS<bool> occlude_cache; s16 d; for(d = d_start; d <= d_max; d++) { /*errorstream<<"checking d="<<d<<" for " <<server->getPlayerName(peer_id)<<std::endl;*/ //infostream<<"RemoteClient::SendBlocks(): d="<<d<<" d_start="<<d_start<<" d_max="<<d_max<<" d_max_gen="<<d_max_gen<<std::endl; std::vector<v3POS> list; if (d > 2 && d == d_start && m_nearest_unsent_reset_timer != 999) { // oops, again magic number from up ^ list.push_back(v3POS(0,0,0)); } bool can_skip = d > 1; // Fast fall/move optimize. speed_in_blocks now limited to 6.4 if (speed_in_blocks>0.8 && d <= 2) { can_skip = false; if (d == 0) { for(s16 addn = 0; addn < (speed_in_blocks+1)*2; ++addn) list.push_back(floatToInt(playerspeeddir*addn, 1)); } else if (d == 1) { for(s16 addn = 0; addn < (speed_in_blocks+1)*1.5; ++addn) { list.push_back(floatToInt(playerspeeddir*addn, 1) + v3POS( 0, 0, 1)); // back list.push_back(floatToInt(playerspeeddir*addn, 1) + v3POS( -1, 0, 0)); // left list.push_back(floatToInt(playerspeeddir*addn, 1) + v3POS( 1, 0, 0)); // right list.push_back(floatToInt(playerspeeddir*addn, 1) + v3POS( 0, 0, -1)); // front } } else if (d == 2) { for(s16 addn = 0; addn < (speed_in_blocks+1)*1.5; ++addn) { list.push_back(floatToInt(playerspeeddir*addn, 1) + v3POS( -1, 0, 1)); // back left list.push_back(floatToInt(playerspeeddir*addn, 1) + v3POS( 1, 0, 1)); // left right list.push_back(floatToInt(playerspeeddir*addn, 1) + v3POS( -1, 0, -1)); // right left list.push_back(floatToInt(playerspeeddir*addn, 1) + v3POS( 1, 0, -1)); // front right } } } else { /* Get the border/face dot coordinates of a "d-radiused" box */ list = FacePositionCache::getFacePositions(d); } for(auto li=list.begin(); li!=list.end(); ++li) { v3POS p = *li + center; /* Send throttling - Don't allow too many simultaneous transfers - EXCEPT when the blocks are very close Also, don't send blocks that are already flying. */ // Start with the usual maximum u16 max_simul_dynamic = max_simul_sends_usually; // If block is very close, allow full maximum if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D) max_simul_dynamic = max_simul_sends_setting; // Don't select too many blocks for sending if (num_blocks_selected + num_blocks_sending >= max_simul_dynamic) { //queue_is_full = true; goto queue_full_break; } /* Do not go over-limit */ if (blockpos_over_limit(p)) continue; // If this is true, inexistent block will be made from scratch bool generate = d <= d_max_gen; { /*// Limit the generating area vertically to 2/3 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3) generate = false;*/ /* maybe good idea (if not use block culling) but brokes far (25+) area generate by flooding emergequeue with no generate blocks // Limit the send area vertically to 1/2 if(can_skip && abs(p.Y - center.Y) > full_d_max / 2) generate = false; */ } //infostream<<"d="<<d<<std::endl; /* Don't generate or send if not in sight FIXME This only works if the client uses a small enough FOV setting. The default of 72 degrees is fine. */ float camera_fov = ((fov+5)*M_PI/180) * 4./3.; if(can_skip && isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false) { continue; } /* Don't send already sent blocks */ unsigned int block_sent = 0; { auto lock = m_blocks_sent.lock_shared_rec(); block_sent = m_blocks_sent.find(p) != m_blocks_sent.end() ? m_blocks_sent.get(p) : 0; } if(block_sent > 0 && (/* (block_overflow && d>1) || */ block_sent + (d <= 2 ? 1 : d*d*d) > m_uptime)) { continue; } /* Check if map has this block */ MapBlock *block; { #if !ENABLE_THREADS auto lock = env->getServerMap().m_nothread_locker.lock_shared_rec(); #endif block = env->getMap().getBlockNoCreateNoEx(p); } bool surely_not_found_on_disk = false; bool block_is_invalid = false; if(block != NULL) { if (d > 3 && block->content_only == CONTENT_AIR) { continue; } if (block_sent > 0 && block_sent >= block->m_changed_timestamp) { continue; } if (occlusion_culling_enabled) { ScopeProfiler sp(g_profiler, "SMap: Occusion calls"); //Occlusion culling auto cpn = p*MAP_BLOCKSIZE; // No occlusion culling when free_move is on and camera is // inside ground cpn += v3POS(MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2, MAP_BLOCKSIZE/2); float step = 1; float stepfac = 1.3; float startoff = 5; float endoff = -MAP_BLOCKSIZE; v3POS spn = cam_pos_nodes + v3POS(0,0,0); s16 bs2 = MAP_BLOCKSIZE/2 + 1; u32 needed_count = 1; #if !ENABLE_THREADS auto lock = env->getServerMap().m_nothread_locker.lock_shared_rec(); #endif //VERY BAD COPYPASTE FROM clientmap.cpp! if( d >= 1 && occlusion_culling_enabled && isOccluded(&env->getMap(), spn, cpn + v3POS(0,0,0), step, stepfac, startoff, endoff, needed_count, nodemgr, occlude_cache) && isOccluded(&env->getMap(), spn, cpn + v3POS(bs2,bs2,bs2), step, stepfac, startoff, endoff, needed_count, nodemgr, occlude_cache) && isOccluded(&env->getMap(), spn, cpn + v3POS(bs2,bs2,-bs2), step, stepfac, startoff, endoff, needed_count, nodemgr, occlude_cache) && isOccluded(&env->getMap(), spn, cpn + v3POS(bs2,-bs2,bs2), step, stepfac, startoff, endoff, needed_count, nodemgr, occlude_cache) && isOccluded(&env->getMap(), spn, cpn + v3POS(bs2,-bs2,-bs2), step, stepfac, startoff, endoff, needed_count, nodemgr, occlude_cache) && isOccluded(&env->getMap(), spn, cpn + v3POS(-bs2,bs2,bs2), step, stepfac, startoff, endoff, needed_count, nodemgr, occlude_cache) && isOccluded(&env->getMap(), spn, cpn + v3POS(-bs2,bs2,-bs2), step, stepfac, startoff, endoff, needed_count, nodemgr, occlude_cache) && isOccluded(&env->getMap(), spn, cpn + v3POS(-bs2,-bs2,bs2), step, stepfac, startoff, endoff, needed_count, nodemgr, occlude_cache) && isOccluded(&env->getMap(), spn, cpn + v3POS(-bs2,-bs2,-bs2), step, stepfac, startoff, endoff, needed_count, nodemgr, occlude_cache) ) { //infostream<<" occlusion player="<<cam_pos_nodes<<" d="<<d<<" block="<<cpn<<" total="<<blocks_occlusion_culled<<"/"<<num_blocks_selected<<std::endl; g_profiler->add("SMap: Occlusion skip", 1); blocks_occlusion_culled++; continue; } } // Reset usage timer, this block will be of use in the future. block->resetUsageTimer(); if (block->getLightingExpired()) { //env->getServerMap().lighting_modified_blocks.set(p, nullptr); env->getServerMap().lighting_modified_add(p, d); } if (block->lighting_broken > 0 && (block_sent || d > 0)) continue; // Block is valid if lighting is up-to-date and data exists if(block->isValid() == false) { block_is_invalid = true; } if(block->isGenerated() == false) { continue; } /* If block is not close, don't send it unless it is near ground level. Block is near ground level if night-time mesh differs from day-time mesh. */ /* if(d >= 4) { if(block->getDayNightDiff() == false) continue; } */ } /* If block has been marked to not exist on disk (dummy) and generating new ones is not wanted, skip block. */ if(generate == false && surely_not_found_on_disk == true) { // get next one. continue; } /* Add inexistent block to emerge queue. */ if(block == NULL || surely_not_found_on_disk || block_is_invalid) { //infostream<<"start gen d="<<d<<" p="<<p<<" notfound="<<surely_not_found_on_disk<<" invalid="<< block_is_invalid<<" block="<<block<<" generate="<<generate<<std::endl; if (emerge->enqueueBlockEmerge(peer_id, p, generate)) { if (nearest_emerged_d == -1) nearest_emerged_d = d; } else { if (nearest_emergefull_d == -1) nearest_emergefull_d = d; goto queue_full_break; } // get next one. continue; } if(nearest_sent_d == -1) nearest_sent_d = d; /* Add block to send queue */ PrioritySortedBlockTransfer q((float)d, p, peer_id); dest.push_back(q); num_blocks_selected += 1; } } queue_full_break: //infostream<<"Stopped at "<<d<<" d_start="<<d_start<< " d_max="<<d_max<<" nearest_emerged_d="<<nearest_emerged_d<<" nearest_emergefull_d="<<nearest_emergefull_d<< " new_nearest_unsent_d="<<new_nearest_unsent_d<< " sel="<<num_blocks_selected<< "+"<<num_blocks_sending << " culled=" << blocks_occlusion_culled <<" cEN="<<occlusion_culling_enabled<<std::endl; num_blocks_selected += num_blocks_sending; if(!num_blocks_selected && d_start <= d) { //new_nearest_unsent_d = 0; m_nothing_to_send_pause_timer = 1.0; } // If nothing was found for sending and nothing was queued for // emerging, continue next time browsing from here if(nearest_emerged_d != -1){ new_nearest_unsent_d = nearest_emerged_d; } else if(nearest_emergefull_d != -1){ new_nearest_unsent_d = nearest_emergefull_d; } else { if(d > full_d_max){ new_nearest_unsent_d = 0; m_nothing_to_send_pause_timer = 1.0; } else { if(nearest_sent_d != -1) new_nearest_unsent_d = nearest_sent_d; else new_nearest_unsent_d = d; } } if(new_nearest_unsent_d != -1) m_nearest_unsent_d = new_nearest_unsent_d; return num_blocks_selected - num_blocks_sending; }
void CircuitElement::findConnectedWithFace(std::vector <std::pair <std::list<CircuitElement>::iterator, u8> >& connected, Map* map, INodeDefManager* ndef, v3POS pos, u8 face, std::map<v3POS, std::list<CircuitElement>::iterator>& pos_to_iterator, bool connected_faces[6]) { static v3POS directions[6] = {v3POS(0, 1, 0), v3POS(0, -1, 0), v3POS(1, 0, 0), v3POS(-1, 0, 0), v3POS(0, 0, 1), v3POS(0, 0, -1), }; // First - wire pos, second - acceptable faces std::queue <std::pair <v3POS, u8> > q; v3POS current_pos, next_pos; MapNode next_node, current_node; // used[pos] = or of all faces, that are already processed std::map <v3POS, u8> used; u8 face_id = FACE_TO_SHIFT(face); connected_faces[face_id] = true; used[pos] = face; current_node = map->getNodeNoEx(pos); const ContentFeatures& first_node_features = ndef->get(current_node); face = rotateFace(current_node, first_node_features, face); face_id = FACE_TO_SHIFT(face); current_pos = pos + directions[face_id]; current_node = map->getNodeNoEx(current_pos); const ContentFeatures& current_node_features = ndef->get(current_node); u8 real_face = revRotateFace(current_node, current_node_features, face); u8 real_face_id = FACE_TO_SHIFT(real_face); if(current_node_features.is_wire || current_node_features.is_wire_connector) { q.push(std::make_pair(current_pos, current_node_features.wire_connections[real_face_id])); while(!q.empty()) { current_pos = q.front().first; u8 acceptable_faces = q.front().second; q.pop(); current_node = map->getNodeNoEx(current_pos); const ContentFeatures& current_node_features = ndef->get(current_node); for(int i = 0; i < 6; ++i) { u8 real_face = revRotateFace(current_node, current_node_features, SHIFT_TO_FACE(i)); if(acceptable_faces & real_face) { used[current_pos] |= real_face; next_pos = current_pos + directions[i]; next_node = map->getNodeNoEx(next_pos); const ContentFeatures& node_features = ndef->get(next_node); u8 next_real_face = revRotateFace(next_node, node_features, OPPOSITE_FACE(SHIFT_TO_FACE(i))); u8 next_real_shift = FACE_TO_SHIFT(next_real_face); // If start element, mark some of it's faces if(next_pos == pos) { connected_faces[next_real_shift] = true; } auto next_used_iterator = used.find(next_pos); bool is_part_of_circuit = node_features.is_wire_connector || node_features.is_circuit_element || (node_features.is_wire && (next_node.getContent() == current_node.getContent())); bool not_used = (next_used_iterator == used.end()) || !(next_used_iterator->second & next_real_face); if(is_part_of_circuit && not_used) { if(node_features.is_circuit_element) { connected.push_back(std::make_pair(pos_to_iterator[next_pos], next_real_shift)); } else { q.push(std::make_pair(next_pos, node_features.wire_connections[next_real_shift])); } if(next_used_iterator != used.end()) { next_used_iterator->second |= next_real_face; } else { used[next_pos] = next_real_face; } } } } } } else if(current_node_features.is_circuit_element) { connected.push_back(std::make_pair(pos_to_iterator[current_pos], OPPOSITE_SHIFT(real_face_id))); } }
void MapgenV6::placeTreesAndJungleGrass() { //TimeTaker t("placeTrees"); PseudoRandom grassrandom(blockseed + 53); content_t c_sand = ndef->getId("mapgen_sand"); content_t c_junglegrass = ndef->getId("mapgen_junglegrass"); // if we don't have junglegrass, don't place cignore... that's bad if (c_junglegrass == CONTENT_IGNORE) c_junglegrass = CONTENT_AIR; MapNode n_junglegrass(c_junglegrass); v3s16 em = vm->m_area.getExtent(); // Divide area into parts s16 div = 8; s16 sidelen = central_area_size.X / div; double area = sidelen * sidelen; // N.B. We must add jungle grass first, since tree leaves will // obstruct the ground, giving us a false ground level for (s16 z0 = 0; z0 < div; z0++) for (s16 x0 = 0; x0 < div; x0++) { // Center position of part of division v2s16 p2d_center( node_min.X + sidelen / 2 + sidelen * x0, node_min.Z + sidelen / 2 + sidelen * z0 ); // Minimum edge of part of division v2s16 p2d_min( node_min.X + sidelen * x0, node_min.Z + sidelen * z0 ); // Maximum edge of part of division v2s16 p2d_max( node_min.X + sidelen + sidelen * x0 - 1, node_min.Z + sidelen + sidelen * z0 - 1 ); // Get biome at center position of part of division BiomeV6Type bt = getBiome(v3POS(p2d_center.X, node_min.Y, p2d_center.Y)); // Amount of trees float humidity = getHumidity(v3POS(p2d_center.X, node_max.Y, p2d_center.Y)); s32 tree_count; if (bt == BT_JUNGLE || bt == BT_TAIGA || bt == BT_NORMAL) { tree_count = area * getTreeAmount(p2d_center) * ((humidity + 1)/2.0); if (bt == BT_JUNGLE) tree_count *= 4; } else { tree_count = 0; } if (node_max.Y < water_level) tree_count /= 2; // Add jungle grass if (bt == BT_JUNGLE) { u32 grass_count = 5 * humidity * tree_count; for (u32 i = 0; i < grass_count; i++) { s16 x = grassrandom.range(p2d_min.X, p2d_max.X); s16 z = grassrandom.range(p2d_min.Y, p2d_max.Y); /* wtf int mapindex = central_area_size.X * (z - node_min.Z) + (x - node_min.X); s16 y = heightmap[mapindex]; */ s16 y = findGroundLevelFull(v2s16(x, z)); if (y < water_level) continue; u32 vi = vm->m_area.index(x, y, z); // place on dirt_with_grass, since we know it is exposed to sunlight if (vm->m_data[vi].getContent() == c_dirt_with_grass) { vm->m_area.add_y(em, vi, 1); vm->m_data[vi] = n_junglegrass; } } } // Put trees in random places on part of division for (s32 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); /* wtf int mapindex = central_area_size.X * (z - node_min.Z) + (x - node_min.X); s16 y = heightmap[mapindex]; */ s16 y = findGroundLevelFull(v2s16(x, z)); // Don't make a tree under water level // Don't make a tree so high that it doesn't fit if (y > node_max.Y - 6) continue; v3s16 p(x, y, z); // Trees grow only on mud and grass and snowblock { u32 i = vm->m_area.index(p); content_t c = vm->m_data[i].getContent(); if (c != c_dirt && c != c_dirt_with_grass && c != c_dirt_with_snow && c != c_snowblock && (y >= water_level || c != c_sand)) continue; } p.Y++; // Make a tree if (y < water_level) { if (y < water_level - 20) // do not spawn trees in lakes treegen::make_cavetree(*vm, p, bt == BT_JUNGLE, ndef, myrand()); } else if (bt == BT_JUNGLE) { treegen::make_jungletree(*vm, p, ndef, myrand()); } else if (bt == BT_TAIGA) { treegen::make_pine_tree(*vm, p - v3s16(0, 1, 0), ndef, myrand()); } else if (bt == BT_NORMAL) { bool is_apple_tree = (myrand_range(0, 3) == 0) && getHaveAppleTree(v2s16(x, z)); treegen::make_tree(*vm, p, is_apple_tree, ndef, myrand()); } } } //printf("placeTreesAndJungleGrass: %dms\n", t.stop()); }
void MinimapUpdateThread::getMap(v3POS pos, s16 size, s16 scan_height, bool is_radar) { v3POS p(pos.X - size / 2, pos.Y, pos.Z - size / 2); v3POS blockpos_player, relpos; getNodeBlockPosWithOffset(pos, blockpos_player, relpos); for (s16 x = 0; x < size; x++) for (s16 z = 0; z < size; z++) { auto mmpixel = &data->minimap_scan[x + z * size]; mmpixel->air_count = 0; mmpixel->id = CONTENT_AIR; v3POS pos(p.X + x, p.Y, p.Z + z); v3POS blockpos_max, blockpos_min; getNodeBlockPosWithOffset(v3POS(pos.X, pos.Y - scan_height / 2, pos.Z), blockpos_min, relpos); getNodeBlockPosWithOffset(v3POS(pos.X, pos.Y + scan_height / 2, pos.Z), blockpos_max, relpos); s16 pixel_height = 0; s16 height = scan_height - MAP_BLOCKSIZE; v2POS top_block_xz(blockpos_max.X, blockpos_max.Z); if (!getmap_cache.count(top_block_xz)) { getmap_cache.emplace(std::piecewise_construct, std::forward_as_tuple(top_block_xz), std::forward_as_tuple()); auto & vec = getmap_cache[top_block_xz]; /* simple: for (auto i = blockpos_max.Y; i > blockpos_min.Y - 1; --i) { auto it = m_blocks_cache.find(v3POS(blockpos_max.X, i, blockpos_max.Z)); if (it == m_blocks_cache.end()) continue; vec.emplace(i, it->second); } */ int c = 0; for (auto i = blockpos_player.Y; i > blockpos_min.Y - 1; --i) { auto it = m_blocks_cache.find(v3POS(blockpos_max.X, i, blockpos_max.Z)); if (it == m_blocks_cache.end()) continue; vec.emplace(c++, it->second); } for (auto i = blockpos_max.Y; i > blockpos_player.Y; --i) { auto it = m_blocks_cache.find(v3POS(blockpos_max.X, i, blockpos_max.Z)); if (it == m_blocks_cache.end()) continue; vec.emplace(c++, it->second); } } //simple: for (auto & mmblock : getmap_cache[top_block_xz]) { for (auto & it : getmap_cache[top_block_xz]) { auto & mmblock = it.second; auto pixel = &mmblock->data[relpos.Z * MAP_BLOCKSIZE + relpos.X]; mmpixel->air_count += pixel->air_count; if (pixel->id != CONTENT_AIR) { pixel_height = height + pixel->height; mmpixel->id = pixel->id; mmpixel->height = pixel_height; if (!is_radar) break; } height -= MAP_BLOCKSIZE; } } }