public override void StartServerSide(ICoreServerAPI api)
        {
            base.StartServerSide(api);
            api.RegisterCommand("cleanworld", "Reverts ? blocks that have an ID but do not have an associated block code to air. Only works on chunks that have been loaded. Requires world save and restart.", "",
                                (IServerPlayer player, int groupId, CmdArgs args) =>
            {
                IBlockAccessor worldBlockAccessor = api.World.BlockAccessor;
                IServerChunk currentChunk         = api.WorldManager.GetChunk(player.Entity.Pos.AsBlockPos);
                int chunkSize = worldBlockAccessor.ChunkSize;
                Dictionary <long, IServerChunk> loadedChunks = api.WorldManager.AllLoadedChunks;

                for (int i = 0; i < loadedChunks.Count; i++)
                {
                    currentChunk = loadedChunks.ElementAt(i).Value;

                    for (int x = 0; x < chunkSize; x++)
                    {
                        for (int y = 0; y < chunkSize; y++)
                        {
                            for (int z = 0; z < chunkSize; z++)
                            {
                                if (currentChunk.Blocks != null)
                                {
                                    if (api.World.GetBlock(currentChunk.Blocks[(y * chunkSize + z) * chunkSize + x]).Code == null)
                                    {
                                        currentChunk.Blocks[(y * chunkSize + z) * chunkSize + x] = 0;
                                    }
                                }
                            }
                        }
                    }
                    currentChunk.MarkModified();
                }
            }, Privilege.controlserver);
        }
Esempio n. 2
0
        private BlockPos HasExitPoint(BlockPos nearpos)
        {
            IServerChunk chunk = api.World.BlockAccessor.GetChunkAtBlockPos(nearpos) as IServerChunk;
            List <GeneratedStructure> structures = chunk?.MapChunk?.MapRegion?.GeneratedStructures;

            if (structures == null)
            {
                return(null);
            }

            foreach (var structure in structures)
            {
                if (structure.Code.Contains("gates"))
                {
                    Cuboidi  loc      = structure.Location;
                    BlockPos foundPos = null;
                    api.World.BlockAccessor.WalkBlocks(loc.Start.AsBlockPos, loc.End.AsBlockPos, (block, pos) =>
                    {
                        BlockStaticTranslocator transBlock = block as BlockStaticTranslocator;

                        if (transBlock != null && !transBlock.Repaired)
                        {
                            foundPos = pos.Copy();
                        }
                    });

                    if (foundPos != null)
                    {
                        return(foundPos);
                    }
                }
            }

            return(null);
        }
Esempio n. 3
0
        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);
            }
        }
Esempio n. 4
0
        private int RebuildRainMap()
        {
            int mapChunksRebuilt = 0;

            Dictionary <long, IMapChunk> mapchunks = sapi.WorldManager.AllLoadedMapchunks;
            int ymax = sapi.WorldManager.MapSizeY / sapi.WorldManager.ChunkSize;

            IServerChunk[] column    = new IServerChunk[ymax];
            int            chunksize = sapi.WorldManager.ChunkSize;

            foreach (var val in mapchunks)
            {
                int cx = (int)(val.Key % (sapi.WorldManager.MapSizeX / chunksize));
                int cz = (int)(val.Key / (sapi.WorldManager.MapSizeX / chunksize));
                mapChunksRebuilt++;


                for (int cy = 0; cy < ymax; cy++)
                {
                    column[cy] = sapi.WorldManager.GetChunk(cx, cy, cz);
                    column[cy]?.Unpack();
                }

                for (int dx = 0; dx < chunksize; dx++)
                {
                    for (int dz = 0; dz < chunksize; dz++)
                    {
                        for (int dy = sapi.WorldManager.MapSizeY - 1; dy >= 0; dy--)
                        {
                            IServerChunk chunk = column[dy / chunksize];
                            if (chunk == null)
                            {
                                continue;
                            }

                            int   index = ((dy % chunksize) * chunksize + dz) * chunksize + dx;
                            Block block = sapi.World.Blocks[chunk.Blocks[index]];

                            if (!block.RainPermeable || dy == 0)
                            {
                                val.Value.RainHeightMap[dz * chunksize + dx] = (ushort)dy;
                                break;
                            }
                        }
                    }
                }


                sapi.WorldManager.ResendMapChunk(cx, cz, true);
                val.Value.MarkDirty();
            }

            return(mapChunksRebuilt);
        }
Esempio n. 5
0
        private IServerChunk[] GetChunkColumn(int chunkX, int chunkZ)
        {
            int size = api.World.BlockAccessor.MapSizeY / chunksize;

            IServerChunk[] chunks = new IServerChunk[size];
            for (int chunkY = 0; chunkY < size; chunkY++)
            {
                chunks[chunkY] = api.WorldManager.GetChunk(chunkX, chunkY, chunkZ);
            }

            return(chunks);
        }
Esempio n. 6
0
        private void OnChunkColumnGen(IServerChunk[] chunks, int chunkX, int chunkZ, ITreeAttribute chunkGenParams = null)
        {
            Dictionary <Vec3i, int> ores = new Dictionary <Vec3i, int>();

            for (int cY = 0; cY < chunks.Length; cY++)
            {
                IServerChunk chunk = chunks[cY];
                if (chunk.Blocks == null)
                {
                    continue;
                }
                for (int x = 0; x < chunksize; x++)
                {
                    for (int y = 0; y < chunksize; y++)
                    {
                        for (int z = 0; z < chunksize; z++)
                        {
                            int block = chunk.Blocks[(y * chunksize + z) * chunksize + x];
                            if (surfaceBlocks.ContainsKey(block) && !ores.ContainsKey(new Vec3i(x, y, z)))
                            {
                                ores.Add(new Vec3i(x, y, z), block);
                            }
                        }
                    }
                }
                foreach (var val in ores)
                {
                    Vec3i vec = val.Key;
                    int   ore = val.Value;
                    if (surfaceBlocks.TryGetValue(ore, out int surface))
                    {
                        for (int y = vec.Y; y < chunksize; y++)
                        {
                            rnd.InitPositionSeed(chunkX * vec.X, chunkZ * vec.Z);
                            if (y < 1 || rnd.NextDouble() > 0.1)
                            {
                                continue;
                            }
                            int dX = rnd.NextInt(chunksize), dZ = rnd.NextInt(chunksize);

                            int block  = chunk.Blocks[(y * chunksize + dZ) * chunksize + dX];
                            int dBlock = chunk.Blocks[((y - 1) * chunksize + dZ) * chunksize + dX];
                            if (bA.GetBlock(dBlock).Fertility > 4 && bA.GetBlock(block).IsReplacableBy(bA.GetBlock(ore)) && !bA.GetBlock(block).IsLiquid())
                            {
                                chunk.Blocks[(y * chunksize + dZ) * chunksize + dX] = surface;
                                bA.ScheduleBlockUpdate(new BlockPos(dX, y, dZ));
                            }
                        }
                    }
                }
            }
        }
Esempio n. 7
0
        void rebuildRainmap(int cx, int cz)
        {
            ICoreServerAPI sapi = api as ICoreServerAPI;

            int ymax = sapi.WorldManager.MapSizeY / sapi.WorldManager.ChunkSize;

            IServerChunk[] column    = new IServerChunk[ymax];
            int            chunksize = sapi.WorldManager.ChunkSize;

            IMapChunk mapchunk = null;

            for (int cy = 0; cy < ymax; cy++)
            {
                column[cy] = sapi.WorldManager.GetChunk(cx, cy, cz);
                column[cy]?.Unpack();

                mapchunk = column[cy]?.MapChunk;
            }

            if (mapchunk == null)
            {
                return;
            }

            for (int dx = 0; dx < chunksize; dx++)
            {
                for (int dz = 0; dz < chunksize; dz++)
                {
                    for (int dy = sapi.WorldManager.MapSizeY - 1; dy >= 0; dy--)
                    {
                        IServerChunk chunk = column[dy / chunksize];
                        if (chunk == null)
                        {
                            continue;
                        }

                        int   index = ((dy % chunksize) * chunksize + dz) * chunksize + dx;
                        Block block = sapi.World.Blocks[chunk.Blocks[index]];

                        if (!block.RainPermeable || dy == 0)
                        {
                            mapchunk.RainHeightMap[dz * chunksize + dx] = (ushort)dy;
                            break;
                        }
                    }
                }
            }


            sapi.WorldManager.ResendMapChunk(cx, cz, true);
            mapchunk.MarkDirty();
        }
Esempio n. 8
0
        private void ClearChunkColumn(IServerChunk[] chunks)
        {
            for (int i = 0; i < chunks.Length; i++)
            {
                IServerChunk chunk = chunks[i];
                chunk.Unpack();

                for (int j = 0; j < chunk.Blocks.Length; j++)
                {
                    chunk.Blocks[j] = 0;
                }

                chunk.MarkModified();
            }
        }
Esempio n. 9
0
        private void OnChunkColumnGen(IServerChunk[] chunks, int chunkX, int chunkZ, ITreeAttribute chunkGenParams = null)
        {
            int seaLevel = TerraGenConfig.seaLevel - 1;
            int chunkY   = seaLevel / chunksize;

            chunkVisitedNodes.Clear();

            for (int cy = chunkY; cy < chunks.Length; cy++)
            {
                IServerChunk chunk   = chunks[cy];
                int          index3d = 0;
                if (cy * chunksize < seaLevel)
                {
                    index3d = (seaLevel - cy * chunksize) * chunksize * chunksize;
                }

                for (; index3d < chunk.Blocks.Length; index3d++)
                {
                    int blockId = chunk.Blocks[index3d];
                    if (blockId == 0)
                    {
                        continue;
                    }


                    // Check up to 5 blocks below if there is air
                    for (int i = 1; i < 5; i++)
                    {
                        int index3dBelow = index3d - chunksize * chunksize;
                        int curcy        = cy;

                        if (index3dBelow < 0)
                        {
                            index3dBelow += chunksize * chunksize * chunksize;
                            curcy--;
                        }

                        blockId = chunks[curcy].Blocks[index3dBelow];

                        if (blockId == 0)
                        {
                            deletePotentialFloatingBlocks(index3d, chunkX, cy, chunkZ);
                            break;
                        }
                    }
                }
            }
        }
        private BlockPos FindTranslocator(Cuboidi location, Dictionary <Vec2i, IServerChunk[]> columnsByChunkCoordinate, int centerCx, int centerCz)
        {
            int chunksize = Api.World.BlockAccessor.ChunkSize;

            for (int x = location.X1; x < location.X2; x++)
            {
                for (int y = location.Y1; y < location.Y2; y++)
                {
                    for (int z = location.Z1; z < location.Z2; z++)
                    {
                        int cx = x / chunksize;
                        int cy = y / chunksize;
                        int cz = z / chunksize;

                        IServerChunk[] chunks = null;
                        if (!columnsByChunkCoordinate.TryGetValue(new Vec2i(cx, cz), out chunks))
                        {
                            continue;
                        }

                        IServerChunk chunk = chunks[y / chunksize];

                        int lx = x % chunksize;
                        int ly = y % chunksize;
                        int lz = z % chunksize;

                        int   index3d = (ly * chunksize + lz) * chunksize + lx;
                        Block block   = Api.World.Blocks[chunk.Blocks[index3d]];

                        BlockStaticTranslocator transBlock = block as BlockStaticTranslocator;
                        if (transBlock != null && !transBlock.Repaired)
                        {
                            return(new BlockPos(x, y, z));
                        }
                    }
                }
            }

            return(null);
        }
Esempio n. 11
0
        private void OnChunkColumnGeneration(IServerChunk[] chunks, int chunkX, int chunkZ, ITreeAttribute chunkGenParams = null)
        {
            // Because blockdata is cached and not cleaned after release
            for (int y = 0; y < chunks.Length; y++)
            {
                IServerChunk chunk = chunks[y];
                for (int i = 0; i < chunk.Blocks.Length; i++)
                {
                    chunk.Blocks[i] = 0;
                }
            }


            IServerChunk botChunk = chunks[0];

            ushort[] rainheightmap    = chunks[0].MapChunk.RainHeightMap;
            ushort[] terrainheightmap = chunks[0].MapChunk.WorldGenTerrainHeightMap;


            int yMove = chunksize * chunksize;

            for (int x = 0; x < chunksize; x++)
            {
                for (int z = 0; z < chunksize; z++)
                {
                    int index3d = z * chunksize + x;

                    rainheightmap[index3d]    = (ushort)(blockIds.Length - 1);
                    terrainheightmap[index3d] = (ushort)(blockIds.Length - 1);

                    for (int i = 0; i < blockIds.Length; i++)
                    {
                        botChunk.Blocks[index3d] = blockIds[i];
                        index3d += yMove;
                    }
                }
            }

            chunks[0].MapChunk.YMax = (ushort)blockIds.Length;
        }
Esempio n. 12
0
        private bool ClearChunkColumn(IServerChunk[] chunks)
        {
            for (int i = 0; i < chunks.Length; i++)
            {
                IServerChunk chunk = chunks[i];
                if (chunk == null)
                {
                    return(false);
                }

                chunk.Unpack();

                for (int j = 0; j < chunk.Blocks.Length; j++)
                {
                    chunk.Blocks[j] = 0;
                }

                chunk.MarkModified();
            }

            return(true);
        }
Esempio n. 13
0
        Dictionary <int, BlockReinforcement> getOrCreateReinforcmentsAt(BlockPos pos)
        {
            IServerChunk chunk = (api as ICoreServerAPI).WorldManager.GetChunk(pos);

            // Fix v1.8 game engine bug (can be removed for v1.9)
            if ((chunk as ServerChunk).Moddata == null)
            {
                (chunk as ServerChunk).Moddata = new Dictionary <string, byte[]>();
            }

            byte[] data = chunk.GetModdata("reinforcements");
            Dictionary <int, BlockReinforcement> reinforcmentsOfChunk = null;

            if (data != null)
            {
                reinforcmentsOfChunk = SerializerUtil.Deserialize <Dictionary <int, BlockReinforcement> >(data);
            }
            else
            {
                reinforcmentsOfChunk = new Dictionary <int, BlockReinforcement>();
            }

            return(reinforcmentsOfChunk);
        }
Esempio n. 14
0
        void saveReinforcments(Dictionary <int, BlockReinforcement> reif, BlockPos pos)
        {
            IServerChunk chunk = (api as ICoreServerAPI).WorldManager.GetChunk(pos);

            chunk.SetModdata("reinforcements", SerializerUtil.Serialize(reif));
        }
Esempio n. 15
0
        void ReadPos(IServerPlayer player, CmdArgs arguments)
        {
            if (arguments.Length < 2)
            {
                player.SendMessage(groupId, "/wgen pos [gprov|landform|climate|height]", EnumChatType.CommandError);
                return;
            }


            int          chunkSize   = api.WorldManager.ChunkSize;
            BlockPos     pos         = player.Entity.Pos.AsBlockPos;
            IServerChunk serverchunk = api.WorldManager.GetChunk(pos);

            if (serverchunk == null)
            {
                player.SendMessage(groupId, "Can't check here, beyond chunk boundaries!", EnumChatType.CommandError);
                return;
            }

            IMapRegion mapRegion = serverchunk.MapChunk.MapRegion;
            IMapChunk  mapchunk  = serverchunk.MapChunk;

            int regionChunkSize = api.WorldManager.RegionSize / chunkSize;


            int lx      = pos.X % chunkSize;
            int lz      = pos.Z % chunkSize;
            int chunkX  = pos.X / chunkSize;
            int chunkZ  = pos.Z / chunkSize;
            int regionX = pos.X / regionSize;
            int regionZ = pos.Z / regionSize;

            switch (arguments[1])
            {
            case "coords":
                player.SendMessage(groupId, string.Format("Chunk X/Z: {0}/{1}, Region X/Z: {2},{3}", chunkX, chunkZ, regionX, regionZ), EnumChatType.CommandSuccess);
                break;

            case "structures":
                bool found = false;
                api.World.BlockAccessor.WalkStructures(pos, (struc) =>
                {
                    found = true;
                    player.SendMessage(groupId, "Structure with code " + struc.Code + " at this position", EnumChatType.CommandSuccess);
                });

                if (!found)
                {
                    player.SendMessage(groupId, "No structures at this position", EnumChatType.CommandSuccess);
                }

                return;


            case "height":
            {
                string str = string.Format("Rain y={0}, Worldgen terrain y={1}", serverchunk.MapChunk.RainHeightMap[lz * chunkSize + lx], serverchunk.MapChunk.WorldGenTerrainHeightMap[lz * chunkSize + lx]);
                player.SendMessage(groupId, str, EnumChatType.CommandSuccess);
            }
            break;


            case "cavedistort":
                Bitmap bmp = new Bitmap(chunkSize, chunkSize);

                for (int x = 0; x < chunkSize; x++)
                {
                    for (int z = 0; z < chunkSize; z++)
                    {
                        byte color = mapchunk.CaveHeightDistort[z * chunkSize + x];
                        bmp.SetPixel(x, z, Color.FromArgb((color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff));
                    }
                }

                bmp.Save("cavedistort" + chunkX + "-" + chunkZ + ".png");
                player.SendMessage(groupId, "saved bitmap cavedistort" + chunkX + "-" + chunkZ + ".png", EnumChatType.CommandSuccess);
                break;


            case "gprov":
            {
                int noiseSizeGeoProv = mapRegion.GeologicProvinceMap.InnerSize;

                float posXInRegion = ((float)pos.X / regionSize - regionX) * noiseSizeGeoProv;
                float posZInRegion = ((float)pos.Z / regionSize - regionZ) * noiseSizeGeoProv;
                GeologicProvinceVariant[] provincesByIndex = NoiseGeoProvince.provinces.Variants;
                IntMap intmap = mapRegion.GeologicProvinceMap;
                LerpedWeightedIndex2DMap map     = new LerpedWeightedIndex2DMap(intmap.Data, mapRegion.GeologicProvinceMap.Size, TerraGenConfig.geoProvSmoothingRadius, mapRegion.GeologicProvinceMap.TopLeftPadding, mapRegion.GeologicProvinceMap.BottomRightPadding);
                WeightedIndex[]          indices = map[posXInRegion, posZInRegion];

                string text = "";
                foreach (WeightedIndex windex in indices)
                {
                    if (text.Length > 0)
                    {
                        text += ", ";
                    }

                    text += (100 * windex.Weight).ToString("#.#") + "% " + provincesByIndex[windex.Index].Code;
                }

                player.SendMessage(groupId, text, EnumChatType.CommandSuccess);

                break;
            }


            case "rockstrata":
            {
                GenRockStrataNew rockstratagen = api.ModLoader.GetModSystem <GenRockStrataNew>();

                int   noiseSizeGeoProv = mapRegion.GeologicProvinceMap.InnerSize;
                float posXInRegion     = ((float)pos.X / regionSize - pos.X / regionSize) * noiseSizeGeoProv;
                float posZInRegion     = ((float)pos.Z / regionSize - pos.Z / regionSize) * noiseSizeGeoProv;
                GeologicProvinceVariant[] provincesByIndex = NoiseGeoProvince.provinces.Variants;
                IntMap intmap = mapRegion.GeologicProvinceMap;
                LerpedWeightedIndex2DMap map     = new LerpedWeightedIndex2DMap(intmap.Data, mapRegion.GeologicProvinceMap.Size, TerraGenConfig.geoProvSmoothingRadius, mapRegion.GeologicProvinceMap.TopLeftPadding, mapRegion.GeologicProvinceMap.BottomRightPadding);
                WeightedIndex[]          indices = map[posXInRegion, posZInRegion];

                float[] rockGroupMaxThickness = new float[4];

                rockGroupMaxThickness[0] = rockGroupMaxThickness[1] = rockGroupMaxThickness[2] = rockGroupMaxThickness[3] = 0;

                int    rdx = chunkX % regionChunkSize;
                int    rdz = chunkZ % regionChunkSize;
                IntMap rockMap;
                float  step  = 0;
                float  distx = (float)rockstratagen.distort2dx.Noise(pos.X, pos.Z);
                float  distz = (float)rockstratagen.distort2dz.Noise(pos.X, pos.Z);



                for (int i = 0; i < indices.Length; i++)
                {
                    float w = indices[i].Weight;

                    GeologicProvinceVariant var = NoiseGeoProvince.provinces.Variants[indices[i].Index];

                    rockGroupMaxThickness[0] += var.RockStrataIndexed[0].MaxThickness * w;
                    rockGroupMaxThickness[1] += var.RockStrataIndexed[1].MaxThickness * w;
                    rockGroupMaxThickness[2] += var.RockStrataIndexed[2].MaxThickness * w;
                    rockGroupMaxThickness[3] += var.RockStrataIndexed[3].MaxThickness * w;
                }

                System.Text.StringBuilder sb = new System.Text.StringBuilder();

                sb.AppendLine("Sedimentary max thickness: " + rockGroupMaxThickness[(int)EnumRockGroup.Sedimentary]);
                sb.AppendLine("Metamorphic max thickness: " + rockGroupMaxThickness[(int)EnumRockGroup.Metamorphic]);
                sb.AppendLine("Igneous max thickness: " + rockGroupMaxThickness[(int)EnumRockGroup.Igneous]);
                sb.AppendLine("Volcanic max thickness: " + rockGroupMaxThickness[(int)EnumRockGroup.Volcanic]);
                sb.AppendLine("========");

                for (int id = 0; id < rockstratagen.strata.Variants.Length; id++)
                {
                    rockMap = mapchunk.MapRegion.RockStrata[id];
                    step    = (float)rockMap.InnerSize / regionChunkSize;

                    float dist = 1 + GameMath.Clamp((distx + distz) / 30, 0.9f, 1.1f);
                    sb.AppendLine(rockstratagen.strata.Variants[id].BlockCode.ToShortString() + " max thickness: " + rockMap.GetIntLerpedCorrectly(rdx * step + step * (float)(lx + distx) / chunkSize, rdz * step + step * (float)(lz + distz) / chunkSize));
                }

                sb.AppendLine("======");

                int         surfaceY        = api.World.BlockAccessor.GetTerrainMapheightAt(pos);
                int         ylower          = 1;
                int         yupper          = surfaceY;
                int         rockStrataId    = -1;
                float       strataThickness = 0;
                RockStratum stratum         = null;


                OrderedDictionary <int, int> stratathicknesses = new OrderedDictionary <int, int>();

                while (ylower <= yupper)
                {
                    if (--strataThickness <= 0)
                    {
                        rockStrataId++;
                        if (rockStrataId >= rockstratagen.strata.Variants.Length)
                        {
                            break;
                        }
                        stratum = rockstratagen.strata.Variants[rockStrataId];
                        rockMap = mapchunk.MapRegion.RockStrata[rockStrataId];
                        step    = (float)rockMap.InnerSize / regionChunkSize;

                        int grp = (int)stratum.RockGroup;

                        float dist = 1 + GameMath.Clamp((distx + distz) / 30, 0.9f, 1.1f);
                        strataThickness = Math.Min(rockGroupMaxThickness[grp] * dist, rockMap.GetIntLerpedCorrectly(rdx * step + step * (float)(lx + distx) / chunkSize, rdz * step + step * (float)(lz + distz) / chunkSize));

                        strataThickness -= (stratum.RockGroup == EnumRockGroup.Sedimentary) ? Math.Max(0, yupper - TerraGenConfig.seaLevel) * 0.5f : 0;

                        if (strataThickness < 2)
                        {
                            strataThickness = -1;
                            continue;
                        }
                    }

                    if (!stratathicknesses.ContainsKey(stratum.BlockId))
                    {
                        stratathicknesses[stratum.BlockId] = 0;
                    }
                    stratathicknesses[stratum.BlockId]++;

                    if (stratum.GenDir == EnumStratumGenDir.BottomUp)
                    {
                        ylower++;
                    }
                    else
                    {
                        yupper--;
                    }
                }

                foreach (var val in stratathicknesses)
                {
                    sb.AppendLine(api.World.Blocks[val.Key].Code.ToShortString() + " : " + val.Value + " blocks");
                }


                player.SendMessage(groupId, sb.ToString(), EnumChatType.CommandSuccess);

                break;
            }


            case "landform":
            {
                int noiseSizeLandform = mapRegion.LandformMap.InnerSize;

                float posXInRegion = ((float)pos.X / regionSize - pos.X / regionSize) * noiseSizeLandform;
                float posZInRegion = ((float)pos.Z / regionSize - pos.Z / regionSize) * noiseSizeLandform;


                LandformVariant[] landforms = NoiseLandforms.landforms.LandFormsByIndex;

                IntMap intmap = mapRegion.LandformMap;

                LerpedWeightedIndex2DMap map = new LerpedWeightedIndex2DMap(intmap.Data, mapRegion.LandformMap.Size, TerraGenConfig.landFormSmoothingRadius, intmap.TopLeftPadding, intmap.BottomRightPadding);

                WeightedIndex[] indices = map[posXInRegion, posZInRegion];

                string text = "";
                foreach (WeightedIndex windex in indices)
                {
                    if (text.Length > 0)
                    {
                        text += ", ";
                    }

                    text += (100 * windex.Weight).ToString("#.#") + "% " + landforms[windex.Index].Code;
                }

                player.SendMessage(groupId, text, EnumChatType.CommandSuccess);

                break;
            }

            case "climate":
            {
                ClimateCondition climate = api.World.BlockAccessor.GetClimateAt(pos);

                string text = string.Format("Temperature: {0}°, Rainfall: {1}%, Fertility: {2}%, Forest: {3}%, Shrub: {4}%, Sealevel dist: {5}%", climate.Temperature.ToString("0.#"), (int)(climate.Rainfall * 100f), (int)(climate.Fertility * 100f), (int)(climate.ForestDensity * 100f), (int)(climate.ShrubDensity * 100f), (int)(100f * pos.Y / 255f));

                player.SendMessage(groupId, text, EnumChatType.CommandSuccess);


                break;
            }
            }
        }
Esempio n. 16
0
        private void ReadRegion(IServerPlayer player, CmdArgs arguments)
        {
            if (arguments.Length < 1)
            {
                player.SendMessage(groupId, "/wgen region [climate|ore|forest|wind|gprov|gprovi|landform|landformi]", EnumChatType.CommandError);
                return;
            }

            int          chunkSize   = api.WorldManager.ChunkSize;
            BlockPos     pos         = player.Entity.Pos.AsBlockPos;
            IServerChunk serverchunk = api.WorldManager.GetChunk(pos);

            if (serverchunk == null)
            {
                player.SendMessage(groupId, "Can't check here, beyond chunk boundaries!", EnumChatType.CommandError);
                return;
            }

            IMapRegion mapRegion = serverchunk.MapChunk.MapRegion;


            int regionX = pos.X / regionSize;
            int regionZ = pos.Z / regionSize;

            string arg = arguments.PopWord();

            string subarg = arguments.PopWord();
            bool   dolerp = subarg == "nolerp";

            NoiseBase.Debug = true;

            switch (arg)
            {
            case "climate":
                DrawMapRegion(0, player, mapRegion.ClimateMap, "climate", dolerp, regionX, regionZ, TerraGenConfig.climateMapScale);
                break;

            case "ore":
                string type = dolerp ? arguments.PopWord("limonite") : subarg;
                if (type == null)
                {
                    type = "limonite";
                }

                if (!mapRegion.OreMaps.ContainsKey(type))
                {
                    player.SendMessage(groupId, "Mapregion does not contain an ore map for ore " + type, EnumChatType.CommandError);
                    return;
                }

                DrawMapRegion(DebugDrawMode.RGB, player, mapRegion.OreMaps[type], "ore-" + type, dolerp, regionX, regionZ, TerraGenConfig.oreMapScale);
                break;

            case "forest":
                DrawMapRegion(DebugDrawMode.FirstByteGrayscale, player, mapRegion.ForestMap, "forest", dolerp, regionX, regionZ, TerraGenConfig.forestMapScale);
                break;


            case "oretopdistort":
                DrawMapRegion(DebugDrawMode.FirstByteGrayscale, player, mapRegion.OreMapVerticalDistortTop, "oretopdistort", dolerp, regionX, regionZ, TerraGenConfig.depositVerticalDistortScale);
                break;

            //case "depdist":
            //DrawMapRegion(0, player, mapRegion.DepositDistortionMap, "depositdistortion", dolerp, regionX, regionZ, TerraGenConfig.depositDistortionScale);
            //break;

            case "rockstrata":

                for (int i = 0; i < mapRegion.RockStrata.Length; i++)
                {
                    DrawMapRegion(DebugDrawMode.FirstByteGrayscale, player, mapRegion.RockStrata[i], "rockstrata" + i, dolerp, regionX, regionZ, TerraGenConfig.rockStrataScale);
                }
                break;

            case "wind":
            {
            }
            break;


            case "gprov":
                DrawMapRegion(DebugDrawMode.ProvinceRGB, player, mapRegion.GeologicProvinceMap, "province", dolerp, regionX, regionZ, TerraGenConfig.geoProvMapScale);
                break;


            case "gprovi":
            {
                int[] gprov            = mapRegion.GeologicProvinceMap.Data;
                int   noiseSizeGeoProv = mapRegion.GeologicProvinceMap.InnerSize;

                int outSize = (noiseSizeGeoProv + TerraGenConfig.geoProvMapPadding - 1) * TerraGenConfig.geoProvMapScale;

                GeologicProvinceVariant[] provincesByIndex = NoiseGeoProvince.provinces.Variants;

                LerpedWeightedIndex2DMap map = new LerpedWeightedIndex2DMap(gprov, noiseSizeGeoProv + 2 * TerraGenConfig.geoProvMapPadding, 2, mapRegion.GeologicProvinceMap.TopLeftPadding, mapRegion.GeologicProvinceMap.BottomRightPadding);

                int[] outColors = new int[outSize * outSize];
                for (int x = 0; x < outSize; x++)
                {
                    for (int z = 0; z < outSize; z++)
                    {
                        WeightedIndex[] indices = map[(float)x / TerraGenConfig.geoProvMapScale, (float)z / TerraGenConfig.geoProvMapScale];
                        for (int i = 0; i < indices.Length; i++)
                        {
                            indices[i].Index = provincesByIndex[indices[i].Index].ColorInt;
                        }
                        int[]   colors;
                        float[] weights;
                        map.Split(indices, out colors, out weights);
                        outColors[z * outSize + x] = ColorUtil.ColorAverage(colors, weights);
                    }
                }

                NoiseBase.DebugDrawBitmap(DebugDrawMode.ProvinceRGB, outColors, outSize, outSize, "geoprovince-lerped-" + regionX + "-" + regionZ);

                player.SendMessage(groupId, "done", EnumChatType.CommandSuccess);

                break;
            }



            case "landform":
                DrawMapRegion(DebugDrawMode.LandformRGB, player, mapRegion.LandformMap, "landform", dolerp, regionX, regionZ, TerraGenConfig.landformMapScale);
                break;



            case "landformi":
            {
                int[] data = mapRegion.LandformMap.Data;
                int   noiseSizeLandform = mapRegion.LandformMap.InnerSize;

                int outSize = (noiseSizeLandform + TerraGenConfig.landformMapPadding - 1) * TerraGenConfig.landformMapScale;

                LandformVariant[] landformsByIndex = NoiseLandforms.landforms.LandFormsByIndex;

                LerpedWeightedIndex2DMap map = new LerpedWeightedIndex2DMap(data, mapRegion.LandformMap.Size, 1, mapRegion.LandformMap.TopLeftPadding, mapRegion.LandformMap.BottomRightPadding);

                int[] outColors = new int[outSize * outSize];
                for (int x = 0; x < outSize; x++)
                {
                    for (int z = 0; z < outSize; z++)
                    {
                        WeightedIndex[] indices = map[(float)x / TerraGenConfig.landformMapScale, (float)z / TerraGenConfig.landformMapScale];
                        for (int i = 0; i < indices.Length; i++)
                        {
                            indices[i].Index = landformsByIndex[indices[i].Index].ColorInt;
                        }
                        int[]   colors;
                        float[] weights;
                        map.Split(indices, out colors, out weights);
                        outColors[z * outSize + x] = ColorUtil.ColorAverage(colors, weights);
                    }
                }

                NoiseBase.DebugDrawBitmap(DebugDrawMode.LandformRGB, outColors, outSize, outSize, "landform-lerped-" + regionX + "-" + regionZ);

                player.SendMessage(groupId, "Landform map done", EnumChatType.CommandSuccess);

                break;
            }


            default:
                player.SendMessage(groupId, "/wgen region [climate|ore|forest|wind|gprov|landform]", EnumChatType.CommandError);
                break;
            }

            NoiseBase.Debug = false;
        }
Esempio n. 17
0
        private void DelRock(IServerPlayer player, CmdArgs arguments, bool aroundPlayer = false)
        {
            player.SendMessage(groupId, "Deleting rock, this may take a while...", EnumChatType.CommandError);

            int chunkMidX = api.WorldManager.MapSizeX / api.WorldManager.ChunkSize / 2;
            int chunkMidZ = api.WorldManager.MapSizeZ / api.WorldManager.ChunkSize / 2;

            if (aroundPlayer)
            {
                chunkMidX = (int)player.Entity.Pos.X / api.WorldManager.ChunkSize;
                chunkMidZ = (int)player.Entity.Pos.Z / api.WorldManager.ChunkSize;
            }

            List <Vec2i> coords = new List <Vec2i>();

            int rad = 2;

            if (arguments.Length > 1)
            {
                int.TryParse(arguments[1], out rad);
            }

            for (int x = -rad; x <= rad; x++)
            {
                for (int z = -rad; z <= rad; z++)
                {
                    coords.Add(new Vec2i(chunkMidX + x, chunkMidZ + z));
                }
            }

            int chunksize = api.WorldManager.ChunkSize;

            List <Block> blocks = api.World.Blocks;

            foreach (Vec2i coord in coords)
            {
                for (int cy = 0; cy < api.WorldManager.MapSizeY / api.World.BlockAccessor.ChunkSize; cy++)
                {
                    IServerChunk chunk = api.WorldManager.GetChunk(coord.X, cy, coord.Y);
                    if (chunk == null)
                    {
                        continue;
                    }

                    chunk.Unpack();
                    for (int i = 0; i < chunk.Blocks.Length; i++)
                    {
                        Block block = blocks[chunk.Blocks[i]];
                        if (block.BlockMaterial == EnumBlockMaterial.Stone || block.BlockMaterial == EnumBlockMaterial.Liquid || block.BlockMaterial == EnumBlockMaterial.Soil)
                        {
                            chunk.Blocks[i] = 0;
                        }
                    }

                    chunk.MarkModified();
                }

                api.WorldManager.FullRelight(new BlockPos(coord.X * chunksize, 0 * chunksize, coord.Y * chunksize), new BlockPos(coord.X * chunksize, api.WorldManager.MapSizeY, coord.Y * chunksize));
            }


            player.CurrentChunkSentRadius = 0;
        }
Esempio n. 18
0
        void ReadPos(IServerPlayer player, CmdArgs arguments)
        {
            if (arguments.Length < 2)
            {
                player.SendMessage(groupId, "/wgen pos [gprov|landform|climate|height]", EnumChatType.CommandError);
                return;
            }


            int          chunkSize   = api.WorldManager.ChunkSize;
            BlockPos     pos         = player.Entity.Pos.AsBlockPos;
            IServerChunk serverchunk = api.WorldManager.GetChunk(pos);

            if (serverchunk == null)
            {
                player.SendMessage(groupId, "Can check here, beyond chunk boundaries!", EnumChatType.CommandError);
                return;
            }

            IMapRegion mapRegion = serverchunk.MapChunk.MapRegion;


            int regionChunkSize = api.WorldManager.RegionSize / chunkSize;


            int lx      = pos.X % chunkSize;
            int lz      = pos.Z % chunkSize;
            int chunkX  = pos.X / chunkSize;
            int chunkZ  = pos.Z / chunkSize;
            int regionX = pos.X / regionSize;
            int regionZ = pos.Z / regionSize;

            switch (arguments[1])
            {
            case "height":
            {
                string str = string.Format("Rain y={0}, Worldgen terrain y={1}", serverchunk.MapChunk.RainHeightMap[lz * chunkSize + lx], serverchunk.MapChunk.WorldGenTerrainHeightMap[lz * chunkSize + lx]);
                player.SendMessage(groupId, str, EnumChatType.CommandSuccess);
            }
            break;

            case "gprov":
            {
                int noiseSizeGeoProv = mapRegion.GeologicProvinceMap.InnerSize;

                float posXInRegion = ((float)pos.X / regionSize - pos.X / regionSize) * noiseSizeGeoProv;
                float posZInRegion = ((float)pos.Z / regionSize - pos.Z / regionSize) * noiseSizeGeoProv;


                GeologicProvinceVariant[] provincesByIndex = NoiseGeoProvince.provinces.Variants;

                IntMap intmap = mapRegion.GeologicProvinceMap;

                LerpedWeightedIndex2DMap map = new LerpedWeightedIndex2DMap(intmap.Data, mapRegion.GeologicProvinceMap.Size, TerraGenConfig.geoProvSmoothingRadius);

                WeightedIndex[] indices = map[intmap.TopLeftPadding + posXInRegion, intmap.TopLeftPadding + posZInRegion];

                string text = "";
                foreach (WeightedIndex windex in indices)
                {
                    if (text.Length > 0)
                    {
                        text += ", ";
                    }

                    text += (100 * windex.weight).ToString("#.#") + "% " + provincesByIndex[windex.index].Code;
                }

                player.SendMessage(groupId, text, EnumChatType.CommandSuccess);

                break;
            }


            case "landform":
            {
                int noiseSizeLandform = mapRegion.LandformMap.InnerSize;

                float posXInRegion = ((float)pos.X / regionSize - pos.X / regionSize) * noiseSizeLandform;
                float posZInRegion = ((float)pos.Z / regionSize - pos.Z / regionSize) * noiseSizeLandform;


                LandformVariant[] landforms = NoiseLandforms.landforms.LandFormsByIndex;

                IntMap intmap = mapRegion.LandformMap;

                LerpedWeightedIndex2DMap map = new LerpedWeightedIndex2DMap(intmap.Data, mapRegion.LandformMap.Size, TerraGenConfig.landFormSmothingRadius);

                WeightedIndex[] indices = map[intmap.TopLeftPadding + posXInRegion, intmap.TopLeftPadding + posZInRegion];

                string text = "";
                foreach (WeightedIndex windex in indices)
                {
                    if (text.Length > 0)
                    {
                        text += ", ";
                    }

                    text += (100 * windex.weight).ToString("#.#") + "% " + landforms[windex.index].Code;
                }

                player.SendMessage(groupId, text, EnumChatType.CommandSuccess);

                break;
            }

            case "climate":
            {
                ClimateCondition climate = api.World.BlockAccessor.GetClimateAt(pos);

                string text = string.Format("Temperature: {0}°, Rainfall: {1}%, Fertility: {2}%, Forest: {3}%, Shrub: {4}%, Sealevel dist: {5}%", climate.Temperature.ToString("0.#"), (int)(climate.Rainfall * 100f), (int)(climate.Fertility * 100f), (int)(climate.ForestDensity * 100f), (int)(climate.ShrubDensity * 100f), (int)(100f * pos.Y / 255f));

                player.SendMessage(groupId, text, EnumChatType.CommandSuccess);


                break;
            }
            }
        }
        public UpdateSnowLayerChunk UpdateSnowLayer(SnowAccumSnapshot sumsnapshot, bool ignoreOldAccum, IServerMapChunk mc, Vec2i chunkPos)
        {
            UpdateSnowLayerChunk updateChunk = new UpdateSnowLayerChunk();
            var layers = ws.GeneralConfig.SnowLayerBlocks;

            int chunkX = chunkPos.X;
            int chunkZ = chunkPos.Y;

            int regionX = (chunkX * chunksize) / regionsize;
            int regionZ = (chunkZ * chunksize) / regionsize;

            int regionBasePosX = regionX * regionsize;
            int regionBasePosZ = regionZ * regionsize;

            BlockPos pos                 = new BlockPos();
            BlockPos placePos            = new BlockPos();
            float    aboveSeaLevelHeight = sapi.World.BlockAccessor.MapSizeY - sapi.World.SeaLevel;

            int[] posIndices = randomShuffles[sapi.World.Rand.Next(randomShuffles.Length)];

            int          prevChunkY = -99999;
            IServerChunk chunk      = null;

            for (int i = 0; i < posIndices.Length; i++)
            {
                int posIndex = posIndices[i];
                int posY     = mc.RainHeightMap[posIndex];
                int chunkY   = posY / chunksize;

                pos.Set(
                    chunkX * chunksize + posIndex % chunksize,
                    posY,
                    chunkZ * chunksize + posIndex / chunksize
                    );

                if (prevChunkY != chunkY || chunk == null)
                {
                    chunk      = sapi.WorldManager.GetChunk(chunkX, chunkY, chunkZ);
                    prevChunkY = chunkY;
                }
                if (chunk == null)
                {
                    return(null);
                }

                float relx = (pos.X - regionBasePosX) / (float)regionsize;
                float rely = GameMath.Clamp((pos.Y - sapi.World.SeaLevel) / aboveSeaLevelHeight, 0, 1);
                float relz = (pos.Z - regionBasePosZ) / (float)regionsize;


                // What needs to be done here?
                // 1. Get desired snow cover level

                // 2. Get current snow cover level
                //    - Get topmmost block. Is it snow?
                //      - Yes. Use it as reference pos and stuff
                //      - No. Must have no snow, increment pos.Y by 1

                // 3. Compare and place block accordingly
                // Idea: New method Block.UpdateSnowLayer() returns a new block instance if a block change is needed


                // What needs to be done here, take 2
                // We have 3 possible cases per-block
                // 1: We find upside solid block. That means it has no snow on top
                // 2: We find snow. That means below is a solid block.
                // 3: We find some other block: That means we should try to find its snow-covered variant

                // We have the following input data
                // 1. Snow accumulation changes since the last update (usually an in-game hour or 2)
                // 2. A precise snow level value from the position (if not set, load from snowlayer block type) (set to zero if the snowlayer is removed)
                // 3. The current block at position, which is either
                //    - A snow layer: Override with internal level + accum changes
                //    - A solid block: Plase snow on top based on internal level + accum changes
                //    - A snow variantable block: Call the method with the new level


                Block block = chunk.GetLocalBlockAtBlockPos(sapi.World, pos);

                float hereAccum = 0;

                Vec2i vec = new Vec2i(pos.X, pos.Z);
                if (!ignoreOldAccum && !mc.SnowAccum.TryGetValue(vec, out hereAccum))
                {
                    hereAccum = block.GetSnowLevel(pos);
                }

                float nowAccum = hereAccum + sumsnapshot.GetAvgSnowAccumByRegionCorner(relx, rely, relz);

                mc.SnowAccum[vec] = GameMath.Clamp(nowAccum, -1, ws.GeneralConfig.SnowLayerBlocks.Count + 0.5f);

                float hereShouldLevel = nowAccum - GameMath.MurmurHash3Mod(pos.X, 0, pos.Z, 100) / 300f;
                float shouldIndexf    = GameMath.Clamp((hereShouldLevel - 1.1f), -1, ws.GeneralConfig.SnowLayerBlocks.Count - 1);
                int   shouldIndex     = shouldIndexf < 0 ? -1 : (int)shouldIndexf;

                placePos.Set(pos.X, pos.Y + 1, pos.Z);
                chunkY = placePos.Y / chunksize;

                if (prevChunkY != chunkY || chunk == null)
                {
                    chunk      = sapi.WorldManager.GetChunk(chunkX, chunkY, chunkZ);
                    prevChunkY = chunkY;
                }
                if (chunk == null)
                {
                    return(null);
                }

                Block upBlock = chunk.GetLocalBlockAtBlockPos(sapi.World, placePos);



                // Case 1: We have a block that can become snow covered (or more snow covered)
                placePos.Set(pos);
                Block newblock = block.GetSnowCoveredVariant(placePos, hereShouldLevel);
                if (newblock != null)
                {
                    if (block.Id != newblock.Id && upBlock.Replaceable > 6000)
                    {
                        updateChunk.SetBlocks[placePos.Copy()] = new BlockIdAndSnowLevel(newblock, hereShouldLevel);
                    }
                }
                // Case 2: We have a solid block that can have snow on top
                else if ((block.SnowCoverage == null && block.SideSolid[BlockFacing.UP.Index]) || block.SnowCoverage == true)
                {
                    placePos.Set(pos.X, pos.Y + 1, pos.Z);

                    if (upBlock.Id != 0)
                    {
                        newblock = upBlock.GetSnowCoveredVariant(placePos, hereShouldLevel);
                        if (newblock != null && upBlock.Id != newblock.Id)
                        {
                            updateChunk.SetBlocks[placePos.Copy()] = new BlockIdAndSnowLevel(newblock, hereShouldLevel);
                        }

                        continue;
                    }

                    if (shouldIndex >= 0)
                    {
                        Block toPlaceBlock = layers.GetKeyAtIndex(shouldIndex);
                        updateChunk.SetBlocks[placePos.Copy()] = new BlockIdAndSnowLevel(toPlaceBlock, hereShouldLevel);
                    }
                }
            }


            return(updateChunk);
        }
Esempio n. 20
0
        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);
            }
        }