+ 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);
+ }
+ }
+
+ }
+
+ }//timer1
+#endif
+
+#if 1
+ {
+ // 1ms @cs=8
+ //TimeTaker timer1("generate trees");
+
+ /*
+ Generate some trees
+ */
+ {
+ // Divide area into parts
+ s16 div = 8;
+ s16 sidelen = data->sectorpos_base_size*MAP_BLOCKSIZE / div;
+ double area = sidelen * sidelen;
+ for(s16 x0=0; x0<div; x0++)
+ for(s16 z0=0; z0<div; z0++)
+ {
+ // Center position of part of division
+ v2s16 p2d_center(
+ data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen/2 + sidelen*x0,
+ data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen/2 + sidelen*z0
+ );
+ // Minimum edge of part of division
+ v2s16 p2d_min(
+ data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen*x0,
+ data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen*z0
+ );
+ // Maximum edge of part of division
+ v2s16 p2d_max(
+ data->sectorpos_base.X*MAP_BLOCKSIZE + sidelen + sidelen*x0 - 1,
+ data->sectorpos_base.Y*MAP_BLOCKSIZE + sidelen + sidelen*z0 - 1
+ );
+ // Amount of trees
+ u32 tree_count = area * tree_amount_2d(data->seed, p2d_center);
+ // Put trees in random places on part of division
+ for(u32 i=0; i<tree_count; i++)
+ {
+ s16 x = myrand_range(p2d_min.X, p2d_max.X);
+ s16 z = myrand_range(p2d_min.Y, p2d_max.Y);
+ s16 y = find_ground_level(data->vmanip, v2s16(x,z));
+ // Don't make a tree under water level
+ if(y < WATER_LEVEL)
+ continue;
+ // Don't make a tree so high that it doesn't fit
+ if(y > y_nodes_max - 6)
+ continue;
+ v3s16 p(x,y,z);
+ /*
+ Trees grow only on mud and grass
+ */
+ {
+ u32 i = data->vmanip.m_area.index(v3s16(p));
+ MapNode *n = &data->vmanip.m_data[i];
+ if(n->d != CONTENT_MUD && n->d != CONTENT_GRASS)
+ continue;
+ }
+ p.Y++;
+ // Make a tree
+ make_tree(data->vmanip, p);
+ }
+ }
+ /*u32 tree_max = relative_area / 60;
+ //u32 count = myrand_range(0, tree_max);
+ for(u32 i=0; i<count; i++)
+ {
+ s16 x = myrand_range(0, data->sectorpos_base_size*MAP_BLOCKSIZE-1);
+ s16 z = myrand_range(0, data->sectorpos_base_size*MAP_BLOCKSIZE-1);
+ x += data->sectorpos_base.X*MAP_BLOCKSIZE;
+ z += data->sectorpos_base.Y*MAP_BLOCKSIZE;
+ s16 y = find_ground_level(data->vmanip, v2s16(x,z));
+ // Don't make a tree under water level
+ if(y < WATER_LEVEL)
+ continue;
+ v3s16 p(x,y+1,z);
+ // Make a tree
+ make_tree(data->vmanip, p);
+ }*/
+ }
+
+ }//timer1
+#endif
+
+#if 1
+ {
+ // 19ms @cs=8
+ //TimeTaker timer1("grow grass");
+
+ /*
+ Grow grass
+ */
+
+ /*for(s16 x=0-4; x<data->sectorpos_base_size*MAP_BLOCKSIZE+4; x++)
+ for(s16 z=0-4; z<data->sectorpos_base_size*MAP_BLOCKSIZE+4; z++)*/
+ 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 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 = data->vmanip.m_area.getExtent();
+ u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_nodes_max, p2d.Y));
+ s16 y;
+ // Go to ground level
+ for(y=y_nodes_max; y>=y_nodes_min; y--)
+ {
+ MapNode &n = data->vmanip.m_data[i];
+ if(n.d != CONTENT_AIR
+ && n.d != CONTENT_LEAVES)
+ break;
+ data->vmanip.m_area.add_y(em, i, -1);
+ }
+ if(y >= y_nodes_min)
+ surface_y = y;
+ else
+ surface_y = y_nodes_min;
+ }
+
+ u32 i = data->vmanip.m_area.index(p2d.X, surface_y, p2d.Y);
+ MapNode *n = &data->vmanip.m_data[i];
+ if(n->d == CONTENT_MUD)
+ n->d = CONTENT_GRASS;
+ }
+
+ }//timer1
+#endif
+
+ /*
+ Initial lighting (sunlight)
+ */
+
+ core::map<v3s16, bool> light_sources;
+
+ {
+ // 750ms @cs=8, can't optimize more
+ TimeTaker timer1("initial lighting");
+
+ // NOTE: This is no used... umm... for some reason!
+#if 0
+ /*
+ Go through the edges and add all nodes that have light to light_sources
+ */
+
+ // Four edges
+ for(s16 i=0; i<4; i++)
+ // Edge length
+ for(s16 j=lighting_min_d;
+ j<=lighting_max_d;
+ j++)
+ {
+ s16 x;
+ s16 z;
+ // +-X
+ if(i == 0 || i == 1)
+ {
+ x = (i==0) ? lighting_min_d : lighting_max_d;
+ if(i == 0)
+ z = lighting_min_d;
+ else
+ z = lighting_max_d;
+ }
+ // +-Z
+ else
+ {
+ z = (i==0) ? lighting_min_d : lighting_max_d;
+ if(i == 0)
+ x = lighting_min_d;
+ else
+ x = lighting_max_d;
+ }
+
+ // Node position in 2d
+ v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
+
+ {
+ v3s16 em = data->vmanip.m_area.getExtent();
+ s16 y_start = y_nodes_max;
+ u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
+ for(s16 y=y_start; y>=y_nodes_min; y--)
+ {
+ MapNode *n = &data->vmanip.m_data[i];
+ if(n->getLight(LIGHTBANK_DAY) != 0)
+ {
+ light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
+ }
+ //NOTE: This is broken, at least the index has to
+ // be incremented
+ }
+ }
+ }
+#endif
+
+#if 1
+ /*
+ Go through the edges and apply sunlight to them, not caring
+ about neighbors
+ */
+
+ // Four edges
+ for(s16 i=0; i<4; i++)
+ // Edge length
+ for(s16 j=lighting_min_d;
+ j<=lighting_max_d;
+ j++)
+ {
+ s16 x;
+ s16 z;
+ // +-X
+ if(i == 0 || i == 1)
+ {
+ x = (i==0) ? lighting_min_d : lighting_max_d;
+ if(i == 0)
+ z = lighting_min_d;
+ else
+ z = lighting_max_d;
+ }
+ // +-Z
+ else
+ {
+ z = (i==0) ? lighting_min_d : lighting_max_d;
+ if(i == 0)
+ x = lighting_min_d;
+ else
+ x = lighting_max_d;
+ }
+
+ // Node position in 2d
+ v2s16 p2d = data->sectorpos_base*MAP_BLOCKSIZE + v2s16(x,z);
+
+ // Loop from top to down
+ {
+ u8 light = LIGHT_SUN;
+ v3s16 em = data->vmanip.m_area.getExtent();
+ s16 y_start = y_nodes_max;
+ u32 i = data->vmanip.m_area.index(v3s16(p2d.X, y_start, p2d.Y));
+ for(s16 y=y_start; y>=y_nodes_min; y--)
+ {
+ MapNode *n = &data->vmanip.m_data[i];
+ if(light_propagates_content(n->d) == false)
+ {
+ light = 0;
+ }
+ else if(light != LIGHT_SUN
+ || sunlight_propagates_content(n->d) == false)
+ {
+ if(light > 0)
+ light--;
+ }
+
+ n->setLight(LIGHTBANK_DAY, light);
+ n->setLight(LIGHTBANK_NIGHT, 0);
+
+ if(light != 0)
+ {
+ // Insert light source
+ light_sources.insert(v3s16(p2d.X, y, p2d.Y), true);
+ }
+
+ // Increment index by y
+ data->vmanip.m_area.add_y(em, i, -1);
+ }