private Block GetBlockLayerBlock(int unscaledRain, int unscaledTemp, int posY, int firstBlockId, int forDepth, Block defaultBlock, IList <Block> blocks) { float temperature = TerraGenConfig.GetScaledAdjustedTemperatureFloat(unscaledTemp, posY - TerraGenConfig.seaLevel); float rainRel = TerraGenConfig.GetRainFall(unscaledRain, posY) / 255f; float heightRel = ((float)posY - TerraGenConfig.seaLevel) / ((float)mapheight - TerraGenConfig.seaLevel); float fertilityRel = TerraGenConfig.GetFertilityFromUnscaledTemp((int)(rainRel * 255), unscaledTemp, heightRel) / 255f; for (int j = forDepth; j < blockLayerConfig.Blocklayers.Length; j++) { BlockLayer bl = blockLayerConfig.Blocklayers[j]; if ( temperature >= bl.MinTemp && temperature <= bl.MaxTemp && rainRel >= bl.MinRain && rainRel <= bl.MaxRain && fertilityRel >= bl.MinFertility && fertilityRel <= bl.MaxFertility && (float)posY / mapheight <= bl.MaxY ) { int blockId = bl.GetBlockId(0.1, temperature, rainRel, fertilityRel, firstBlockId); if (blockId != 0) { return(blocks[blockId]); } } } return(defaultBlock); }
public virtual void GenDeposit(IServerChunk[] chunks, int chunkX, int chunkZ, BlockPos depoCenterPos, DepositVariant variant) { int lx = GameMath.Mod(depoCenterPos.X, chunksize); int lz = GameMath.Mod(depoCenterPos.Z, chunksize); // Check if suited for this area, climate wise if (variant.Climate != null) { IMapChunk originMapchunk = api.WorldManager.GetMapChunk(depoCenterPos.X / chunksize, depoCenterPos.Z / chunksize); if (originMapchunk == null) { return; // Definition: Climate dependent deposits are limited to size 32x32x32 } depoCenterPos.Y = originMapchunk.RainHeightMap[lz * chunksize + lx]; IntDataMap2D climateMap = blockAccessor.GetMapRegion(depoCenterPos.X / regionSize, depoCenterPos.Z / regionSize).ClimateMap; float posXInRegionClimate = ((float)lx / regionSize - (float)lx / regionSize) * noiseSizeClimate; float posZInRegionClimate = ((float)lz / regionSize - (float)lz / regionSize) * noiseSizeClimate; int climate = climateMap.GetUnpaddedColorLerped(posXInRegionClimate, posZInRegionClimate); float temp = TerraGenConfig.GetScaledAdjustedTemperatureFloat((climate >> 16) & 0xff, depoCenterPos.Y - TerraGenConfig.seaLevel); float rainRel = TerraGenConfig.GetRainFall((climate >> 8) & 0xff, depoCenterPos.Y) / 255f; if (rainRel < variant.Climate.MinRain || rainRel > variant.Climate.MaxRain || temp < variant.Climate.MinTemp || temp > variant.Climate.MaxTemp) { return; } } variant.GeneratorInst?.GenDeposit(blockAccessor, chunks, chunkX, chunkZ, depoCenterPos, ref subDepositsToPlace); }
public TreeGenForClimate GetRandomGenForClimate(TreeVariant[] gens, int climate, int forest, int y) { int rain = TerraGenConfig.GetRainFall((climate >> 8) & 0xff, y); int temp = TerraGenConfig.GetScaledAdjustedTemperature((climate >> 16) & 0xff, y - TerraGenConfig.seaLevel); float heightRel = ((float)y - TerraGenConfig.seaLevel) / ((float)api.WorldManager.MapSizeY - TerraGenConfig.seaLevel); int fertility = TerraGenConfig.GetFertility(rain, temp, heightRel); float total = 0; float fertDist, rainDist, tempDist, forestDist, heightDist; distances.Clear(); for (int i = 0; i < gens.Length; i++) { TreeVariant variant = gens[i]; fertDist = Math.Abs(fertility - variant.FertMid) / variant.FertRange; rainDist = Math.Abs(rain - variant.RainMid) / variant.RainRange; tempDist = Math.Abs(temp - variant.TempMid) / variant.TempRange; forestDist = Math.Abs(forest - variant.ForestMid) / variant.ForestRange; heightDist = Math.Abs((y / worldheight) - variant.HeightMid) / variant.HeightRange; double distSq = Math.Max(0, fertDist * fertDist - 1) + Math.Max(0, rainDist * rainDist - 1) + Math.Max(0, tempDist * tempDist - 1) + Math.Max(0, forestDist * forestDist - 1) + Math.Max(0, heightDist * heightDist - 1) ; if (random.NextDouble() < distSq) { continue; } float distance = (fertDist + rainDist + tempDist + forestDist + heightDist) * variant.Weight / 100f; distances.Add(variant, distance); total += distance; } distances = distances.Shuffle(random); double rnd = random.NextDouble(); foreach (var val in distances) { rnd -= val.Value / total; if (rnd <= 0.001) { float suitabilityBonus = GameMath.Clamp(0.7f - val.Value, 0f, 0.7f) * 1 / 0.7f * val.Key.SuitabilitySizeBonus; float size = val.Key.MinSize + (float)random.NextDouble() * (val.Key.MaxSize - val.Key.MinSize) + suitabilityBonus; float rainVal = Math.Max(0, (rain / 255f - treeGenProps.vinesMinRain) / (1 - treeGenProps.vinesMinRain)); float tempVal = Math.Max(0, (TerraGenConfig.DescaleTemperature(temp) / 255f - treeGenProps.descVineMinTempRel) / (1 - treeGenProps.descVineMinTempRel)); float vinesGrowthChance = 2f * rainVal * tempVal; ITreeGenerator treegen = treeGenerators.GetGenerator(val.Key.Generator); if (treegen == null) { api.World.Logger.Error("treengenproperties.json references tree generator {0}, but no such generator exists!", val.Key.Generator); return(null); } return(new TreeGenForClimate(treegen, size, vinesGrowthChance)); } } return(null); }
private void OnChunkColumnGeneration(IServerChunk[] chunks, int chunkX, int chunkZ, ITreeAttribute chunkGenParams = null) { rnd.InitPositionSeed(chunkX, chunkZ); IntDataMap2D forestMap = chunks[0].MapChunk.MapRegion.ForestMap; IntDataMap2D climateMap = chunks[0].MapChunk.MapRegion.ClimateMap; IntDataMap2D beachMap = chunks[0].MapChunk.MapRegion.BeachMap; ushort[] heightMap = chunks[0].MapChunk.RainHeightMap; int regionChunkSize = api.WorldManager.RegionSize / chunksize; int rdx = chunkX % regionChunkSize; int rdz = chunkZ % regionChunkSize; // Amount of data points per chunk float climateStep = (float)climateMap.InnerSize / regionChunkSize; float forestStep = (float)forestMap.InnerSize / regionChunkSize; float beachStep = (float)beachMap.InnerSize / regionChunkSize; // Retrieves the map data on the chunk edges int forestUpLeft = forestMap.GetUnpaddedInt((int)(rdx * forestStep), (int)(rdz * forestStep)); int forestUpRight = forestMap.GetUnpaddedInt((int)(rdx * forestStep + forestStep), (int)(rdz * forestStep)); int forestBotLeft = forestMap.GetUnpaddedInt((int)(rdx * forestStep), (int)(rdz * forestStep + forestStep)); int forestBotRight = forestMap.GetUnpaddedInt((int)(rdx * forestStep + forestStep), (int)(rdz * forestStep + forestStep)); int beachUpLeft = beachMap.GetUnpaddedInt((int)(rdx * beachStep), (int)(rdz * beachStep)); int beachUpRight = beachMap.GetUnpaddedInt((int)(rdx * beachStep + beachStep), (int)(rdz * beachStep)); int beachBotLeft = beachMap.GetUnpaddedInt((int)(rdx * beachStep), (int)(rdz * beachStep + beachStep)); int beachBotRight = beachMap.GetUnpaddedInt((int)(rdx * beachStep + beachStep), (int)(rdz * beachStep + beachStep)); // increasing x -> left to right // increasing z -> top to bottom float transitionSize = blockLayerConfig.blockLayerTransitionSize; for (int x = 0; x < chunksize; x++) { for (int z = 0; z < chunksize; z++) { // Some weird randomnes stuff to hide fundamental bugs in the climate transition system :D T_T (maybe not bugs but just fundamental shortcomings of using lerp on a very low resolution map) float distx = (float)distort2dx.Noise(chunkX * chunksize + x, chunkZ * chunksize + z); float distz = (float)distort2dz.Noise(chunkX * chunksize + x, chunkZ * chunksize + z); double posRand = (double)GameMath.MurmurHash3(x + chunkX * chunksize, 1, z + chunkZ * chunksize) / int.MaxValue; double transitionRand = (posRand + 1) * transitionSize; int posY = heightMap[z * chunksize + x]; int climate = climateMap.GetUnpaddedColorLerped( rdx * climateStep + climateStep * (float)(x + distx) / chunksize, rdz * climateStep + climateStep * (float)(z + distz) / chunksize ); int tempUnscaled = (climate >> 16) & 0xff; int rnd = (int)(distx / 5); float temp = TerraGenConfig.GetScaledAdjustedTemperatureFloat(tempUnscaled, posY - TerraGenConfig.seaLevel + rnd); float tempRel = TerraGenConfig.GetAdjustedTemperature(tempUnscaled, posY - TerraGenConfig.seaLevel + rnd) / 255f; float rainRel = TerraGenConfig.GetRainFall((climate >> 8) & 0xff, posY + rnd) / 255f; float forestRel = GameMath.BiLerp(forestUpLeft, forestUpRight, forestBotLeft, forestBotRight, (float)x / chunksize, (float)z / chunksize) / 255f; float beachRel = GameMath.BiLerp(beachUpLeft, beachUpRight, beachBotLeft, beachBotRight, (float)x / chunksize, (float)z / chunksize) / 255f; int prevY = posY; posY = PutLayers(transitionRand, x, posY, z, chunks, rainRel, temp, tempUnscaled, heightMap); int blockID = chunks[0].MapChunk.TopRockIdMap[z * chunksize + x]; GenBeach(x, prevY, z, chunks, rainRel, temp, beachRel, blockID); PlaceTallGrass(x, prevY, z, chunks, rainRel, tempRel, temp, forestRel); // Try again to put layers if above sealevel and we found over 10 air blocks int foundAir = 0; while (posY >= TerraGenConfig.seaLevel - 1) { int chunkY = posY / chunksize; int lY = posY % chunksize; int index3d = (chunksize * lY + z) * chunksize + x; int blockId = chunks[chunkY].Blocks[index3d]; if (blockId == 0) { foundAir++; } else { if (foundAir >= 8) { //temp = TerraGenConfig.GetScaledAdjustedTemperatureFloat(tempUnscaled, posY - TerraGenConfig.seaLevel); //rainRel = TerraGenConfig.GetRainFall((climate >> 8) & 0xff, posY) / 255f; //PutLayers(transitionRand, x, posY, z, chunks, rainRel, temp, tempUnscaled, null); break; } else { foundAir = 0; } } posY--; } } } }
/// <summary> /// forceInitialPosY is for subdeposits /// </summary> /// <param name="chunks"></param> /// <param name="chunkX"></param> /// <param name="chunkZ"></param> /// <param name="offsetX"></param> /// <param name="offsetZ"></param> /// <param name="variant"></param> /// <param name="forceInitialPosY"></param> /// <returns></returns> Dictionary <Vec3i, DepositVariant> GenDeposit(IServerChunk[] chunks, int chunkX, int chunkZ, int offsetX, int offsetZ, DepositVariant variant, int?forceInitialPosY = null) { Dictionary <Vec3i, DepositVariant> SubDepositsToPlace = new Dictionary <Vec3i, DepositVariant>(); IMapChunk mapchunk = chunks[0].MapChunk; int radius = Math.Min(64, (int)variant.Radius.nextFloat(1, depositRand)); if (radius <= 0) { return(SubDepositsToPlace); } // Let's deform that perfect circle a bit (+/- 25%) float deform = GameMath.Clamp(depositRand.NextFloat() - 0.5f, -0.25f, 0.25f); int radiusX = radius - (int)(radius * deform); int radiusZ = radius + (int)(radius * deform); int posY; // No need to caluclate further if this deposit won't be part of this chunk if (radiusX + offsetX < 0 || radiusZ + offsetZ < 0 || offsetX - radiusX >= chunksize || offsetZ - radiusZ >= chunksize) { return(SubDepositsToPlace); } IMapChunk originMapchunk = null; int origPosY = 0; int lx = GameMath.Mod(offsetX, chunksize); int lz = GameMath.Mod(offsetZ, chunksize); if (variant.MaxY < 1 || variant.CheckClimate) { originMapchunk = api.WorldManager.GetMapChunk((chunkX * chunksize + offsetX) / chunksize, (chunkZ * chunksize + offsetZ) / chunksize); if (originMapchunk == null) { return(SubDepositsToPlace); // argh >.< } origPosY = originMapchunk.RainHeightMap[lz * chunksize + lx]; if ((float)origPosY / api.World.BlockAccessor.MapSizeY > variant.MaxY) { return(SubDepositsToPlace); } } // Check if suited for this area, climate wise if (variant.CheckClimate) { IntMap climateMap = api.World.BlockAccessor.GetMapRegion((chunkX * chunksize + offsetX) / regionSize, (chunkZ * chunksize + offsetZ) / regionSize).ClimateMap; float posXInRegionClimate = ((float)lx / regionSize - lx / regionSize) * noiseSizeClimate; float posZInRegionClimate = ((float)lz / regionSize - lz / regionSize) * noiseSizeClimate; int climate = climateMap.GetUnpaddedColorLerped(posXInRegionClimate, posZInRegionClimate); float temp = TerraGenConfig.GetScaledAdjustedTemperatureFloat((climate >> 16) & 0xff, origPosY - TerraGenConfig.seaLevel); float rainRel = TerraGenConfig.GetRainFall((climate >> 8) & 0xff, origPosY) / 255f; if (rainRel < variant.MinRain || rainRel > variant.MaxRain || temp < variant.MinTemp || temp > variant.MaxTemp) { return(SubDepositsToPlace); } } // Ok generate float th = variant.Thickness.nextFloat(1, depositRand); int thickness = (int)th + (depositRand.NextFloat() < th - (int)th ? 1 : 0); float xRadSqInv = 1f / (radiusX * radiusX); float zRadSqInv = 1f / (radiusZ * radiusZ); int blockIndex = 0; bool parentBlockOk = false; float depthf; bool shouldGenSurfaceDeposit = depositRand.NextFloat() > 0.35f && variant.SurfaceBlockCode != null; if (forceInitialPosY != null) { depthf = (float)forceInitialPosY / mapchunk.WorldGenTerrainHeightMap[offsetX * chunksize + offsetZ]; } else { depthf = variant.Depth.nextFloat(1, depositRand); } int depthi = (int)depthf; int topLeft = 2 * depositRand.NextInt(radiusX + 1) - radiusX; int topRight = 2 * depositRand.NextInt(radiusZ + 1) - radiusZ; int botLeft = 2 * depositRand.NextInt(radiusX + 1) - radiusX; int botRight = 2 * depositRand.NextInt(radiusZ + 1) - radiusZ; int yOff = 0; // Only generate inside this current chunk column int minx = GameMath.Clamp(offsetX - radiusX, 0, chunksize); int maxx = GameMath.Clamp(offsetX + radiusX, 0, chunksize); int minz = GameMath.Clamp(offsetZ - radiusZ, 0, chunksize); int maxz = GameMath.Clamp(offsetZ + radiusZ, 0, chunksize); float invChunkAreaSize = 1f / (chunksize * chunksize); for (int x = minx; x < maxx; x++) { float xSq = (x - offsetX) * (x - offsetX) * xRadSqInv; for (int z = minz; z < maxz; z++) { if (xSq + (z - offsetZ) * (z - offsetZ) * zRadSqInv > 1) { continue; } if (variant.Placement == EnumDepositPlacement.FollowSurfaceBelow) { posY = mapchunk.WorldGenTerrainHeightMap[z * chunksize + x] - depthi; } else if (variant.Placement == EnumDepositPlacement.FollowSurface) { yOff = (int)GameMath.BiLerp(topLeft, topRight, botLeft, botRight, (x - offsetX + radiusX) / (2f * radiusX), (z - offsetZ + radiusZ) / (2f * radiusZ)); posY = (int)(depthf * mapchunk.WorldGenTerrainHeightMap[z * chunksize + x]) + yOff / 2; } else if (variant.Placement == EnumDepositPlacement.Straight) { posY = (int)(depthf * mapchunk.WorldGenTerrainHeightMap[z * chunksize + x]); } else { yOff = (int)GameMath.BiLerp(topLeft, topRight, botLeft, botRight, (x - offsetX + radiusX) / (2f * radiusX), (z - offsetZ + radiusZ) / (2f * radiusZ)); posY = depthi + yOff; } // Some deposits may not appear all over cliffs if (variant.CheckClimate && Math.Abs(origPosY - posY) > variant.MaxYRoughness) { continue; } for (int y = 0; y < thickness; y++) { if (posY <= 1 || posY >= worldheight) { continue; } long index3d = ((posY % chunksize) * chunksize + z) * chunksize + x; ushort blockId = chunks[posY / chunksize].Blocks[index3d]; // Check if we are in mother material, but only if it has changed since last iteration (should reduce amount of these checks by 50-100%) parentBlockOk = false; for (int i = 0; i < variant.ParentBlockIds.Length; i++) { if (variant.ParentBlockIds[i] == blockId) { parentBlockOk = true; blockIndex = i; break; } } if (parentBlockOk) { if (variant.WithBlockCallback) { tmpPos.Set(chunkX * chunksize + x, posY, chunkZ * chunksize + z); blockTypes[variant.BlockIds[blockIndex]].TryPlaceBlockForWorldGen(blockAccessor, tmpPos, BlockFacing.UP); } else { chunks[posY / chunksize].Blocks[index3d] = variant.BlockIds[blockIndex]; } for (int i = 0; i < variant.ChildDeposits.Length; i++) { float rndVal = depositRand.NextFloat(); float quantity = variant.ChildDeposits[i].Quantity * invChunkAreaSize; if (quantity > rndVal) { Vec3i pos = new Vec3i(x, posY, z); if (ShouldPlaceAdjustedForOreMap(variant.ChildDeposits[i], chunkX * chunksize + x, chunkZ * chunksize + z, quantity, rndVal)) { SubDepositsToPlace[pos] = variant.ChildDeposits[i]; } } } if (shouldGenSurfaceDeposit) { int surfaceY = mapchunk.RainHeightMap[z * chunksize + x]; int depth = surfaceY - posY; float chance = variant.SurfaceBlockChance * Math.Max(0, 1 - depth / 8f); if (depositRand.NextFloat() < chance) { index3d = (((surfaceY + 1) % chunksize) * chunksize + z) * chunksize + x; Block belowBlock = api.World.Blocks[chunks[surfaceY / chunksize].Blocks[((surfaceY % chunksize) * chunksize + z) * chunksize + x]]; if (belowBlock.SideSolid[BlockFacing.UP.Index] && chunks[(surfaceY + 1) / chunksize].Blocks[index3d] == 0) { chunks[(surfaceY + 1) / chunksize].Blocks[index3d] = variant.SurfaceBlockIds[blockIndex]; } } } } posY--; } } } return(SubDepositsToPlace); }
private void OnChunkColumnGeneration(IServerChunk[] chunks, int chunkX, int chunkZ) { IntMap forestMap = chunks[0].MapChunk.MapRegion.ForestMap; IntMap climateMap = chunks[0].MapChunk.MapRegion.ClimateMap; ushort[] heightMap = chunks[0].MapChunk.RainHeightMap; int regionChunkSize = api.WorldManager.RegionSize / chunksize; int rlX = chunkX % regionChunkSize; int rlZ = chunkZ % regionChunkSize; // "Pixels per chunk" float facF = (float)forestMap.InnerSize / regionChunkSize; // Retrieves the map data on the chunk edges int forestUpLeft = forestMap.GetUnpaddedInt((int)(rlX * facF), (int)(rlZ * facF)); int forestUpRight = forestMap.GetUnpaddedInt((int)(rlX * facF + facF), (int)(rlZ * facF)); int forestBotLeft = forestMap.GetUnpaddedInt((int)(rlX * facF), (int)(rlZ * facF + facF)); int forestBotRight = forestMap.GetUnpaddedInt((int)(rlX * facF + facF), (int)(rlZ * facF + facF)); float facC = (float)climateMap.InnerSize / regionChunkSize; int climateUpLeft = climateMap.GetUnpaddedInt((int)(rlX * facC), (int)(rlZ * facC)); int climateUpRight = climateMap.GetUnpaddedInt((int)(rlX * facC + facC), (int)(rlZ * facC)); int climateBotLeft = climateMap.GetUnpaddedInt((int)(rlX * facC), (int)(rlZ * facC + facC)); int climateBotRight = climateMap.GetUnpaddedInt((int)(rlX * facC + facC), (int)(rlZ * facC + facC)); for (int x = 0; x < chunksize; x++) { for (int z = 0; z < chunksize; z++) { int posY = heightMap[z * chunksize + x]; int climate = GameMath.BiLerpRgbColor((float)x / chunksize, (float)z / chunksize, climateUpLeft, climateUpRight, climateBotLeft, climateBotRight); int tempUnscaled = (climate >> 16) & 0xff; float temp = TerraGenConfig.GetScaledAdjustedTemperatureFloat(tempUnscaled, posY - TerraGenConfig.seaLevel); float rainRel = TerraGenConfig.GetRainFall((climate >> 8) & 0xff, posY) / 255f; float forestRel = GameMath.BiLerp(forestUpLeft, forestUpRight, forestBotLeft, forestBotRight, (float)x / chunksize, (float)z / chunksize) / 255f; int prevY = posY; posY = PutLayers(x, posY, z, chunks, rainRel, temp, tempUnscaled, heightMap); PlaceTallGrass(x, prevY, z, chunks, rainRel, temp, forestRel); // Try again to put layers if above sealevel and we found over 10 air blocks int foundAir = 0; while (posY >= TerraGenConfig.seaLevel - 1) { int chunkY = posY / chunksize; int lY = posY % chunksize; int index3d = (chunksize * lY + z) * chunksize + x; ushort blockId = chunks[chunkY].Blocks[index3d]; if (blockId == 0) { foundAir++; } else { if (foundAir >= 8) { temp = TerraGenConfig.GetScaledAdjustedTemperatureFloat(tempUnscaled, posY - TerraGenConfig.seaLevel); rainRel = TerraGenConfig.GetRainFall((climate >> 8) & 0xff, posY) / 255f; PutLayers(x, posY, z, chunks, rainRel, temp, tempUnscaled, null); break; } else { foundAir = 0; } } posY--; } } } }
void genTrees(int chunkX, int chunkZ) { int climate = GameMath.BiLerpRgbColor((float)0.5f, (float)0.5f, climateUpLeft, climateUpRight, climateBotLeft, climateBotRight); float wetrel = TerraGenConfig.GetRainFall((climate >> 8) & 0xff, heightmap[(chunksize / 2) * chunksize + chunksize / 2]) / 255f; float dryrel = 1 - wetrel; float drypenalty = 1 - GameMath.Clamp(2f * (dryrel - 0.5f), 0, 0.8f); // Reduce tree generation by up to 70% in low rain places float wetboost = 1 + 3 * Math.Max(0, wetrel - 0.75f); int triesTrees = (int)(treeSupplier.treeGenProps.treesPerChunk.nextFloat() * drypenalty * wetboost); int dx, dz, x, z; Block block; while (triesTrees > 0) { triesTrees--; dx = rnd.NextInt(chunksize); dz = rnd.NextInt(chunksize); x = dx + chunkX * chunksize; z = dz + chunkZ * chunksize; int y = heightmap[dz * chunksize + dx]; if (y <= 0 || y >= worldheight - 15) { continue; } tmpPos.Set(x, y, z); block = blockAccessor.GetBlock(tmpPos); if (block.Fertility == 0) { continue; } // Place according to forest value float treeDensity = GameMath.BiLerp(forestUpLeft, forestUpRight, forestBotLeft, forestBotRight, (float)dx / chunksize, (float)dz / chunksize); climate = GameMath.BiLerpRgbColor((float)dx / chunksize, (float)dz / chunksize, climateUpLeft, climateUpRight, climateBotLeft, climateBotRight); //float shrubChance = GameMath.BiLerp(shrubUpLeft, shrubUpRight, shrubBotLeft, shrubBotRight, (float)dx / chunksize, (float)dz / chunksize); treeDensity = GameMath.Clamp(treeDensity + forestMod * 255, 0, 255); float treeDensityNormalized = treeDensity / 255f; // 1 in 400 chance to always spawn a tree // otherwise go by tree density using a quadratic drop off to create clearer forest edges if (rnd.NextDouble() > Math.Max(0.0025, treeDensityNormalized * treeDensityNormalized) || forestMod <= -1) { continue; } TreeGenForClimate treegenParams = treeSupplier.GetRandomTreeGenForClimate(climate, (int)treeDensity, y); if (treegenParams != null) { bool canGen = true; for (int i = 0; i < structuresIntersectingChunk.Count; i++) { if (structuresIntersectingChunk[i].Location.Contains(tmpPos)) { canGen = false; break; } } if (!canGen) { continue; } if (blockAccessor.GetBlock(tmpPos.X, tmpPos.Y, tmpPos.Z).Replaceable >= 6000) { tmpPos.Y--; } treegenParams.treeGen.GrowTree( blockAccessor, tmpPos, treegenParams.size, treegenParams.vinesGrowthChance ); } } }
public void TryPlacePondAt(int dx, int pondYPos, int dz, int chunkX, int chunkZ, int depth = 0) { searchPositionsDeltas.Clear(); pondPositions.Clear(); // Clear Array for (int i = 0; i < didCheckPosition.Length; i++) { didCheckPosition[i] = false; } int basePosX = chunkX * chunksize; int basePosZ = chunkZ * chunksize; Vec2i tmp = new Vec2i(); searchPositionsDeltas.Enqueue(new Vec2i(dx, dz)); pondPositions.Enqueue(new Vec2i(basePosX + dx, basePosZ + dz)); didCheckPosition[(dz + mapOffset) * searchSize + dx + mapOffset] = true; while (searchPositionsDeltas.Count > 0) { Vec2i p = searchPositionsDeltas.Dequeue(); foreach (BlockFacing facing in BlockFacing.HORIZONTALS) { ndx = p.X + facing.Normali.X; ndz = p.Y + facing.Normali.Z; tmp.Set(chunkX * chunksize + ndx, chunkZ * chunksize + ndz); Block belowBlock = blockAccessor.GetBlock(tmp.X, pondYPos - 1, tmp.Y); bool inBoundary = ndx > minBoundary && ndz > minBoundary && ndx < maxBoundary && ndz < maxBoundary; // Only continue when within our 3x3 chunk search area and having a more or less solid block below (or water) if (inBoundary && (belowBlock.Replaceable < 6000 || belowBlock.BlockId == GlobalConfig.waterBlockId)) { int arrayIndex = (ndz + mapOffset) * searchSize + ndx + mapOffset; // Already checked or did we reach a pond border? if (!didCheckPosition[arrayIndex] && blockAccessor.GetBlock(tmp.X, pondYPos, tmp.Y).Replaceable >= 6000) { searchPositionsDeltas.Enqueue(new Vec2i(ndx, ndz)); pondPositions.Enqueue(tmp.Copy()); didCheckPosition[arrayIndex] = true; } } else { pondPositions.Clear(); searchPositionsDeltas.Clear(); return; } } } if (pondPositions.Count == 0) { return; } int curChunkX, curChunkZ; int prevChunkX = -1, prevChunkZ = -1; int regionChunkSize = api.WorldManager.RegionSize / chunksize; IMapChunk mapchunk = null; IServerChunk chunk = null; IServerChunk chunkOneBlockBelow = null; int ly = GameMath.Mod(pondYPos, chunksize); bool extraPondDepth = rand.NextDouble() > 0.5; bool withSeabed = extraPondDepth || pondPositions.Count > 16; foreach (Vec2i p in pondPositions) { curChunkX = p.X / chunksize; curChunkZ = p.Y / chunksize; int lx = GameMath.Mod(p.X, chunksize); int lz = GameMath.Mod(p.Y, chunksize); // Get correct chunk and correct climate data if we don't have it already if (curChunkX != prevChunkX || curChunkZ != prevChunkZ) { chunk = (IServerChunk)blockAccessor.GetChunk(curChunkX, pondYPos / chunksize, curChunkZ); if (chunk == null) { chunk = api.WorldManager.GetChunk(curChunkX, pondYPos / chunksize, curChunkZ); } chunk.Unpack(); if (ly == 0) { chunkOneBlockBelow = ((IServerChunk)blockAccessor.GetChunk(curChunkX, (pondYPos - 1) / chunksize, curChunkZ)); if (chunkOneBlockBelow == null) { return; } chunkOneBlockBelow.Unpack(); } else { chunkOneBlockBelow = chunk; } mapchunk = chunk.MapChunk; IntDataMap2D climateMap = mapchunk.MapRegion.ClimateMap; float fac = (float)climateMap.InnerSize / regionChunkSize; int rlX = curChunkX % regionChunkSize; int rlZ = curChunkZ % regionChunkSize; climateUpLeft = climateMap.GetUnpaddedInt((int)(rlX * fac), (int)(rlZ * fac)); climateUpRight = climateMap.GetUnpaddedInt((int)(rlX * fac + fac), (int)(rlZ * fac)); climateBotLeft = climateMap.GetUnpaddedInt((int)(rlX * fac), (int)(rlZ * fac + fac)); climateBotRight = climateMap.GetUnpaddedInt((int)(rlX * fac + fac), (int)(rlZ * fac + fac)); prevChunkX = curChunkX; prevChunkZ = curChunkZ; chunkOneBlockBelow.MarkModified(); chunk.MarkModified(); } // Raise heightmap by 1 mapchunk.RainHeightMap[lz * chunksize + lx] = Math.Max(mapchunk.RainHeightMap[lz * chunksize + lx], (ushort)pondYPos); // Identify correct climate at this position int climate = GameMath.BiLerpRgbColor((float)lx / chunksize, (float)lz / chunksize, climateUpLeft, climateUpRight, climateBotLeft, climateBotRight); float temp = TerraGenConfig.GetScaledAdjustedTemperatureFloat((climate >> 16) & 0xff, pondYPos - TerraGenConfig.seaLevel); // 1. Place water or ice block chunk.Blocks[(ly * chunksize + lz) * chunksize + lx] = temp < -5 ? GlobalConfig.lakeIceBlockId : GlobalConfig.waterBlockId; // 2. Let's make a nice muddy gravely sea bed if (!withSeabed) { continue; } // Need to check the block below first int index = ly == 0 ? ((31 * chunksize + lz) * chunksize + lx) : (((ly - 1) * chunksize + lz) * chunksize + lx) ; Block belowBlock = api.World.Blocks[chunkOneBlockBelow.Blocks[index]]; // Water below? Seabed already placed if (belowBlock.IsLiquid()) { continue; } float rainRel = TerraGenConfig.GetRainFall((climate >> 8) & 0xff, pondYPos) / 255f; int rockBlockId = mapchunk.TopRockIdMap[lz * chunksize + lx]; if (rockBlockId == 0) { continue; } for (int i = 0; i < lakebedLayerConfig.BlockCodeByMin.Length; i++) { if (lakebedLayerConfig.BlockCodeByMin[i].Suitable(temp, rainRel, (float)pondYPos / mapheight, rand)) { chunkOneBlockBelow.Blocks[index] = lakebedLayerConfig.BlockCodeByMin[i].GetBlockForMotherRock(rockBlockId); break; } } } if (pondPositions.Count > 0 && extraPondDepth) { TryPlacePondAt(dx, pondYPos + 1, dz, chunkX, chunkZ, depth + 1); } }