public void Generate(IBlockAccessor blockAccessor, LCGRandom rnd, int posX, int posY, int posZ, int firstBlockId) { float quantity = Quantity.nextFloat() + 1; int chunkSize = blockAccessor.ChunkSize; Block[] blocks = getBlocks(firstBlockId); if (blocks.Length == 0) { return; } while (quantity-- > 0) { if (quantity < 1 && rnd.NextDouble() > quantity) { break; } pos.X = posX + (int)OffsetX.nextFloat(); pos.Z = posZ + (int)OffsetZ.nextFloat(); int index = GameMath.Mod((int)BlockCodeIndex.nextFloat(), blocks.Length); IServerChunk chunk = (IServerChunk)blockAccessor.GetChunk(pos.X / chunkSize, 0, pos.Z / chunkSize); if (chunk == null) { break; } int lx = GameMath.Mod(pos.X, chunkSize); int lz = GameMath.Mod(pos.Z, chunkSize); if (Placement == EnumBlockPatchPlacement.Underground) { pos.Y = rnd.NextInt(Math.Max(1, chunk.MapChunk.WorldGenTerrainHeightMap[lz * blockAccessor.ChunkSize + lx] - 1)); } else { pos.Y = chunk.MapChunk.RainHeightMap[lz * blockAccessor.ChunkSize + lx] + 1; if (Math.Abs(pos.Y - posY) > 8 || pos.Y >= blockAccessor.MapSizeY - 1) { continue; } if (Placement == EnumBlockPatchPlacement.UnderWater) { tempPos.Set(pos.X, pos.Y - GameMath.Max(1, MinWaterDepth), pos.Z); Block downBlock = blockAccessor.GetBlock(tempPos); if (downBlock == null || downBlock.LiquidCode != "water") { continue; } } } blocks[index].TryPlaceBlockForWorldGen(blockAccessor, pos, BlockFacing.UP, rnd); } }
private bool isChunkAreaLoaded(IBlockAccessor blockAccessor, int growRange) { int chunksize = blockAccessor.ChunkSize; int mincx = (Pos.X - growRange) / chunksize; int maxcx = (Pos.X + growRange) / chunksize; int mincz = (Pos.Z - growRange) / chunksize; int maxcz = (Pos.Z + growRange) / chunksize; for (int cx = mincx; cx <= maxcx; cx++) { for (int cz = mincz; cz <= maxcz; cz++) { if (blockAccessor.GetChunk(cx, Pos.Y / chunksize, cz) == null) { return(false); } } } return(true); }
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); } }