private void GenHere(IBlockAccessor blockAccessor, BlockPos pos, LCGRandom worldGenRand) { int rnd = worldGenRand.NextInt(bases.Length); Block placeblock = api.World.GetBlock(CodeWithVariant("type", bases[rnd])); blockAccessor.SetBlock(placeblock.Id, pos); if (segments[rnd] != null) { placeblock = api.World.GetBlock(CodeWithVariant("type", segments[rnd])); int len = worldGenRand.NextInt(3); while (len-- > 0) { pos.Down(); if (blockAccessor.GetBlock(pos).Replaceable > 6000) { blockAccessor.SetBlock(placeblock.Id, pos); } } pos.Down(); placeblock = api.World.GetBlock(CodeWithVariant("type", ends[rnd])); if (blockAccessor.GetBlock(pos).Replaceable > 6000) { blockAccessor.SetBlock(placeblock.Id, pos); } } }
public override bool TryPlaceBlockForWorldGen(IBlockAccessor blockAccessor, BlockPos pos, BlockFacing onBlockFace, LCGRandom worldGenRand) { Block block = blockAccessor.GetBlock(pos); if (block.IsReplacableBy(this) && block.SideSolid[BlockFacing.UP.Index]) { blockAccessor.SetBlock(rndGearBlock().BlockId, pos); return(true); } pos = pos.DownCopy(); block = blockAccessor.GetBlock(pos); if (block.IsReplacableBy(this) && block.SideSolid[BlockFacing.UP.Index]) { blockAccessor.SetBlock(rndGearBlock().BlockId, pos); return(true); } pos.Down(); block = blockAccessor.GetBlock(pos); if (block.IsReplacableBy(this) && block.SideSolid[BlockFacing.UP.Index]) { blockAccessor.SetBlock(rndGearBlock().BlockId, pos); return(true); } return(false); }
private void Erode(float quantity, BlockPos dpos) { while (quantity-- >= 1) { //if (quantity < 1 && rand.NextDouble() > quantity) break; blockAccessRev.SetBlock(0, dpos); dpos.Down(); } }
protected bool TryStackDown(IPlayer byPlayer, IWorldAccessor world, BlockPos pos, BlockFacing face, ItemStack itemstack) { Block ladderBlock = world.BlockAccessor.GetBlock(pos); string ladderType = ladderBlock.GetBehavior <BlockBehaviorLadder>()?.LadderType; if (ladderType != LadderType) { return(false); } BlockPos belowPos = pos.DownCopy(); Block belowBlock = null; while (belowPos.Y > 0) { belowBlock = world.BlockAccessor.GetBlock(belowPos); if (belowBlock.FirstCodePart() != ownFirstCodePart) { break; } belowPos.Down(); } string useless = ""; if (belowBlock == null || belowBlock.FirstCodePart() == ownFirstCodePart) { return(false); } if (!belowBlock.IsReplacableBy(block)) { return(false); } if (!ladderBlock.CanPlaceBlock(world, byPlayer, new BlockSelection() { Position = belowPos, Face = face }, ref useless)) { return(false); } ladderBlock.DoPlaceBlock(world, byPlayer, new BlockSelection() { Position = belowPos, Face = face }, itemstack); BlockPos neibPos = new BlockPos(); foreach (BlockFacing facing in BlockFacing.ALLFACES) { neibPos.Set(belowPos).Offset(facing); world.BlockAccessor.GetBlock(neibPos).OnNeighbourBlockChange(world, neibPos, belowPos); } return(true); }
protected bool TryCollectLowest(IPlayer byPlayer, IWorldAccessor world, BlockPos pos) { Block ladderBlock = world.BlockAccessor.GetBlock(pos); if (ladderBlock.FirstCodePart() != ownFirstCodePart) { return(false); } BlockPos belowPos = pos.DownCopy(); Block belowBlock; while (belowPos.Y > 0) { belowBlock = world.BlockAccessor.GetBlock(belowPos); if (belowBlock.FirstCodePart() != ownFirstCodePart) { break; } belowPos.Down(); } belowPos.Up(); Block collectBlock = world.BlockAccessor.GetBlock(belowPos); var bh = collectBlock.GetBehavior <BlockBehaviorLadder>(); if (bh == null || !bh.isFlexible) { return(false); } if (!world.Claims.TryAccess(byPlayer, belowPos, EnumBlockAccessFlags.BuildOrBreak)) { return(false); } ItemStack[] stacks = collectBlock.GetDrops(world, pos, byPlayer); world.BlockAccessor.SetBlock(0, belowPos); world.PlaySoundAt(collectBlock.Sounds.Break, pos.X + 0.5, pos.Y + 0.5, pos.Z + 0.5, byPlayer); if (stacks.Length > 0) { if (!byPlayer.InventoryManager.TryGiveItemstack(stacks[0], true)) { world.SpawnItemEntity(stacks[0], byPlayer.Entity.Pos.XYZ); } } return(true); }
private bool TryStackDown(IPlayer byPlayer, IWorldAccessor world, BlockPos pos, BlockFacing face, ItemStack itemstack) { Block ladderBlock = world.BlockAccessor.GetBlock(pos); if (ladderBlock.FirstCodePart() != ownFirstCodePart) { return(false); } BlockPos belowPos = pos.DownCopy(); Block belowBlock = null; while (belowPos.Y > 0) { belowBlock = world.BlockAccessor.GetBlock(belowPos); if (belowBlock.FirstCodePart() != ownFirstCodePart) { break; } belowPos.Down(); } string useless = ""; if (belowBlock == null || belowBlock.FirstCodePart() == ownFirstCodePart) { return(false); } if (!belowBlock.IsReplacableBy(block)) { return(false); } if (!ladderBlock.CanPlaceBlock(world, byPlayer, new BlockSelection() { Position = belowPos, Face = face }, ref useless)) { return(false); } ladderBlock.DoPlaceBlock(world, byPlayer, new BlockSelection() { Position = belowPos, Face = face }, itemstack); return(true); }
private string GetBlocksAround(BlockLogSection[] around, IBlockAccessor blockAccessor, BlockPos pos) { string[] any = new string[3]; int[] anyCount = new int[3]; if (blockAccessor.GetBlock(pos.North()) is BlockLogSection n) { around[0] = n; UpdateAny(n.LastCodePart(), any, anyCount); } if (blockAccessor.GetBlock(pos.South().East()) is BlockLogSection e) { around[1] = e; UpdateAny(e.LastCodePart(), any, anyCount); } if (blockAccessor.GetBlock(pos.South().West()) is BlockLogSection s) { around[2] = s; UpdateAny(s.LastCodePart(), any, anyCount); } if (blockAccessor.GetBlock(pos.North().West()) is BlockLogSection w) { around[3] = w; UpdateAny(w.LastCodePart(), any, anyCount); } if (blockAccessor.GetBlock(pos.East().Up()) is BlockLogSection u) { around[4] = u; UpdateAny(u.LastCodePart(), any, anyCount); } if (blockAccessor.GetBlock(pos.Down().Down()) is BlockLogSection d) { around[5] = d; UpdateAny(d.LastCodePart(), any, anyCount); } if (anyCount[1] > anyCount[0]) { any[0] = any[1]; anyCount[0] = anyCount[1]; } return((anyCount[2] > anyCount[0]) ? any[2] : any[0]); }
public override bool TryPlaceBlockForWorldGen(IBlockAccessor blockAccessor, BlockPos pos, BlockFacing onBlockFace, LCGRandom worldGenRand) { BlockPos rootPos = onBlockFace.IsHorizontal ? pos.AddCopy(onBlockFace) : pos.AddCopy(onBlockFace.Opposite); var block = blockAccessor.GetBlock(rootPos); if (!block.HasBehavior <BehaviorMyceliumHost>()) { rootPos.Down(); block = blockAccessor.GetBlock(rootPos); if (!block.HasBehavior <BehaviorMyceliumHost>()) { return(false); } } blockAccessor.SpawnBlockEntity("Mycelium", rootPos); (blockAccessor.GetBlockEntity(rootPos) as BlockEntityMycelium).OnGenerated(blockAccessor, worldGenRand, this); return(true); }
public static BlockPos GetRecommendedPos(this BlockSelection sel, ICoreAPI api, Block block) { BlockPos adjPos = sel.Position.Copy(); if (!api.World.BlockAccessor.GetBlock(adjPos).IsReplacableBy(block)) { switch (sel.Face.Code) { case "up": adjPos.Up(); break; case "down": adjPos.Down(); break; case "north": adjPos.West(); break; case "south": adjPos.East(); break; case "east": adjPos.North(); break; case "west": adjPos.South(); break; default: break; } } return(adjPos); }
public override bool TryPlaceBlockForWorldGen(IBlockAccessor blockAccessor, BlockPos pos, BlockFacing onBlockFace, LCGRandom worldGenRand) { BlockPos belowPos = pos.DownCopy(); Block block = blockAccessor.GetBlock(belowPos); if (block.LiquidCode != "water") { return(false); } int depth = 1; while (depth < 10) { belowPos.Down(); block = blockAccessor.GetBlock(belowPos); if (block.Fertility > 0) { belowPos.Up(); PlaceSeaweed(blockAccessor, belowPos, depth); return(true); } else { if (!block.IsLiquid()) { return(false); } } depth++; } return(false); }
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); }
internal bool TryGenerateUnderground(IBlockAccessor blockAccessor, IWorldAccessor worldForCollectibleResolve, BlockPos pos) { int num = rand.NextInt(schematicDatas.Length); BlockSchematicStructure[] schematicStruc = schematicDatas[num]; BlockPos targetPos = pos.Copy(); BlockSchematicStructure schematic; if (schematicStruc[0].PathwayStarts.Length > 0) { // 1. Give up if non air block or mapheight is not at least 4 blocks higher // 2. Search up to 4 blocks downwards. Give up if no stone is found. // 3. Select one pathway randomly // 4. For every horizontal orientation // - Get the correctly rotated version for this pathway // - Starting at 2 blocks away, move one block closer each iteration // - Check if // - at every pathway block pos there is stone or air // - at least one pathway block has an air block facing towards center? // - If yes, remove the blocks that are in the way and place schematic Block block = blockAccessor.GetBlock(targetPos); if (block.Id != 0) { return(false); } // 1./2. Search an underground position that has air and a stone floor below bool found = false; for (int dy = 0; dy <= 4; dy++) { targetPos.Down(); block = blockAccessor.GetBlock(targetPos); if (block.BlockMaterial == EnumBlockMaterial.Stone) { targetPos.Up(); found = true; break; } } if (!found) { return(false); } // 3. Random pathway found = false; int pathwayNum = rand.NextInt(schematicStruc[0].PathwayStarts.Length); int targetOrientation = 0; int targetDistance = -1; BlockFacing targetFacing = null; BlockPos[] pathway = null; // 4. At that position search for a suitable stone wall in any direction for (targetOrientation = 0; targetOrientation < 4; targetOrientation++) { // Try every rotation pathway = schematicStruc[targetOrientation].PathwayOffsets[pathwayNum]; // This is the facing we are currently checking targetFacing = schematicStruc[targetOrientation].PathwaySides[pathwayNum]; targetDistance = CanPlacePathwayAt(blockAccessor, pathway, targetFacing, targetPos); if (targetDistance != -1) { break; } } if (targetDistance == -1) { return(false); } BlockPos pathwayStart = schematicStruc[targetOrientation].PathwayStarts[pathwayNum]; // Move back the structure so that the door aligns to the cave wall targetPos.Add( -pathwayStart.X - targetFacing.Normali.X * targetDistance, -pathwayStart.Y - targetFacing.Normali.Y * targetDistance, -pathwayStart.Z - targetFacing.Normali.Z * targetDistance ); if (!TestUndergroundCheckPositions(blockAccessor, targetPos, schematicStruc[targetOrientation].UndergroundCheckPositions)) { return(false); } if (isStructureAt(targetPos, worldForCollectibleResolve)) { return(false); } schematic = schematicStruc[targetOrientation]; LastPlacedSchematicLocation.Set(targetPos.X, targetPos.Y, targetPos.Z, targetPos.X + schematic.SizeX, targetPos.Y + schematic.SizeY, targetPos.Z + schematic.SizeZ); schematic.Place(blockAccessor, worldForCollectibleResolve, targetPos); // Free up a layer of blocks in front of the door ushort blockId = 0; // blockAccessor.GetBlock(new AssetLocation("creativeblock-37")).BlockId; for (int i = 0; i < pathway.Length; i++) { for (int d = 0; d <= targetDistance; d++) { tmpPos.Set( targetPos.X + pathwayStart.X + pathway[i].X + (d + 1) * targetFacing.Normali.X, targetPos.Y + pathwayStart.Y + pathway[i].Y + (d + 1) * targetFacing.Normali.Y, targetPos.Z + pathwayStart.Z + pathway[i].Z + (d + 1) * targetFacing.Normali.Z ); blockAccessor.SetBlock(blockId, tmpPos); } } return(true); } schematic = schematicStruc[rand.NextInt(4)]; BlockPos placePos = schematic.AdjustStartPos(targetPos.Copy(), Origin); LastPlacedSchematicLocation.Set(placePos.X, placePos.Y, placePos.Z, placePos.X + schematic.SizeX, placePos.Y + schematic.SizeY, placePos.Z + schematic.SizeZ); LastPlacedSchematic = schematic; if (insideblockids.Count > 0 && !insideblockids.Contains(blockAccessor.GetBlock(targetPos).Id)) { return(false); } if (!TestUndergroundCheckPositions(blockAccessor, placePos, schematic.UndergroundCheckPositions)) { return(false); } if (!satisfiesMinDistance(pos, worldForCollectibleResolve)) { return(false); } if (isStructureAt(pos, worldForCollectibleResolve)) { return(false); } if (resolvedReplaceWithRocktype != null) { //Console.WriteLine(schematic.FromFileName + " place at " + targetPos +", offseted to " + placePos); schematic.PlaceReplacingBlocks(blockAccessor, worldForCollectibleResolve, placePos, schematic.ReplaceMode, resolvedReplaceWithRocktype); } else { schematic.Place(blockAccessor, worldForCollectibleResolve, targetPos); } return(false); }
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; } } } }
void OnUse(WorldEdit worldEdit, BlockPos pos, int oldBlockId, int sign, ItemStack withItemStack) { if (Radius <= 0) { return; } int radInt = (int)Math.Ceiling(Radius); float radSq = Radius * Radius; Block block = blockAccessRev.GetBlock(pos); if (sign > 0) { worldEdit.sapi.World.BlockAccessor.SetBlock(oldBlockId, pos); } float maxhgt = Depth; EnumHeightToolMode dist = Mode; int quantityBlocks = (int)(GameMath.PI * radSq) * (int)maxhgt; if (!worldEdit.MayPlace(block, quantityBlocks)) { return; } for (int dx = -radInt; dx <= radInt; dx++) { for (int dz = -radInt; dz <= radInt; dz++) { float distanceSq = dx * dx + dz * dz; if (distanceSq > radSq) { continue; } BlockPos dpos = pos.AddCopy(dx, 0, dz); float height = sign * maxhgt; switch (dist) { case EnumHeightToolMode.Pyramid: height *= 1 - distanceSq / radSq; break; case EnumHeightToolMode.Gaussian: float sigmaSq = 0.1f; float sigma = GameMath.Sqrt(sigmaSq); float a = 1f / (sigma * GameMath.Sqrt(GameMath.TWOPI)); float x = distanceSq / radSq; double gaussValue = a * Math.Exp(-(x * x) / (2 * sigmaSq)); height *= (float)gaussValue; break; case EnumHeightToolMode.Perlin: height *= (float)noiseGen.Noise(dpos.X, dpos.Y, dpos.Z); break; } while (dpos.Y > 0 && blockAccessRev.GetBlock(dpos).Replaceable >= 6000) { dpos.Down(); } if (height < 0) { Erode(-height, dpos); } else { dpos.Up(); Grow(worldEdit.sapi.World, height, dpos, block, BlockFacing.UP, withItemStack); } } } blockAccessRev.SetHistoryStateBlock(pos.X, pos.Y, pos.Z, oldBlockId, blockAccessRev.GetBlockId(pos)); blockAccessRev.Commit(); }
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; } } } }