+/*
+ Some helper functions for the map generator
+*/
+
+s16 find_ground_level(VoxelManipulator &vmanip, v2s16 p2d)
+{
+ 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(content_walkable(n.d))
+ break;
+
+ vmanip.m_area.add_y(em, i, -1);
+ }
+ if(y >= y_nodes_min)
+ return y;
+ else
+ return y_nodes_min;
+}
+
+s16 find_ground_level_clever(VoxelManipulator &vmanip, v2s16 p2d)
+{
+ 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(content_walkable(n.d)
+ && n.d != CONTENT_TREE
+ && n.d != CONTENT_LEAVES)
+ break;
+
+ vmanip.m_area.add_y(em, i, -1);
+ }
+ if(y >= y_nodes_min)
+ return y;
+ else
+ return y_nodes_min;
+}
+
+void make_tree(VoxelManipulator &vmanip, v3s16 p0)
+{
+ MapNode treenode(CONTENT_TREE);
+ MapNode leavesnode(CONTENT_LEAVES);
+
+ s16 trunk_h = myrand_range(3, 6);
+ v3s16 p1 = p0;
+ for(s16 ii=0; ii<trunk_h; ii++)
+ {
+ if(vmanip.m_area.contains(p1))
+ vmanip.m_data[vmanip.m_area.index(p1)] = treenode;
+ p1.Y++;
+ }
+
+ // p1 is now the last piece of the trunk
+ p1.Y -= 1;
+
+ VoxelArea leaves_a(v3s16(-2,-2,-2), v3s16(2,2,2));
+ //SharedPtr<u8> leaves_d(new u8[leaves_a.getVolume()]);
+ Buffer<u8> leaves_d(leaves_a.getVolume());
+ for(s32 i=0; i<leaves_a.getVolume(); i++)
+ leaves_d[i] = 0;
+
+ // 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;
+ }
+ }
+
+ // Add leaves randomly
+ for(u32 iii=0; iii<7; iii++)
+ {
+ s16 d = 1;
+
+ 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;
+ }
+ }
+
+ // 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].d != CONTENT_AIR)
+ continue;
+ u32 i = leaves_a.index(x,y,z);
+ if(leaves_d[i] == 1)
+ vmanip.m_data[vi] = leavesnode;
+ }
+}
+
+/*
+ Noise functions. Make sure seed is mangled differently in each one.
+*/
+
+// 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 zeroval = -0.3;
+ if(noise < zeroval)
+ return 0;
+ else
+ return 0.04 * (noise-zeroval) / (1.0-zeroval);
+}
+
+#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/500., 0.5+(float)p.Y/500.,
+ (seed>>32)+654879876, 6, 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>>27)+90340, 6, 0.69);
+ if(base2 > base)
+ base = base2;*/
+#if 1
+ // Higher ground level
+ double higher = (double)WATER_LEVEL + 25. + 35. * noise2d_perlin(
+ 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
+ seed+85039, 5, 0.69);
+ //higher = 30; // For debugging
+
+ // Limit higher to at least base
+ if(higher < base)
+ higher = base;
+
+ // Steepness factor of cliffs
+ double b = 1.0 + 1.0 * noise2d_perlin(
+ 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
+ seed-932, 7, 0.7);
+ b = rangelim(b, 0.0, 1000.0);
+ b = pow(b, 5);
+ b *= 7;
+ b = rangelim(b, 3.0, 1000.0);
+ //dstream<<"b="<<b<<std::endl;
+ //double b = 20;
+
+ // Offset to more low
+ double a_off = -0.2;
+ // High/low selector
+ /*double a = 0.5 + b * (a_off + noise2d_perlin(
+ 0.5+(float)p.X/500., 0.5+(float)p.Y/500.,
+ seed-359, 6, 0.7));*/
+ double a = (double)0.5 + b * (a_off + noise2d_perlin(
+ 0.5+(float)p.X/250., 0.5+(float)p.Y/250.,
+ seed-359, 5, 0.60));
+ // Limit
+ a = rangelim(a, 0.0, 1.0);
+
+ //dstream<<"a="<<a<<std::endl;
+
+ double h = base*(1.0-a) + higher*a;
+#else
+ double h = base;
+#endif
+ return h;
+}
+
+double get_mud_add_amount(u64 seed, v2s16 p)
+{
+ return ((float)AVERAGE_MUD_AMOUNT + 3.0 * noise2d_perlin(
+ 0.5+(float)p.X/200, 0.5+(float)p.Y/200,
+ seed+91013, 3, 0.55));
+}
+
+/*
+ Adds random objects to block, depending on the content of the block
+*/
+void addRandomObjects(MapBlock *block)
+{
+ for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
+ for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
+ {
+ bool last_node_walkable = false;
+ for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
+ {
+ v3s16 p(x0,y0,z0);
+ MapNode n = block->getNodeNoEx(p);
+ if(n.d == CONTENT_IGNORE)
+ continue;
+ if(content_features(n.d).liquid_type != LIQUID_NONE)
+ continue;
+ if(content_features(n.d).walkable)
+ {
+ last_node_walkable = true;
+ continue;
+ }
+ if(last_node_walkable)
+ {
+ // If block contains light information
+ if(content_features(n.d).param_type == CPT_LIGHT)
+ {
+ if(n.getLight(LIGHTBANK_DAY) <= 3)
+ {
+ if(myrand() % 300 == 0)
+ {
+ v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
+ pos_f.Y -= BS*0.4;
+ ServerActiveObject *obj = new RatSAO(NULL, 0, pos_f);
+ std::string data = obj->getStaticData();
+ StaticObject s_obj(obj->getType(),
+ obj->getBasePosition(), data);
+ // Add some
+ block->m_static_objects.insert(0, s_obj);
+ block->m_static_objects.insert(0, s_obj);
+ block->m_static_objects.insert(0, s_obj);
+ block->m_static_objects.insert(0, s_obj);
+ block->m_static_objects.insert(0, s_obj);
+ block->m_static_objects.insert(0, s_obj);
+ delete obj;
+ }
+ if(myrand() % 300 == 0)
+ {
+ v3f pos_f = intToFloat(p+block->getPosRelative(), BS);
+ pos_f.Y -= BS*0.4;
+ ServerActiveObject *obj = new Oerkki1SAO(NULL,0,pos_f);
+ std::string data = obj->getStaticData();
+ StaticObject s_obj(obj->getType(),
+ obj->getBasePosition(), data);
+ // Add one
+ block->m_static_objects.insert(0, s_obj);
+ delete obj;
+ }
+ }
+ }
+ }
+ last_node_walkable = false;
+ }
+ }
+ block->setChangedFlag();
+}
+
+#define VMANIP_FLAG_DUNGEON VOXELFLAG_CHECKED1
+
+/*
+ This is the main map generation method
+*/
+
+void makeChunk(ChunkMakeData *data)
+{
+ if(data->no_op)
+ return;
+
+ s16 y_nodes_min = data->y_blocks_min * MAP_BLOCKSIZE;
+ s16 y_nodes_max = data->y_blocks_max * MAP_BLOCKSIZE + MAP_BLOCKSIZE - 1;
+ s16 h_blocks = data->y_blocks_max - data->y_blocks_min + 1;
+ u32 relative_volume = (u32)data->sectorpos_base_size*MAP_BLOCKSIZE
+ *(u32)data->sectorpos_base_size*MAP_BLOCKSIZE
+ *(u32)h_blocks*MAP_BLOCKSIZE;
+ v3s16 bigarea_blocks_min(
+ data->sectorpos_bigbase.X,
+ data->y_blocks_min,
+ data->sectorpos_bigbase.Y
+ );
+ v3s16 bigarea_blocks_max(
+ data->sectorpos_bigbase.X + data->sectorpos_bigbase_size - 1,
+ data->y_blocks_max,
+ data->sectorpos_bigbase.Y + data->sectorpos_bigbase_size - 1
+ );
+ s16 lighting_min_d = 0-data->max_spread_amount;
+ s16 lighting_max_d = data->sectorpos_base_size*MAP_BLOCKSIZE
+ + data->max_spread_amount-1;
+
+ // Clear all flags
+ data->vmanip.clearFlag(0xff);
+
+ TimeTaker timer_generate("makeChunk() generate");
+
+ // Maximum height of the stone surface and obstacles.
+ // This is used to disable cave generation from going too high.
+ s16 stone_surface_max_y = 0;
+
+ /*
+ Generate general ground level to full area
+ */
+ {
+ // 22ms @cs=8
+ TimeTaker timer1("Generating ground level");
+
+#if 0
+ NoiseBuffer noisebuf1;
+ //NoiseBuffer noisebuf2;
+ {
+ v3f minpos_f(
+ data->sectorpos_bigbase.X*MAP_BLOCKSIZE,
+ y_nodes_min,
+ data->sectorpos_bigbase.Y*MAP_BLOCKSIZE
+ );
+ v3f maxpos_f = minpos_f + v3f(
+ data->sectorpos_bigbase_size*MAP_BLOCKSIZE,
+ y_nodes_max-y_nodes_min,
+ data->sectorpos_bigbase_size*MAP_BLOCKSIZE
+ );
+ v3f samplelength_f = v3f(4.0, 4.0, 4.0);
+
+ TimeTaker timer("noisebuf.create");
+
+ noisebuf1.create(data->seed+25104, 6, 0.60, 200.0,
+ minpos_f.X, minpos_f.Y, minpos_f.Z,
+ maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
+ samplelength_f.X, samplelength_f.Y, samplelength_f.Z);
+ /*noisebuf1.create(data->seed+25104, 3, 0.60, 25.0,
+ minpos_f.X, minpos_f.Y, minpos_f.Z,
+ maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
+ samplelength_f.X, samplelength_f.Y, samplelength_f.Z);
+ noisebuf2.create(data->seed+25105, 4, 0.50, 200.0,
+ minpos_f.X, minpos_f.Y, minpos_f.Z,
+ maxpos_f.X, maxpos_f.Y, maxpos_f.Z,
+ samplelength_f.X, samplelength_f.Y, samplelength_f.Z);*/
+ }
+
+ for(s16 x=0; x<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
+ for(s16 z=0; z<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
+ {
+ // Node position
+ v2s16 p2d = data->sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
+
+ // 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);
+ //surface_y_f = base_rock_level_2d(data->seed, p2d);
+
+ // 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;
+
+ /*
+ Fill ground with stone
+ */
+ {
+ // Use fast index incrementing
+ v3s16 em = data->vmanip.m_area.getExtent();
+ u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
+ for(s16 y=y_nodes_min; y<=y_nodes_max; y++)
+ {
+ // Skip if already generated.
+ // This is done here because there might be a cave at
+ // any point in ground, which could look like it
+ // wasn't generated.
+ if(data->vmanip.m_data[i].d != CONTENT_AIR)
+ break;
+
+ /*s16 noiseval = 50.0 * noise3d_perlin(
+ 0.5+(float)p2d.X/100.0,
+ 0.5+(float)y/100.0,
+ 0.5+(float)p2d.Y/100.0,
+ data->seed+123, 5, 0.5);*/
+ double noiseval = 64.0 * noisebuf1.get(p2d.X, y, p2d.Y);
+ /*double noiseval = 30.0 * noisebuf1.get(p2d.X, y, p2d.Y);
+ noiseval *= MYMAX(0, -0.2 + noisebuf2.get(p2d.X, y, p2d.Y));*/
+
+ //if(y < surface_y + noiseval)
+ if(noiseval > 0)
+ //if(noiseval > y)
+ data->vmanip.m_data[i].d = CONTENT_STONE;
+
+ data->vmanip.m_area.add_y(em, i, 1);
+ }
+ }
+ }
+#endif
+
+#if 1
+ for(s16 x=0; x<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; x++)
+ for(s16 z=0; z<data->sectorpos_bigbase_size*MAP_BLOCKSIZE; z++)
+ {
+ // Node position
+ v2s16 p2d = data->sectorpos_bigbase*MAP_BLOCKSIZE + v2s16(x,z);
+
+ /*
+ Skip of already generated
+ */
+ /*{
+ v3s16 p(p2d.X, y_nodes_min, p2d.Y);
+ if(data->vmanip.m_data[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;
+
+ /*
+ Fill ground with stone
+ */
+ {
+ // Use fast index incrementing
+ v3s16 em = data->vmanip.m_area.getExtent();
+ u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_min, p2d.Y));
+ for(s16 y=y_nodes_min; y<surface_y && y<=y_nodes_max; y++)
+ {
+ // Skip if already generated.
+ // This is done here because there might be a cave at
+ // any point in ground, which could look like it
+ // wasn't generated.
+ if(data->vmanip.m_data[i].d != CONTENT_AIR)
+ break;
+
+ data->vmanip.m_data[i].d = CONTENT_STONE;
+
+ data->vmanip.m_area.add_y(em, i, 1);
+ }
+ }
+ }
+#endif
+
+ }//timer1
+
+ /*
+ Randomize some parameters
+ */
+
+ //s32 stone_obstacle_count = 0;
+ /*s32 stone_obstacle_count =
+ rangelim((1.0+noise2d(data->seed+897,
+ data->sectorpos_base.X, data->sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
+
+ //s16 stone_obstacle_max_height = 0;
+ /*s16 stone_obstacle_max_height =
+ rangelim((1.0+noise2d(data->seed+5902,
+ data->sectorpos_base.X, data->sectorpos_base.Y))/2.0 * 30, 0, 100000);*/
+
+ /*
+ Loop this part, it will make stuff look older and newer nicely
+ */
+ const u32 age_loops = 2;
+ for(u32 i_age=0; i_age<age_loops; i_age++)
+ { // Aging loop
+ /******************************
+ BEGINNING OF AGING LOOP
+ ******************************/
+
+#if 1
+ {
+ // 24ms @cs=8
+ //TimeTaker timer1("caves");
+
+ /*
+ Make caves
+ */
+ u32 caves_count = relative_volume / 400000;
+ u32 bruises_count = relative_volume * stone_surface_max_y / 40000000;
+ if(stone_surface_max_y < WATER_LEVEL)
+ bruises_count = 0;
+ /*u32 caves_count = 0;
+ u32 bruises_count = 0;*/
+ for(u32 jj=0; jj<caves_count+bruises_count; jj++)
+ {
+ s16 min_tunnel_diameter = 3;
+ s16 max_tunnel_diameter = 5;
+ u16 tunnel_routepoints = 20;
+
+ v3f main_direction(0,0,0);
+
+ bool bruise_surface = (jj > caves_count);
+
+ if(bruise_surface)
+ {
+ min_tunnel_diameter = 5;
+ max_tunnel_diameter = myrand_range(10, 20);
+ /*min_tunnel_diameter = MYMAX(0, stone_surface_max_y/6);
+ max_tunnel_diameter = myrand_range(MYMAX(0, stone_surface_max_y/6), MYMAX(0, stone_surface_max_y/2));*/
+
+ /*s16 tunnel_rou = rangelim(25*(0.5+1.0*noise2d(data->seed+42,
+ data->sectorpos_base.X, data->sectorpos_base.Y)), 0, 15);*/
+
+ tunnel_routepoints = 5;
+ }
+ else
+ {
+ }
+
+ // Allowed route area size in nodes
+ v3s16 ar(
+ data->sectorpos_base_size*MAP_BLOCKSIZE,
+ h_blocks*MAP_BLOCKSIZE,
+ data->sectorpos_base_size*MAP_BLOCKSIZE
+ );
+
+ // Area starting point in nodes
+ v3s16 of(
+ data->sectorpos_base.X*MAP_BLOCKSIZE,
+ data->y_blocks_min*MAP_BLOCKSIZE,
+ data->sectorpos_base.Y*MAP_BLOCKSIZE
+ );
+
+ // 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 = data->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(bruise_surface == false)
+ route_y_max -= myrand_range(0, max_tunnel_diameter*2);*/
+
+ // Limit maximum to area
+ route_y_max = rangelim(route_y_max, 0, ar.Y-1);
+
+ if(bruise_surface)
+ {
+ /*// 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;
+ route_y_min = myrand_range(min, min + max_tunnel_diameter);
+ route_y_min = rangelim(route_y_min, 0, route_y_max);
+ }
+
+ /*dstream<<"route_y_min = "<<route_y_min
+ <<", route_y_max = "<<route_y_max<<std::endl;*/
+
+ s16 route_start_y_min = route_y_min;
+ s16 route_start_y_max = route_y_max;
+
+ // Start every 2nd cave from surface
+ bool coming_from_surface = (jj % 2 == 0 && bruise_surface == false);
+
+ if(coming_from_surface)
+ {
+ route_start_y_min = -of.Y + stone_surface_max_y + 10;
+ }
+
+ route_start_y_min = rangelim(route_start_y_min, 0, ar.Y-1);
+ route_start_y_max = rangelim(route_start_y_max, route_start_y_min, ar.Y-1);
+
+ // Randomize starting position
+ v3f orp(
+ (float)(myrand()%ar.X)+0.5,
+ (float)(myrand_range(route_start_y_min, route_start_y_max))+0.5,
+ (float)(myrand()%ar.Z)+0.5
+ );
+
+ MapNode airnode(CONTENT_AIR);
+
+ /*
+ Generate some tunnel starting from orp
+ */
+
+ for(u16 j=0; j<tunnel_routepoints; j++)
+ {
+ if(j%7==0 && bruise_surface == false)
+ {
+ main_direction = v3f(
+ ((float)(myrand()%20)-(float)10)/10,
+ ((float)(myrand()%20)-(float)10)/30,
+ ((float)(myrand()%20)-(float)10)/10
+ );
+ main_direction *= (float)myrand_range(1, 3);
+ }
+
+ // Randomize size
+ s16 min_d = min_tunnel_diameter;
+ s16 max_d = max_tunnel_diameter;
+ s16 rs = myrand_range(min_d, max_d);
+
+ v3s16 maxlen;
+ if(bruise_surface)
+ {
+ maxlen = v3s16(rs*7,rs*7,rs*7);
+ }
+ else
+ {
+ maxlen = v3s16(rs*4, myrand_range(1, rs*3), rs*4);
+ }
+
+ v3f vec;
+
+ if(coming_from_surface && j < 3)
+ {
+ vec = v3f(
+ (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
+ (float)(myrand()%(maxlen.Y*1))-(float)maxlen.Y,
+ (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
+ );
+ }
+ else
+ {
+ vec = v3f(
+ (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
+ (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
+ (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
+ );
+ }
+
+ vec += main_direction;
+
+ v3f rp = orp + vec;
+ if(rp.X < 0)
+ rp.X = 0;
+ else if(rp.X >= ar.X)
+ rp.X = ar.X-1;
+ if(rp.Y < route_y_min)
+ rp.Y = route_y_min;
+ else if(rp.Y >= route_y_max)
+ rp.Y = route_y_max-1;
+ if(rp.Z < 0)
+ rp.Z = 0;
+ else if(rp.Z >= ar.Z)
+ rp.Z = ar.Z-1;
+ vec = rp - orp;
+
+ for(float f=0; f<1.0; f+=1.0/vec.getLength())
+ {
+ v3f fp = orp + vec * f;
+ v3s16 cp(fp.X, fp.Y, fp.Z);
+
+ s16 d0 = -rs/2;
+ s16 d1 = d0 + rs - 1;
+ for(s16 z0=d0; z0<=d1; z0++)
+ {
+ //s16 si = rs - MYMAX(0, abs(z0)-rs/4);
+ s16 si = rs - MYMAX(0, abs(z0)-rs/7);
+ for(s16 x0=-si; x0<=si-1; x0++)
+ {
+ s16 maxabsxz = MYMAX(abs(x0), abs(z0));
+ //s16 si2 = rs - MYMAX(0, maxabsxz-rs/4);
+ s16 si2 = rs - MYMAX(0, maxabsxz-rs/7);
+ //s16 si2 = rs - abs(x0);
+ for(s16 y0=-si2+1+2; y0<=si2-1; y0++)
+ {
+ s16 z = cp.Z + z0;
+ s16 y = cp.Y + y0;
+ s16 x = cp.X + x0;
+ v3s16 p(x,y,z);
+ /*if(isInArea(p, ar) == false)
+ continue;*/
+ // Check only height
+ if(y < 0 || y >= ar.Y)
+ continue;
+ p += of;
+
+ //assert(data->vmanip.m_area.contains(p));
+ if(data->vmanip.m_area.contains(p) == false)
+ {
+ dstream<<"WARNING: "<<__FUNCTION_NAME
+ <<":"<<__LINE__<<": "
+ <<"point not in area"
+ <<std::endl;
+ continue;
+ }
+
+ // Just set it to air, it will be changed to
+ // water afterwards
+ u32 i = data->vmanip.m_area.index(p);
+ data->vmanip.m_data[i] = airnode;
+
+ if(bruise_surface == false)
+ {
+ // Set tunnel flag
+ data->vmanip.m_flags[i] |= VMANIP_FLAG_DUNGEON;
+ }
+ }
+ }
+ }
+ }
+
+ orp = rp;
+ }
+
+ }
+
+ }//timer1
+#endif
+
+#if 1
+ {
+ // 46ms @cs=8
+ //TimeTaker timer1("ore veins");
+
+ /*
+ Make ore veins
+ */
+ for(u32 jj=0; jj<relative_volume/1000; jj++)
+ {
+ s16 max_vein_diameter = 3;
+
+ // Allowed route area size in nodes
+ v3s16 ar(
+ data->sectorpos_base_size*MAP_BLOCKSIZE,
+ h_blocks*MAP_BLOCKSIZE,
+ data->sectorpos_base_size*MAP_BLOCKSIZE
+ );
+
+ // Area starting point in nodes
+ v3s16 of(
+ data->sectorpos_base.X*MAP_BLOCKSIZE,
+ data->y_blocks_min*MAP_BLOCKSIZE,
+ data->sectorpos_base.Y*MAP_BLOCKSIZE
+ );
+
+ // Allow a bit more
+ //(this should be more than the maximum radius of the tunnel)
+ s16 insure = 3;
+ s16 more = data->max_spread_amount - max_vein_diameter/2 - insure;
+ ar += v3s16(1,0,1) * more * 2;
+ of -= v3s16(1,0,1) * more;
+
+ // Randomize starting position
+ v3f orp(
+ (float)(myrand()%ar.X)+0.5,
+ (float)(myrand()%ar.Y)+0.5,
+ (float)(myrand()%ar.Z)+0.5
+ );
+
+ // Randomize mineral
+ u8 mineral;
+ if(myrand()%3 != 0)
+ mineral = MINERAL_COAL;
+ else
+ mineral = MINERAL_IRON;
+
+ /*
+ Generate some vein starting from orp
+ */
+
+ for(u16 j=0; j<2; j++)
+ {
+ /*v3f rp(
+ (float)(myrand()%ar.X)+0.5,
+ (float)(myrand()%ar.Y)+0.5,
+ (float)(myrand()%ar.Z)+0.5
+ );
+ v3f vec = rp - orp;*/
+
+ v3s16 maxlen(5, 5, 5);
+ v3f vec(
+ (float)(myrand()%(maxlen.X*2))-(float)maxlen.X,
+ (float)(myrand()%(maxlen.Y*2))-(float)maxlen.Y,
+ (float)(myrand()%(maxlen.Z*2))-(float)maxlen.Z
+ );
+ v3f rp = orp + vec;
+ if(rp.X < 0)
+ rp.X = 0;
+ else if(rp.X >= ar.X)
+ rp.X = ar.X;
+ if(rp.Y < 0)
+ rp.Y = 0;
+ else if(rp.Y >= ar.Y)
+ rp.Y = ar.Y;
+ if(rp.Z < 0)
+ rp.Z = 0;
+ else if(rp.Z >= ar.Z)
+ rp.Z = ar.Z;
+ vec = rp - orp;
+
+ // Randomize size
+ s16 min_d = 0;
+ s16 max_d = max_vein_diameter;
+ s16 rs = myrand_range(min_d, max_d);
+
+ for(float f=0; f<1.0; f+=1.0/vec.getLength())
+ {
+ v3f fp = orp + vec * f;
+ v3s16 cp(fp.X, fp.Y, fp.Z);
+ s16 d0 = -rs/2;
+ s16 d1 = d0 + rs - 1;
+ for(s16 z0=d0; z0<=d1; z0++)
+ {
+ s16 si = rs - abs(z0);
+ for(s16 x0=-si; x0<=si-1; x0++)
+ {
+ s16 si2 = rs - abs(x0);
+ for(s16 y0=-si2+1; y0<=si2-1; y0++)
+ {
+ // Don't put mineral to every place
+ if(myrand()%5 != 0)
+ continue;
+
+ s16 z = cp.Z + z0;
+ s16 y = cp.Y + y0;
+ s16 x = cp.X + x0;
+ v3s16 p(x,y,z);
+ /*if(isInArea(p, ar) == false)
+ continue;*/
+ // Check only height
+ if(y < 0 || y >= ar.Y)
+ continue;
+ p += of;
+
+ assert(data->vmanip.m_area.contains(p));
+
+ // Just set it to air, it will be changed to
+ // water afterwards
+ u32 i = data->vmanip.m_area.index(p);
+ MapNode *n = &data->vmanip.m_data[i];
+ if(n->d == CONTENT_STONE)
+ n->param = mineral;
+ }
+ }
+ }
+ }
+
+ orp = rp;
+ }
+
+ }
+
+ }//timer1
+#endif
+
+#if 1
+ {
+ // 15ms @cs=8
+ TimeTaker timer1("add mud");
+
+ /*
+ Add mud to the central chunk
+ */
+
+ for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
+ for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)
+ {
+ // Node position in 2d
+ v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
+
+ // Randomize mud amount
+ s16 mud_add_amount = get_mud_add_amount(data->seed, p2d) / 2.0;
+
+ // Find ground level
+ s16 surface_y = find_ground_level_clever(data->vmanip, p2d);
+
+ /*
+ 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 = data->vmanip.m_area.index(v3s16(p2d.X, surface_y, p2d.Y));
+ MapNode *n = &data->vmanip.m_data[i];
+ if(n->d == CONTENT_GRASS)
+ *n = MapNode(CONTENT_MUD);
+ //n->d = CONTENT_MUD;
+ }
+
+ /*
+ Add mud on ground
+ */
+ {
+ s16 mudcount = 0;
+ v3s16 em = data->vmanip.m_area.getExtent();
+ s16 y_start = surface_y+1;
+ u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
+ for(s16 y=y_start; y<=y_nodes_max; y++)
+ {
+ if(mudcount >= mud_add_amount)
+ break;
+
+ MapNode &n = data->vmanip.m_data[i];
+ n = MapNode(CONTENT_MUD);
+ //n.d = CONTENT_MUD;
+ mudcount++;
+
+ data->vmanip.m_area.add_y(em, i, 1);
+ }
+ }
+
+ }
+
+ }//timer1
+#endif
+
+#if 1
+ {
+ // 340ms @cs=8
+ TimeTaker timer1("flow mud");
+
+ /*
+ Flow mud away from steep edges
+ */
+
+ // Limit area by 1 because mud is flown into neighbors.
+ s16 mudflow_minpos = 0-data->max_spread_amount+1;
+ s16 mudflow_maxpos = data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-2;
+
+ // Iterate a few times
+ for(s16 k=0; k<3; k++)
+ {
+
+ 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);
+ }
+
+ // Node position in 2d
+ v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
+
+ v3s16 em = data->vmanip.m_area.getExtent();
+ u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
+ s16 y=y_nodes_max;
+
+ for(;; y--)
+ {
+ MapNode *n = NULL;
+ // Find mud
+ for(; y>=y_nodes_min; y--)
+ {
+ n = &data->vmanip.m_data[i];
+ //if(content_walkable(n->d))
+ // break;
+ if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
+ break;
+
+ data->vmanip.m_area.add_y(em, i, -1);
+ }
+
+ // Stop if out of area
+ //if(data->vmanip.m_area.contains(i) == false)
+ if(y < y_nodes_min)
+ break;
+
+ /*// If not mud, do nothing to it
+ MapNode *n = &data->vmanip.m_data[i];
+ if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
+ continue;*/
+
+ /*
+ Don't flow it if the stuff under it is not mud
+ */
+ {
+ u32 i2 = i;
+ data->vmanip.m_area.add_y(em, i2, -1);
+ // Cancel if out of area
+ if(data->vmanip.m_area.contains(i2) == false)
+ continue;
+ MapNode *n2 = &data->vmanip.m_data[i2];
+ if(n2->d != CONTENT_MUD && n2->d != CONTENT_GRASS)
+ continue;
+ }
+
+ // Make it exactly mud
+ n->d = CONTENT_MUD;
+
+ /*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;
+ data->vmanip.m_area.add_y(em, i3, 1);
+ if(data->vmanip.m_area.contains(i3) == true
+ && content_walkable(data->vmanip.m_data[i3].d) == true)
+ {
+ continue;
+ }
+
+ // Drop mud on side
+
+ for(u32 di=0; di<4; di++)
+ {
+ v3s16 dirp = dirs4[di];
+ u32 i2 = i;
+ // Move to side
+ data->vmanip.m_area.add_p(em, i2, dirp);
+ // Fail if out of area
+ if(data->vmanip.m_area.contains(i2) == false)
+ continue;
+ // Check that side is air
+ MapNode *n2 = &data->vmanip.m_data[i2];
+ if(content_walkable(n2->d))
+ continue;
+ // Check that under side is air
+ data->vmanip.m_area.add_y(em, i2, -1);
+ if(data->vmanip.m_area.contains(i2) == false)
+ continue;
+ n2 = &data->vmanip.m_data[i2];
+ if(content_walkable(n2->d))
+ continue;
+ /*// Check that under that is air (need a drop of 2)
+ data->vmanip.m_area.add_y(em, i2, -1);
+ if(data->vmanip.m_area.contains(i2) == false)
+ continue;
+ n2 = &data->vmanip.m_data[i2];
+ if(content_walkable(n2->d))
+ continue;*/
+ // Loop further down until not air
+ do{
+ data->vmanip.m_area.add_y(em, i2, -1);
+ // Fail if out of area
+ if(data->vmanip.m_area.contains(i2) == false)
+ continue;
+ n2 = &data->vmanip.m_data[i2];
+ }while(content_walkable(n2->d) == false);
+ // Loop one up so that we're in air
+ data->vmanip.m_area.add_y(em, i2, 1);
+ n2 = &data->vmanip.m_data[i2];
+
+ // Move mud to new place
+ *n2 = *n;
+ // Set old place to be air
+ *n = MapNode(CONTENT_AIR);
+
+ // Done
+ break;
+ }
+ }
+ }
+
+ }
+
+ }//timer1
+#endif
+
+#if 1
+ {
+ // 50ms @cs=8
+ TimeTaker timer1("add water");
+
+ /*
+ Add water to the central chunk (and a bit more)
+ */
+
+ for(s16 x=0-data->max_spread_amount;
+ x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
+ x++)
+ for(s16 z=0-data->max_spread_amount;
+ z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount;
+ z++)
+ {
+ // Node position in 2d
+ v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
+
+ // Find ground level
+ //s16 surface_y = find_ground_level(data->vmanip, p2d);
+
+ /*
+ If ground level is over water level, skip.
+ NOTE: This leaves caves near water without water,
+ which looks especially crappy when the nearby water
+ won't start flowing either for some reason
+ */
+ /*if(surface_y > WATER_LEVEL)
+ continue;*/
+
+ /*
+ Add water on ground
+ */
+ {
+ v3s16 em = data->vmanip.m_area.getExtent();
+ u8 light = LIGHT_MAX;
+ // Start at global water surface level
+ s16 y_start = WATER_LEVEL;
+ u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
+ MapNode *n = &data->vmanip.m_data[i];
+
+ for(s16 y=y_start; y>=y_nodes_min; y--)
+ {
+ n = &data->vmanip.m_data[i];
+
+ // Stop when there is no water and no air
+ if(n->d != CONTENT_AIR && n->d != CONTENT_WATERSOURCE
+ && n->d != CONTENT_WATER)
+ {
+
+ break;
+ }
+
+ // Make water only not in caves
+ if(!(data->vmanip.m_flags[i]&VMANIP_FLAG_DUNGEON))
+ {
+ n->d = CONTENT_WATERSOURCE;
+ //n->setLight(LIGHTBANK_DAY, light);
+
+ // Add to transforming liquid queue (in case it'd
+ // start flowing)
+ v3s16 p = v3s16(p2d.X, y, p2d.Y);
+ data->transforming_liquid.push_back(p);
+ }
+
+ // Next one
+ data->vmanip.m_area.add_y(em, i, -1);
+ if(light > 0)
+ light--;
+ }
+ }
+
+ }
+
+ }//timer1
+#endif
+
+ } // Aging loop
+ /***********************
+ END OF AGING LOOP
+ ************************/
+
+#if 1
+ {
+ //TimeTaker timer1("convert mud to sand");
+
+ /*
+ Convert mud to sand
+ */
+
+ //s16 mud_add_amount = myrand_range(2, 4);
+ //s16 mud_add_amount = 0;
+
+ /*for(s16 x=0; x<data->sectorpos_base_size*MAP_BLOCKSIZE; x++)
+ for(s16 z=0; z<data->sectorpos_base_size*MAP_BLOCKSIZE; z++)*/
+ for(s16 x=0-data->max_spread_amount+1;
+ x<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
+ x++)
+ for(s16 z=0-data->max_spread_amount+1;
+ z<data->sectorpos_base_size*MAP_BLOCKSIZE+data->max_spread_amount-1;
+ z++)
+ {
+ // Node position in 2d
+ v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
+
+ // Determine whether to have sand here
+ double sandnoise = noise2d_perlin(
+ 0.5+(float)p2d.X/500, 0.5+(float)p2d.Y/500,
+ data->seed+59420, 3, 0.50);
+
+ bool have_sand = (sandnoise > -0.15);
+
+ if(have_sand == false)
+ continue;
+
+ // Find ground level
+ s16 surface_y = find_ground_level_clever(data->vmanip, p2d);
+
+ if(surface_y > WATER_LEVEL + 2)
+ continue;
+
+ {
+ v3s16 em = data->vmanip.m_area.getExtent();
+ s16 y_start = surface_y;
+ u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
+ u32 not_sand_counter = 0;
+ for(s16 y=y_start; y>=y_nodes_min; y--)
+ {
+ MapNode *n = &data->vmanip.m_data[i];
+ if(n->d == CONTENT_MUD || n->d == CONTENT_GRASS)
+ {
+ n->d = CONTENT_SAND;
+ }
+ else
+ {
+ not_sand_counter++;
+ if(not_sand_counter > 3)
+ break;
+ }
+
+ data->vmanip.m_area.add_y(em, i, -1);
+ }
+ }
+