public override bool TryPlaceBlockForWorldGen(IBlockAccessor blockAccessor, BlockPos pos, BlockFacing onBlockFace, LCGRandom worldgenRandom)
        {
            var dBlock = blockAccessor.GetBlock(pos.X, pos.Y - 1, pos.Z);

            if (dBlock.Fertility <= 20)
            {
                return(false);
            }

            var climate = blockAccessor.GetClimateAt(pos, EnumGetClimateMode.WorldGenValues);
            int rnd     = worldgenRandom.NextInt(WorldGenConds.Length);

            int len = WorldGenConds.Length;

            for (int i = 0; i < len; i++)
            {
                var conds = WorldGenConds[(i + rnd) % len];
                if (conds.MinTemp <= climate.Temperature && conds.MaxTemp >= climate.Temperature && conds.MinRain <= climate.Rainfall && conds.MaxRain >= climate.Rainfall && worldgenRandom.NextFloat() <= conds.Chance)
                {
                    blockAccessor.SetBlock(BlockId, pos);
                    blockAccessor.SpawnBlockEntity(EntityClass, pos);
                    var be = blockAccessor.GetBlockEntity(pos) as BlockEntityFruitTreeBranch;

                    be.TreeType          = conds.Type;
                    be.InitAfterWorldGen = true;

                    return(true);
                }
            }

            return(false);
        }
        internal void CreateForestFloor(IBlockAccessor blockAccessor, TreeGenConfig config, BlockPos pos, LCGRandom rnd, int treesInChunkGenerated)
        {
            int grassLevelOffset = 0;
            // More grass coverage for jungles
            ClimateCondition climate = blockAccessor.GetClimateAt(pos, EnumGetClimateMode.WorldGenValues);

            if (climate.Temperature > 24 && climate.Rainfall > 160)
            {
                grassLevelOffset = 2;
            }

            short[] outline = outlineThreadSafe.Value;
            this.api = blockAccessor;

            float forestness = climate.ForestDensity * climate.ForestDensity * 4 * (climate.Fertility + 0.25f);


            // Only replace soil with forestFloor in certain climate conditions
            if (climate.Fertility <= 0.25 || forestness <= 0.4)
            {
                return;
            }

            // Otherwise adjust the strength of the effect according to forest density and fertility (fertility is higher for tropical forests)
            for (int i = 0; i < outline.Length; i++)
            {
                outline[i] = (short)(outline[i] * forestness + 0.3f);
            }

            // Blend the canopy outline outwards from the center in a way that ensures smoothness
            for (int pass = 0; pass < 7; pass++)
            {
                bool noChange = true;

                for (int x = 0; x < 16; x++)
                {
                    for (int z = 0; z < 16; z++)
                    {
                        if (x == 0 && z == 0)
                        {
                            continue;
                        }
                        int o = Math.Min((int)outline[(16 + z) * 33 + (16 + x)], 18 * 9);
                        if (o == 0)
                        {
                            continue;
                        }

                        int n1 = (17 + z) * 33 + (16 + x);
                        int n2 = (16 + z) * 33 + (17 + x);
                        if (outline[n1] < o - 18)
                        {
                            outline[n1] = (short)(o - 18);
                            noChange    = false;
                        }
                        if (outline[n2] < o - 18)
                        {
                            outline[n2] = (short)(o - 18);
                            noChange    = false;
                        }

                        o  = Math.Min((int)outline[(16 - z) * 33 + (16 + x)], 18 * 9);
                        n1 = (15 - z) * 33 + (16 + x);
                        n2 = (16 - z) * 33 + (17 + x);
                        if (outline[n1] < o - 18)
                        {
                            outline[n1] = (short)(o - 18);
                            noChange    = false;
                        }
                        if (outline[n2] < o - 18)
                        {
                            outline[n2] = (short)(o - 18);
                            noChange    = false;
                        }
                    }

                    for (int z = 0; z < 16; z++)
                    {
                        if (x == 0 && z == 0)
                        {
                            continue;
                        }
                        int o  = Math.Min((int)outline[(16 + z) * 33 + (16 - x)], 18 * 9);
                        int n1 = (17 + z) * 33 + (16 - x);
                        int n2 = (16 + z) * 33 + (15 - x);
                        if (outline[n1] < o - 18)
                        {
                            outline[n1] = (short)(o - 18);
                            noChange    = false;
                        }
                        if (outline[n2] < o - 18)
                        {
                            outline[n2] = (short)(o - 18);
                            noChange    = false;
                        }

                        o  = Math.Min((int)outline[(16 - z) * 33 + (16 - x)], 18 * 9);
                        n1 = (15 - z) * 33 + (16 - x);
                        n2 = (16 - z) * 33 + (15 - x);
                        if (outline[n1] < o - 18)
                        {
                            outline[n1] = (short)(o - 18);
                            noChange    = false;
                        }
                        if (outline[n2] < o - 18)
                        {
                            outline[n2] = (short)(o - 18);
                            noChange    = false;
                        }
                    }
                }
                if (noChange)
                {
                    break;
                }
            }


            BlockPos currentPos = new BlockPos();

            for (int canopyIndex = 0; canopyIndex < outline.Length; canopyIndex++)
            {
                int intensity = outline[canopyIndex];
                if (intensity == 0)
                {
                    continue;
                }

                int dz = canopyIndex / 33 - 16;
                int dx = canopyIndex % 33 - 16;
                currentPos.Set(pos.X + dx, pos.Y, pos.Z + dz);
                currentPos.Y = blockAccessor.GetTerrainMapheightAt(currentPos);

                if (currentPos.Y - pos.Y < 4)  //Don't place forest floor above approximate height of the canopy of this tree
                {
                    CheckAndReplaceForestFloor(currentPos, intensity, grassLevelOffset);
                }
            }

            GenPatches(blockAccessor, pos, forestness, config.Treetype, rnd);
        }
        private void GenPatches(IBlockAccessor blockAccessor, BlockPos pos, float forestNess, EnumTreeType treetype, LCGRandom rnd)
        {
            var bpc         = genPatchesSystem.bpc;
            int radius      = 5;
            int worldheight = blockAccessor.MapSizeY;

            int cnt = underTreePatches?.Count ?? 0;

            for (int i = 0; i < cnt; i++)
            {
                BlockPatch bPatch = underTreePatches[i];
                if (bPatch.TreeType != EnumTreeType.Any && bPatch.TreeType != treetype)
                {
                    continue;
                }

                float chance = 0.003f * forestNess * bPatch.Chance * bpc.ChanceMultiplier.nextFloat();

                //if (bPatch.blockCodes[0].Path.Contains("mushroom")) chance *= 20; - for debugging

                while (chance-- > rnd.NextDouble())
                {
                    int dx = rnd.NextInt(2 * radius) - radius;
                    int dz = rnd.NextInt(2 * radius) - radius;

                    tmpPos.Set(pos.X + dx, 0, pos.Z + dz);

                    int y = blockAccessor.GetTerrainMapheightAt(tmpPos);
                    if (y <= 0 || y >= worldheight - 8)
                    {
                        continue;
                    }

                    tmpPos.Y = y;

                    var climate = blockAccessor.GetClimateAt(tmpPos, EnumGetClimateMode.WorldGenValues);
                    if (climate == null)
                    {
                        continue;
                    }

                    if (bpc.IsPatchSuitableUnderTree(bPatch, worldheight, climate, y))
                    {
                        int regionX = pos.X / blockAccessor.RegionSize;
                        int regionZ = pos.Z / blockAccessor.RegionSize;
                        if (bPatch.MapCode != null && rnd.NextInt(255) > genPatchesSystem.GetPatchDensity(bPatch.MapCode, tmpPos.X, tmpPos.Z, blockAccessor.GetMapRegion(regionX, regionZ)))
                        {
                            continue;
                        }

                        int  firstBlockId = 0;
                        bool found        = true;

                        if (bPatch.BlocksByRockType != null)
                        {
                            found = false;
                            int dy = 1;
                            while (dy < 5 && y - dy > 0)
                            {
                                string lastCodePart = blockAccessor.GetBlock(tmpPos.X, y - dy, tmpPos.Z).LastCodePart();
                                if (genPatchesSystem.RockBlockIdsByType.TryGetValue(lastCodePart, out firstBlockId))
                                {
                                    found = true; break;
                                }
                                dy++;
                            }
                        }

                        if (found)
                        {
                            bPatch.Generate(blockAccessor, rnd, tmpPos.X, tmpPos.Y, tmpPos.Z, firstBlockId);
                        }
                    }
                }
            }

            cnt = onTreePatches?.Count ?? 0;
            for (int i = 0; i < cnt; i++)
            {
                BlockPatch blockPatch = onTreePatches[i];

                float chance = 3 * forestNess * blockPatch.Chance * bpc.ChanceMultiplier.nextFloat();

                while (chance-- > rnd.NextDouble())
                {
                    int dx = 1 - rnd.NextInt(2) * 2;
                    int dy = rnd.NextInt(5);
                    int dz = 1 - rnd.NextInt(2) * 2;

                    tmpPos.Set(pos.X + dx, pos.Y + dy, pos.Z + dz);

                    var block = api.GetBlock(tmpPos);
                    if (block.Id != 0)
                    {
                        continue;
                    }
                    BlockFacing facing = null;

                    for (int j = 0; j < 4; j++)
                    {
                        var f      = BlockFacing.HORIZONTALS[j];
                        var nblock = api.GetBlock(tmpPos.X + f.Normali.X, tmpPos.Y, tmpPos.Z + f.Normali.Z);
                        if (nblock is BlockLog && nblock.Variant["type"] != "resin")
                        {
                            facing = f;
                            break;
                        }
                    }
                    if (facing == null)
                    {
                        break;
                    }

                    var climate = blockAccessor.GetClimateAt(tmpPos, EnumGetClimateMode.WorldGenValues);
                    if (climate == null)
                    {
                        continue;
                    }

                    if (bpc.IsPatchSuitableUnderTree(blockPatch, worldheight, climate, tmpPos.Y))
                    {
                        int regionX = pos.X / blockAccessor.RegionSize;
                        int regionZ = pos.Z / blockAccessor.RegionSize;
                        if (blockPatch.MapCode != null && rnd.NextInt(255) > genPatchesSystem.GetPatchDensity(blockPatch.MapCode, tmpPos.X, tmpPos.Z, blockAccessor.GetMapRegion(regionX, regionZ)))
                        {
                            continue;
                        }

                        int index = rnd.NextInt(blockPatch.Blocks.Length);
                        blockPatch.Blocks[index].TryPlaceBlockForWorldGen(blockAccessor, tmpPos, facing, rnd);
                    }
                }
            }
        }