Exemple #1
0
        public override bool TryPlaceBlock(IWorldAccessor world, IPlayer byPlayer, ItemStack itemstack, BlockSelection blockSel)
        {
            if (!world.TestPlayerAccessBlock(byPlayer, blockSel.Position, EnumBlockAccessFlags.BuildOrBreak))
            {
                byPlayer.InventoryManager.ActiveHotbarSlot.MarkDirty();
                return(false);
            }

            BlockPos       abovePos = blockSel.Position.AddCopy(0, 1, 0);
            IBlockAccessor ba       = world.BlockAccessor;

            if (ba.GetBlockId(abovePos) == 0 && IsSuitablePosition(world, blockSel.Position))
            {
                BlockFacing[] horVer = SuggestedHVOrientation(byPlayer, blockSel);

                string knobOrientation = GetSuggestedKnobOrientation(ba, blockSel.Position, horVer[0]);

                AssetLocation downBlockCode = CodeWithParts(horVer[0].Code, "down", "closed", knobOrientation);
                Block         downBlock     = ba.GetBlock(downBlockCode);

                AssetLocation upBlockCode = CodeWithParts(horVer[0].Code, "up", "closed", knobOrientation);
                Block         upBlock     = ba.GetBlock(upBlockCode);

                ba.SetBlock(downBlock.BlockId, blockSel.Position);
                ba.SetBlock(upBlock.BlockId, abovePos);
                return(true);
            }

            return(false);
        }
Exemple #2
0
        bool CanTorchStay(IBlockAccessor blockAccessor, BlockPos pos)
        {
            BlockFacing facing = BlockFacing.FromCode(LastCodePart());
            Block       block  = blockAccessor.GetBlock(blockAccessor.GetBlockId(pos.AddCopy(facing.GetOpposite())));

            return(block.CanAttachBlockAt(blockAccessor, this, pos, facing));
        }
        private void tryPlaceDecoUp(BlockPos blockPos, IBlockAccessor blockAccessor, LCGRandom worldgenRand)
        {
            if (blockAccessor.GetBlockId(blockPos) != 0)
            {
                return;
            }

            int tries = 7;

            while (tries-- > 0)
            {
                blockPos.Y++;
                Block block = blockAccessor.GetBlock(blockPos);
                if (block.IsLiquid())
                {
                    return;
                }
                if (block.SideSolid[BlockFacing.DOWN.Index])
                {
                    blockPos.Y--;
                    Block placeblock = DecoBlocksCeiling[worldgenRand.NextInt(DecoBlocksCeiling.Length)];
                    blockAccessor.SetBlock(placeblock.BlockId, blockPos);
                    return;
                }
            }
        }
Exemple #4
0
        public override bool TryPlaceBlock(IWorldAccessor world, IPlayer byPlayer, ItemStack itemstack, BlockSelection blockSel, ref string failureCode)
        {
            BlockPos       abovePos = blockSel.Position.AddCopy(0, 1, 0);
            IBlockAccessor ba       = world.BlockAccessor;

            if (ba.GetBlockId(abovePos) == 0 && CanPlaceBlock(world, byPlayer, blockSel, ref failureCode))
            {
                BlockFacing[] horVer = SuggestedHVOrientation(byPlayer, blockSel);

                string knobOrientation = GetSuggestedKnobOrientation(ba, blockSel.Position, horVer[0]);

                AssetLocation downBlockCode = CodeWithVariants(new Dictionary <string, string>()
                {
                    { "horizontalorientation", horVer[0].Code },
                    { "part", "down" },
                    { "state", "closed" },
                    { "knobOrientation", knobOrientation }
                });

                Block downBlock = ba.GetBlock(downBlockCode);

                AssetLocation upBlockCode = downBlock.CodeWithVariant("part", "up");
                Block         upBlock     = ba.GetBlock(upBlockCode);

                ba.SetBlock(downBlock.BlockId, blockSel.Position);
                ba.SetBlock(upBlock.BlockId, abovePos);
                return(true);
            }

            return(false);
        }
        private void tryPlaceDecoDown(BlockPos blockPos, IBlockAccessor blockAccessor, Random worldGenRand)
        {
            if (blockAccessor.GetBlockId(blockPos) != 0)
            {
                return;
            }

            int tries = 7;

            while (tries-- > 0)
            {
                blockPos.Y--;
                Block block = blockAccessor.GetBlock(blockPos);
                if (block.IsLiquid())
                {
                    return;
                }
                if (block.SideSolid[BlockFacing.DOWN.Index])
                {
                    blockPos.Y++;
                    blockAccessor.SetBlock(DecoBlocksFloor[worldGenRand.Next(DecoBlocksFloor.Length)].BlockId, blockPos);
                    return;
                }
            }
        }
        public override bool TryPlaceBlockForWorldGen(IBlockAccessor blockAccessor, BlockPos pos, BlockFacing onBlockFace, LCGRandom worldGenRand)
        {
            if (blockAccessor.GetBlockId(pos) != 0)
            {
                return(false);
            }

            int surfaceY = blockAccessor.GetTerrainMapheightAt(pos);

            if (surfaceY - pos.Y < 30 || pos.Y < 25)
            {
                return(false);
            }

            BlockPos cavepos = getSemiLargeCavePos(blockAccessor, pos);

            if (cavepos == null)
            {
                return(false);
            }

            int dy = 0;

            while (dy < 15 && !blockAccessor.GetBlock(cavepos.X, cavepos.Y + dy, cavepos.Z).SideSolid[BlockFacing.UP.Index])
            {
                dy++;
            }
            if (dy >= 15)
            {
                return(false);
            }
            blockAccessor.SetBlock(this.BlockId, cavepos.AddCopy(0, dy, 0));
            if (EntityClass != null)
            {
                blockAccessor.SpawnBlockEntity(EntityClass, cavepos.AddCopy(0, dy, 0));
            }

            BlockPos tmppos = new BlockPos();
            int      tries  = 55 + worldGenRand.NextInt(55);

            while (tries-- > 0)
            {
                int offX = worldGenRand.NextInt(15) - 7;
                int offY = worldGenRand.NextInt(15) - 7;
                int offZ = worldGenRand.NextInt(15) - 7;

                if (worldGenRand.NextDouble() < 0.4)
                {
                    tryPlaceDecoUp(tmppos.Set(cavepos.X + offX, cavepos.Y + offY, cavepos.Z + offZ), blockAccessor, worldGenRand);
                }
                else
                {
                    tryPlaceDecoDown(tmppos.Set(cavepos.X + offX, cavepos.Y + offY, cavepos.Z + offZ), blockAccessor, worldGenRand);
                }
            }

            return(true);
        }
Exemple #7
0
        private void ExportArea(string filename, BlockPos start, BlockPos end)
        {
            int exported = 0;

            IBlockAccessor blockAcccessor = api.WorldManager.GetBlockAccessor(false, false, false);

            BlockPos startPos = new BlockPos(Math.Min(start.X, end.X), Math.Min(start.Y, end.Y), Math.Min(start.Z, end.Z));
            BlockPos finalPos = new BlockPos(Math.Max(start.X, end.X), Math.Max(start.Y, end.Y), Math.Max(start.Z, end.Z));

            BlockSchematic blockdata = new BlockSchematic();

            for (int x = startPos.X; x < finalPos.X; x++)
            {
                for (int y = startPos.Y; y < finalPos.Y; y++)
                {
                    for (int z = startPos.Z; z < finalPos.Z; z++)
                    {
                        BlockPos pos     = new BlockPos(x, y, z);
                        ushort   blockid = blockAcccessor.GetBlockId(pos);
                        if (blockid == 0)
                        {
                            continue;
                        }

                        blockdata.BlocksUnpacked[pos] = blockid;
                        exported++;
                    }
                }
            }

            blockdata.Pack(blockAcccessor, startPos);

            string outfilepath = Path.Combine(exportFolderPath, filename);

            if (!outfilepath.EndsWith(".json"))
            {
                outfilepath += ".json";
            }

            try
            {
                using (TextWriter textWriter = new StreamWriter(outfilepath))
                {
                    textWriter.Write(JsonConvert.SerializeObject(blockdata, Formatting.None));
                    textWriter.Close();
                }
            }
            catch (IOException e)
            {
                Good("Failed exporting: " + e.Message);
                return;
            }

            Good(exported + " blocks exported.");
        }
        private BlockPos getSemiLargeCavePos(IBlockAccessor blockAccessor, BlockPos pos)
        {
            BlockPos outpos = pos.Copy();

            int maxY = pos.Y;
            int minY = pos.Y;

            int minX = pos.X;
            int maxX = pos.X;

            int minZ = pos.Z;
            int maxZ = pos.Z;

            while (pos.Y - minY < 12 && blockAccessor.GetBlockId(pos.X, minY - 1, pos.Z) == 0)
            {
                minY--;
            }
            while (maxY - pos.Y < 12 && blockAccessor.GetBlockId(pos.X, maxY + 1, pos.Z) == 0)
            {
                maxY++;
            }

            outpos.Y = (maxY + minY) / 2;
            if (maxY - minY < 4 || maxY - minY >= 10)
            {
                return(null);
            }

            while (pos.X - minX < 12 && blockAccessor.GetBlockId(minX - 1, pos.Y, pos.Z) == 0)
            {
                minX--;
            }
            while (maxX - pos.X < 12 && blockAccessor.GetBlockId(maxX + 1, pos.Y, pos.Z) == 0)
            {
                maxX++;
            }

            if (maxX - minX < 3)
            {
                return(null);
            }
            outpos.X = (maxX + minX) / 2;

            while (pos.Z - minZ < 12 && blockAccessor.GetBlockId(pos.X, pos.Y, minZ - 1) == 0)
            {
                minZ--;
            }
            while (maxZ - pos.Z < 12 && blockAccessor.GetBlockId(pos.X, pos.Y, maxZ + 1) == 0)
            {
                maxZ++;
            }

            if (maxZ - minZ < 3)
            {
                return(null);
            }
            outpos.Z = (maxZ + minZ) / 2;

            return(outpos);
        }
Exemple #9
0
        bool TryAttachTo(IBlockAccessor blockAccessor, BlockPos blockpos, BlockFacing onBlockFace)
        {
            BlockPos attachingBlockPos = blockpos.AddCopy(onBlockFace.Opposite);
            Block    block             = blockAccessor.GetBlock(blockAccessor.GetBlockId(attachingBlockPos));

            if (block.CanAttachBlockAt(blockAccessor, this, attachingBlockPos, onBlockFace))
            {
                int blockId = blockAccessor.GetBlock(CodeWithParts(onBlockFace.Code)).BlockId;
                blockAccessor.SetBlock(blockId, blockpos);
                return(true);
            }

            return(false);
        }
Exemple #10
0
        public override void OnHeldInteractStart(ItemSlot slot, EntityAgent byEntity, BlockSelection blockSel, EntitySelection entitySel, bool firstEvent, ref EnumHandHandling handling)
        {
            IPlayer byPlayer = null;

            if (byEntity is EntityPlayer)
            {
                byPlayer = byEntity.World.PlayerByUid(((EntityPlayer)byEntity).PlayerUID);
            }

            IClientPlayer spry = byPlayer as IClientPlayer;

            IWorldAccessor world = byEntity.World;
            IBlockAccessor bacc  = world.BlockAccessor;

            List <BlockPos> plist = new List <BlockPos>();



            if (bacc.GetBlockId(blockSel.Position + blockSel.Face.Normali.AsBlockPos) == 0)
            {
                Debug.WriteLine("airblock good to place thing here");
                Dictionary <string, string> rplace = new Dictionary <string, string>();
                rplace.Add("rock", "clay");
                rplace.Add("size", "oogle");
                Debug.WriteLine(CodeWithVariants(rplace));
            }


            plist.Add(blockSel.Position + blockSel.Face.Normali.AsBlockPos);
            byEntity.World.HighlightBlocks(byPlayer, 0, plist);

            if (spry == null)
            {
                Debug.WriteLine("spry is null");
                return;
            }
            spry.ShowChatNotification(slot.Itemstack.Attributes.GetInt("stonestored").ToString());


            base.OnHeldInteractStart(slot, byEntity, blockSel, entitySel, firstEvent, ref handling);
        }
        void TestTree(IServerPlayer player, CmdArgs arguments)
        {
            if (arguments.Length < 2)
            {
                player.SendMessage(groupId, "/wgen tree {treeWorldPropertyCode} [0.1 - 3] [aheadoffset]", EnumChatType.CommandError);
                return;
            }

            float size        = 1f;
            int   aheadoffset = 0;

            if (arguments.Length > 2)
            {
                float.TryParse(arguments[2], out size);
            }

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

            BlockPos pos = player.Entity.Pos.HorizontalAheadCopy(aheadoffset).AsBlockPos;

            IBlockAccessor blockAccessor = api.WorldManager.GetBlockAccessorBulkUpdate(true, true);

            while (blockAccessor.GetBlockId(pos) == 0 && pos.Y > 1)
            {
                pos.Down();
            }

            treeGenerators.ReloadTreeGenerators();
            treeGenerators.RunGenerator(new AssetLocation(arguments[1]), blockAccessor, pos, size);

            blockAccessor.Commit();

            player.SendMessage(groupId, arguments[1] + " size " + size + " generated.", EnumChatType.CommandError);
        }
Exemple #12
0
        private void growBranch(Random rand, int depth, BlockPos pos, float dx, float dy, float dz, float angleVerStart, float angleHorStart, float curWidth, float dieAt, float trunkWidthLoss, bool wideTrunk)
        {
            if (depth > 30)
            {
                Console.WriteLine("TreeGen.growBranch() aborted, too many branches!"); return;
            }

            TreeGenBranch branch = branchesByDepth[Math.Min(depth, branchesByDepth.Count - 1)];

            short[] outline = forestFloor.GetOutline();

            float widthloss                  = depth == 0 ? trunkWidthLoss : branch.WidthLoss(rand);
            float widthlossCurve             = branch.widthlossCurve;
            float branchspacing              = branch.branchSpacing.nextFloat(1, rand);
            float branchstart                = branch.branchStart.nextFloat(1, rand);
            float branchQuantityStart        = branch.branchQuantity.nextFloat(1, rand);
            float branchWidthMulitplierStart = branch.branchWidthMultiplier.nextFloat(1, rand);

            float reldistance, lastreldistance = 0;
            float totaldistance = curWidth / widthloss;

            int   iteration             = 0;
            float sequencesPerIteration = 1f / (curWidth / widthloss);


            float ddrag, angleVer, angleHor;

            // we want to place around the trunk/branch => offset the coordinates when growing stuff from the base
            float trunkOffsetX, trunkOffsetZ;

            BlockPos currentPos = new BlockPos();

            float branchQuantity, branchWidth;
            float sinAngleVer, cosAnglerHor, sinAngleHor;

            float currentSequence;

            LCGRandom lcgrand = lcgrandTL.Value;

            while (curWidth > 0 && iteration++ < 5000)
            {
                curWidth -= widthloss;
                if (widthlossCurve + curWidth / 20 < 1f)
                {
                    widthloss *= (widthlossCurve + curWidth / 20);
                }

                currentSequence = sequencesPerIteration * (iteration - 1);

                if (curWidth < dieAt)
                {
                    break;
                }

                angleVer = branch.angleVertEvolve.nextFloat(angleVerStart, currentSequence);
                angleHor = branch.angleHoriEvolve.nextFloat(angleHorStart, currentSequence);

                sinAngleVer  = GameMath.FastSin(angleVer);
                cosAnglerHor = GameMath.FastCos(angleHor);
                sinAngleHor  = GameMath.FastSin(angleHor);

                trunkOffsetX = Math.Max(-0.5f, Math.Min(0.5f, 0.7f * sinAngleVer * cosAnglerHor));
                trunkOffsetZ = Math.Max(-0.5f, Math.Min(0.5f, 0.7f * sinAngleVer * sinAngleHor));

                ddrag = branch.gravityDrag * (float)Math.Sqrt(dx * dx + dz * dz);

                dx += sinAngleVer * cosAnglerHor / Math.Max(1, Math.Abs(ddrag));
                dy += Math.Min(1, Math.Max(-1, GameMath.FastCos(angleVer) - ddrag));
                dz += sinAngleVer * sinAngleHor / Math.Max(1, Math.Abs(ddrag));

                int blockId = branch.getBlockId(curWidth, config.treeBlocks, this);
                if (blockId == 0)
                {
                    return;
                }

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

                PlaceResumeState state = getPlaceResumeState(currentPos, blockId, wideTrunk);

                if (state == PlaceResumeState.CanPlace)
                {
                    api.SetBlock(blockId, currentPos);

                    // Update the canopy outline of the tree for this block position
                    int idz = (int)(dz + 16);
                    int idx = (int)(dx + 16);
                    if (idz > 1 && idz < 31 && idx > 1 && idx < 31)
                    {
                        int canopyIndex = idz * 33 + idx;
                        outline[canopyIndex - 68]++;
                        outline[canopyIndex - 67]++;  //bias canopy shading towards the North (- z direction) for sun effects
                        outline[canopyIndex - 66]++;
                        outline[canopyIndex - 65]++;
                        outline[canopyIndex - 64]++;
                        outline[canopyIndex - 35]++;
                        outline[canopyIndex - 34] += 2;
                        outline[canopyIndex - 33] += 2;
                        outline[canopyIndex - 32] += 2;
                        outline[canopyIndex - 31]++;
                        outline[canopyIndex - 2]++;
                        outline[canopyIndex - 1] += 2;
                        outline[canopyIndex + 0] += 3;
                        outline[canopyIndex + 1] += 2;
                        outline[canopyIndex + 2]++;
                        outline[canopyIndex + 33]++;
                    }

                    if (vineGrowthChance > 0 && rand.NextDouble() < vineGrowthChance && config.treeBlocks.vinesBlock != null)
                    {
                        BlockFacing facing = BlockFacing.HORIZONTALS[rand.Next(4)];

                        BlockPos vinePos = currentPos.AddCopy(facing);
                        float    cnt     = 1 + rand.Next(11) * (vineGrowthChance + 0.2f);

                        while (api.GetBlockId(vinePos) == 0 && cnt-- > 0)
                        {
                            Block block = config.treeBlocks.vinesBlock;

                            if (cnt <= 0 && config.treeBlocks.vinesEndBlock != null)
                            {
                                block = config.treeBlocks.vinesEndBlock;
                            }

                            block.TryPlaceBlockForWorldGen(api, vinePos, facing, lcgrand);
                            vinePos.Down();
                        }
                    }
                }
                else
                {
                    if (state == PlaceResumeState.Stop)
                    {
                        return;
                    }
                }

                reldistance = (float)Math.Sqrt(dx * dx + dy * dy + dz * dz) / totaldistance;


                if (reldistance < branchstart)
                {
                    continue;
                }

                if (reldistance > lastreldistance + branchspacing * (1f - reldistance))
                {
                    branchspacing   = branch.branchSpacing.nextFloat(1, rand);
                    lastreldistance = reldistance;

                    if (branch.branchQuantityEvolve != null)
                    {
                        branchQuantity = branch.branchQuantityEvolve.nextFloat(branchQuantityStart, currentSequence);
                    }
                    else
                    {
                        branchQuantity = branch.branchQuantity.nextFloat(1, rand);
                    }

                    float prevHorAngle = 0f;
                    float horAngle;
                    float minHorangleDist = Math.Min(GameMath.PI / 5, branch.branchHorizontalAngle.var / 5);


                    bool first = true;

                    while (branchQuantity-- > 0)
                    {
                        if (branchQuantity < 1 && rand.NextDouble() < branchQuantity)
                        {
                            break;
                        }

                        curWidth *= branch.branchWidthLossMul;

                        horAngle = angleHor + branch.branchHorizontalAngle.nextFloat(1, rand);

                        int tries = 10;
                        while (!first && Math.Abs(horAngle - prevHorAngle) < minHorangleDist && tries-- > 0)
                        {
                            float newAngle = angleHor + branch.branchHorizontalAngle.nextFloat(1, rand);
                            if (Math.Abs(horAngle - prevHorAngle) < Math.Abs(newAngle - prevHorAngle))
                            {
                                horAngle = newAngle;
                            }
                        }

                        if (branch.branchWidthMultiplierEvolve != null)
                        {
                            branchWidth = curWidth * branch.branchWidthMultiplierEvolve.nextFloat(branchWidthMulitplierStart, currentSequence);
                        }
                        else
                        {
                            branchWidth = branch.branchWidthMultiplier.nextFloat(curWidth, rand);
                        }

                        growBranch(
                            rand,
                            depth + 1,
                            pos, dx + trunkOffsetX, dy, dz + trunkOffsetZ,
                            branch.branchVerticalAngle.nextFloat(1, rand),
                            horAngle,
                            branchWidth,
                            Math.Max(0, branch.dieAt.nextFloat(1, rand)),
                            trunkWidthLoss, false
                            );

                        first        = false;
                        prevHorAngle = angleHor + horAngle;
                    }
                }
            }
        }
Exemple #13
0
        private void growBranch(int depth, BlockPos pos, float dx, float dy, float dz, float angleVerStart, float angleHorStart, float curWidth, float dieAt)
        {
            if (depth > 30)
            {
                Console.WriteLine("TreeGen.growBranch() aborted, too many branches!"); return;
            }

            TreeGenBranch branch = branchesByDepth[Math.Min(depth, branchesByDepth.Count - 1)];


            float branchspacing              = branch.branchSpacing.nextFloat();
            float branchstart                = branch.branchStart.nextFloat();
            float branchQuantityStart        = branch.branchQuantity.nextFloat();
            float branchWidthMulitplierStart = branch.branchWidthMultiplier.nextFloat();

            float reldistance = 0, lastreldistance = 0;
            float totaldistance = curWidth / branch.widthloss;

            int   iteration             = 0;
            float sequencesPerIteration = 1f / (curWidth / branch.widthloss);


            float ddrag = 0, angleVer = 0, angleHor = 0;

            // we want to place around the trunk/branch => offset the coordinates when growing stuff from the base
            float trunkOffsetX = 0, trunkOffsetZ = 0, trunkOffsetY = 0;

            BlockPos currentPos;

            float branchQuantity, branchWidth;
            float sinAngleVer, cosAnglerHor, sinAngleHor;

            float currentSequence;

            while (curWidth > 0 && iteration++ < 5000)
            {
                curWidth -= branch.widthloss;

                currentSequence = sequencesPerIteration * (iteration - 1);

                if (curWidth < dieAt)
                {
                    break;
                }

                angleVer = branch.angleVertEvolve.nextFloat(angleVerStart, currentSequence);
                angleHor = branch.angleHoriEvolve.nextFloat(angleHorStart, currentSequence);

                sinAngleVer  = GameMath.FastSin(angleVer);
                cosAnglerHor = GameMath.FastCos(angleHor);
                sinAngleHor  = GameMath.FastSin(angleHor);

                trunkOffsetX = Math.Max(-0.5f, Math.Min(0.5f, 0.7f * sinAngleVer * cosAnglerHor));
                trunkOffsetY = Math.Max(-0.5f, Math.Min(0.5f, 0.7f * cosAnglerHor)) + 0.5f;
                trunkOffsetZ = Math.Max(-0.5f, Math.Min(0.5f, 0.7f * sinAngleVer * sinAngleHor));

                ddrag = branch.gravityDrag * GameMath.FastSqrt(dx * dx + dz * dz);

                dx += sinAngleVer * cosAnglerHor / Math.Max(1, Math.Abs(ddrag));
                dy += Math.Min(1, Math.Max(-1, GameMath.FastCos(angleVer) - ddrag));
                dz += sinAngleVer * sinAngleHor / Math.Max(1, Math.Abs(ddrag));

                ushort blockId = getBlockId(curWidth);
                if (blockId == 0)
                {
                    return;
                }

                currentPos = pos.AddCopy(dx, dy, dz);

                if (canPlace(currentPos, blockId))
                {
                    api.SetBlock(blockId, currentPos);

                    if (vineGrowthChance > 0 && rand.NextDouble() < vineGrowthChance && config.treeBlocks.vinesBlock != null)
                    {
                        BlockFacing facing = BlockFacing.HORIZONTALS[rand.Next(4)];

                        BlockPos vinePos = currentPos.AddCopy(facing);
                        float    cnt     = 1 + rand.Next(11) * (vineGrowthChance + 0.2f);

                        while (api.GetBlockId(vinePos) == 0 && cnt-- > 0)
                        {
                            Block block = config.treeBlocks.vinesBlock;

                            if (cnt <= 0 && config.treeBlocks.vinesEndBlock != null)
                            {
                                block = config.treeBlocks.vinesEndBlock;
                            }

                            block.TryPlaceBlockForWorldGen(api, vinePos, facing);
                            vinePos.Down();
                        }
                    }
                }

                reldistance = GameMath.FastSqrt(dx * dx + dy * dy + dz * dz) / totaldistance;


                if (reldistance < branchstart)
                {
                    continue;
                }

                if (reldistance > lastreldistance + branchspacing * (1f - reldistance))
                {
                    branchspacing   = branch.branchSpacing.nextFloat();
                    lastreldistance = reldistance;

                    if (branch.branchQuantityEvolve != null)
                    {
                        branchQuantity = branch.branchQuantityEvolve.nextFloat(branchQuantityStart, currentSequence);
                    }
                    else
                    {
                        branchQuantity = branch.branchQuantity.nextFloat();
                    }

                    float prevHorAngle = 0f;
                    float horAngle;
                    float minHorangleDist = Math.Min(GameMath.PI / 10, branch.branchHorizontalAngle.var / 5);


                    bool first = true;

                    while (branchQuantity-- > 0)
                    {
                        if (branchQuantity < 1 && rand.NextDouble() < branchQuantity)
                        {
                            break;
                        }

                        curWidth *= branch.branchWidthLossMul;

                        horAngle = branch.branchHorizontalAngle.nextFloat();

                        int tries = 5;
                        while (!first && Math.Abs(horAngle - prevHorAngle) < minHorangleDist && tries-- > 0)
                        {
                            horAngle = branch.branchHorizontalAngle.nextFloat();
                        }

                        if (branch.branchWidthMultiplierEvolve != null)
                        {
                            branchWidth = curWidth * branch.branchWidthMultiplierEvolve.nextFloat(branchWidthMulitplierStart, currentSequence);
                        }
                        else
                        {
                            branchWidth = branch.branchWidthMultiplier.nextFloat(curWidth);
                        }

                        growBranch(
                            depth + 1,
                            pos, dx + trunkOffsetX, dy, dz + trunkOffsetZ,
                            branch.branchVerticalAngle.nextFloat(),
                            angleHor + branch.branchHorizontalAngle.nextFloat(),
                            branchWidth,
                            Math.Max(0, branch.dieAt.nextFloat())
                            );

                        first        = false;
                        prevHorAngle = horAngle;
                    }
                }
            }
        }
        private void ApplyErode(WorldEdit worldEdit, IBlockAccessor blockAccessor, BlockPos pos, Block blockToPlace, ItemStack withItemStack)
        {
            int   radInt          = (int)Math.Ceiling(BrushRadius);
            float radSq           = BrushRadius * BrushRadius;
            int   blurRad         = KernelRadius;
            int   quantitySamples = (2 * blurRad + 1) * (2 * blurRad + 1);

            BlockPos dpos;
            Block    prevBlock   = blockAccessRev.GetBlock(0);
            bool     useSelected = UseSelectedBlock;

            int mapSizeY = worldEdit.sapi.WorldManager.MapSizeY;

            for (int dx = -radInt; dx <= radInt; dx++)
            {
                for (int dz = -radInt; dz <= radInt; dz++)
                {
                    if (dx * dx + dz * dz > radSq)
                    {
                        continue;
                    }

                    double avgHeight = 0;

                    // Todo: More efficient implementation. This ones super lame.
                    for (int lx = -blurRad; lx <= blurRad; lx++)
                    {
                        for (int lz = -blurRad; lz <= blurRad; lz++)
                        {
                            dpos = pos.AddCopy(dx + lx, 0, dz + lz);
                            while (dpos.Y < mapSizeY && blockAccessor.GetBlockId(dpos) != 0)
                            {
                                dpos.Up();
                            }
                            while (dpos.Y > 0 && blockAccessor.GetBlockId(dpos.X, dpos.Y, dpos.Z) == 0)
                            {
                                dpos.Down();
                            }

                            avgHeight += dpos.Y * kernel[lx + blurRad, lz + blurRad];
                        }
                    }


                    dpos = pos.AddCopy(dx, 0, dz);
                    while (dpos.Y < mapSizeY && blockAccessor.GetBlockId(dpos) != 0)
                    {
                        dpos.Up();
                    }
                    while (dpos.Y > 0 && (prevBlock = blockAccessor.GetBlock(dpos.X, dpos.Y, dpos.Z)).BlockId == 0)
                    {
                        dpos.Down();
                    }

                    if (Math.Abs(dpos.Y - avgHeight) < 0.36)
                    {
                        continue;
                    }

                    if (dpos.Y > avgHeight)
                    {
                        blockAccessor.SetBlock(0, dpos);
                    }
                    else
                    {
                        if (useSelected)
                        {
                            blockAccessRev.SetBlock(blockToPlace.BlockId, dpos.Up(), withItemStack);
                        }
                        else
                        {
                            blockAccessRev.SetBlock(prevBlock.BlockId, dpos.Up());
                        }
                    }
                }
            }
        }