public BlockSchematicStructure Clone() { BlockSchematicStructure cloned = new BlockSchematicStructure(); cloned.SizeX = SizeX; cloned.SizeY = SizeY; cloned.SizeZ = SizeZ; cloned.BlockCodes = new Dictionary <int, AssetLocation>(BlockCodes); cloned.ItemCodes = new Dictionary <int, AssetLocation>(ItemCodes); cloned.Indices = new List <uint>(Indices); cloned.BlockIds = new List <int>(BlockIds); cloned.BlockEntities = new Dictionary <uint, string>(BlockEntities); cloned.ReplaceMode = ReplaceMode; cloned.FromFileName = FromFileName; return(cloned); }
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); }
internal bool TryGenerateAtSurface(IBlockAccessor blockAccessor, IWorldAccessor worldForCollectibleResolve, BlockPos pos) { int num = rand.NextInt(schematicDatas.Length); int orient = rand.NextInt(4); BlockSchematicStructure schematic = schematicDatas[num][orient]; int wdthalf = (int)Math.Ceiling(schematic.SizeX / 2f); int lenhalf = (int)Math.Ceiling(schematic.SizeZ / 2f); int wdt = schematic.SizeX; int len = schematic.SizeZ; tmpPos.Set(pos.X + wdthalf, 0, pos.Z + lenhalf); int centerY = blockAccessor.GetTerrainMapheightAt(tmpPos); // Probe all 4 corners + center if they are on the same height tmpPos.Set(pos.X, 0, pos.Z); int topLeftY = blockAccessor.GetTerrainMapheightAt(tmpPos); tmpPos.Set(pos.X + wdt, 0, pos.Z); int topRightY = blockAccessor.GetTerrainMapheightAt(tmpPos); tmpPos.Set(pos.X, 0, pos.Z + len); int botLeftY = blockAccessor.GetTerrainMapheightAt(tmpPos); tmpPos.Set(pos.X + wdt, 0, pos.Z + len); int botRightY = blockAccessor.GetTerrainMapheightAt(tmpPos); int diff = GameMath.Max(centerY, topLeftY, topRightY, botLeftY, botRightY) - GameMath.Min(centerY, topLeftY, topRightY, botLeftY, botRightY); if (diff != 0) { return(false); } pos.Y += centerY - pos.Y + 1 + OffsetY; // Ensure not floating on water tmpPos.Set(pos.X + wdthalf, pos.Y - 1, pos.Z + lenhalf); if (blockAccessor.GetBlock(tmpPos).IsLiquid()) { return(false); } tmpPos.Set(pos.X, pos.Y - 1, pos.Z); if (blockAccessor.GetBlock(tmpPos).IsLiquid()) { return(false); } tmpPos.Set(pos.X + wdt, pos.Y - 1, pos.Z); if (blockAccessor.GetBlock(tmpPos).IsLiquid()) { return(false); } tmpPos.Set(pos.X, pos.Y - 1, pos.Z + len); if (blockAccessor.GetBlock(tmpPos).IsLiquid()) { return(false); } tmpPos.Set(pos.X + wdt, pos.Y - 1, pos.Z + len); if (blockAccessor.GetBlock(tmpPos).IsLiquid()) { return(false); } // Ensure not submerged in water tmpPos.Set(pos.X, pos.Y, pos.Z); if (blockAccessor.GetBlock(tmpPos).IsLiquid()) { return(false); } tmpPos.Set(pos.X + wdt, pos.Y - 1, pos.Z + len); if (blockAccessor.GetBlock(tmpPos).IsLiquid()) { return(false); } tmpPos.Set(pos.X + wdt, pos.Y, pos.Z); if (blockAccessor.GetBlock(tmpPos).IsLiquid()) { return(false); } tmpPos.Set(pos.X, pos.Y, pos.Z + len); if (blockAccessor.GetBlock(tmpPos).IsLiquid()) { return(false); } tmpPos.Set(pos.X + wdt, pos.Y, pos.Z + len); if (blockAccessor.GetBlock(tmpPos).IsLiquid()) { return(false); } tmpPos.Set(pos.X, pos.Y + 1, pos.Z); if (blockAccessor.GetBlock(tmpPos).IsLiquid()) { return(false); } tmpPos.Set(pos.X + wdt, pos.Y + 1, pos.Z); if (blockAccessor.GetBlock(tmpPos).IsLiquid()) { return(false); } tmpPos.Set(pos.X, pos.Y + 1, pos.Z + len); if (blockAccessor.GetBlock(tmpPos).IsLiquid()) { return(false); } tmpPos.Set(pos.X + wdt, pos.Y + 1, pos.Z + len); if (blockAccessor.GetBlock(tmpPos).IsLiquid()) { return(false); } if (!satisfiesMinDistance(pos, worldForCollectibleResolve)) { return(false); } if (isStructureAt(pos, worldForCollectibleResolve)) { return(false); } LastPlacedSchematicLocation.Set(pos.X, pos.Y, pos.Z, pos.X + schematic.SizeX, pos.Y + schematic.SizeY, pos.Z + schematic.SizeZ); LastPlacedSchematic = schematic; schematic.PlaceRespectingBlockLayers(blockAccessor, worldForCollectibleResolve, pos, climateUpLeft, climateUpRight, climateBotLeft, climateBotRight, replaceblockids); return(true); }
public void Init(ICoreServerAPI api, BlockLayerConfig config, RockStrataConfig rockstrata, LCGRandom rand) { this.rand = rand; List <BlockSchematicStructure[]> schematics = new List <BlockSchematicStructure[]>(); for (int i = 0; i < Schematics.Length; i++) { string error = ""; IAsset[] assets; if (Schematics[i].EndsWith("*")) { assets = api.Assets.GetMany("worldgen/schematics/" + Schematics[i].Substring(0, Schematics[i].Length - 1)).ToArray(); } else { assets = new IAsset[] { api.Assets.Get("worldgen/schematics/" + Schematics[i] + ".json") }; } for (int j = 0; j < assets.Length; j++) { IAsset asset = assets[j]; BlockSchematicStructure schematic = asset.ToObject <BlockSchematicStructure>(); if (schematic == null) { api.World.Logger.Warning("Could not load {0}: {1}", Schematics[i], error); continue; } schematic.FromFileName = asset.Name; BlockSchematicStructure[] rotations = new BlockSchematicStructure[4]; rotations[0] = schematic; for (int k = 0; k < 4; k++) { if (k > 0) { rotations[k] = rotations[0].Clone(); rotations[k].TransformWhilePacked(api.World, EnumOrigin.BottomCenter, k * 90); } rotations[k].blockLayerConfig = config; rotations[k].Init(api.World.BlockAccessor); rotations[k].LoadMetaInformationAndValidate(api.World.BlockAccessor, api.World, schematic.FromFileName); } schematics.Add(rotations); } } this.schematicDatas = schematics.ToArray(); if (ReplaceWithBlocklayers != null) { replaceblockids = new int[ReplaceWithBlocklayers.Length]; for (int i = 0; i < replaceblockids.Length; i++) { Block block = api.World.GetBlock(ReplaceWithBlocklayers[i]); if (block == null) { throw new Exception(string.Format("Schematic with code {0} has replace block layer {1} defined, but no such block found!", Code, ReplaceWithBlocklayers[i])); } else { replaceblockids[i] = block.Id; } } } if (InsideBlockCodes != null) { for (int i = 0; i < InsideBlockCodes.Length; i++) { Block block = api.World.GetBlock(InsideBlockCodes[i]); if (block == null) { throw new Exception(string.Format("Schematic with code {0} has inside block {1} defined, but no such block found!", Code, InsideBlockCodes[i])); } else { insideblockids.Add(block.Id); } } } if (ReplaceWithRockType != null) { resolvedReplaceWithRocktype = new Dictionary <int, Dictionary <int, int> >(); foreach (var val in ReplaceWithRockType) { int sourceBlockId = api.World.GetBlock(val.Key).Id; Dictionary <int, int> blockIdByRockId = new Dictionary <int, int>(); foreach (var strat in rockstrata.Variants) { Block rockBlock = api.World.GetBlock(strat.BlockCode); AssetLocation resolvedLoc = val.Value.Clone(); resolvedLoc.Path = resolvedLoc.Path.Replace("{rock}", rockBlock.LastCodePart()); Block resolvedBlock = api.World.GetBlock(resolvedLoc); if (resolvedBlock != null) { blockIdByRockId[rockBlock.Id] = resolvedBlock.Id; Block quartzBlock = api.World.GetBlock(new AssetLocation("ore-quartz-" + rockBlock.LastCodePart())); if (quartzBlock != null) { blockIdByRockId[quartzBlock.Id] = resolvedBlock.Id; } } } resolvedReplaceWithRocktype[sourceBlockId] = blockIdByRockId; } } }
internal bool TryGenerateRuinAtSurface(IBlockAccessor blockAccessor, IWorldAccessor worldForCollectibleResolve, BlockPos pos) { int chunksize = blockAccessor.ChunkSize; int climate = GameMath.BiLerpRgbColor((float)(pos.X % chunksize) / chunksize, (float)(pos.Z % chunksize) / chunksize, climateUpLeft, climateUpRight, climateBotLeft, climateBotRight); int num = rand.NextInt(schematicDatas.Length); int orient = rand.NextInt(4); BlockSchematicStructure schematic = schematicDatas[num][orient]; int widthHalf = (int)Math.Ceiling(schematic.SizeX / 2f); int lengthHalf = (int)Math.Ceiling(schematic.SizeZ / 2f); // Probe all 4 corners + center if they either touch the surface or are sightly below ground int centerY = blockAccessor.GetTerrainMapheightAt(pos); tmpPos.Set(pos.X - widthHalf, 0, pos.Z - lengthHalf); int topLeftY = blockAccessor.GetTerrainMapheightAt(tmpPos); tmpPos.Set(pos.X + widthHalf, 0, pos.Z - lengthHalf); int topRightY = blockAccessor.GetTerrainMapheightAt(tmpPos); tmpPos.Set(pos.X - widthHalf, 0, pos.Z + lengthHalf); int botLeftY = blockAccessor.GetTerrainMapheightAt(tmpPos); tmpPos.Set(pos.X + widthHalf, 0, pos.Z + lengthHalf); int botRightY = blockAccessor.GetTerrainMapheightAt(tmpPos); int maxY = GameMath.Max(centerY, topLeftY, topRightY, botLeftY, botRightY); int minY = GameMath.Min(centerY, topLeftY, topRightY, botLeftY, botRightY); int diff = Math.Abs(maxY - minY); if (diff > 3) { return(false); } pos.Y = minY; // Ensure not deeply submerged in water tmpPos.Set(pos.X - widthHalf, pos.Y + 1 + OffsetY, pos.Z - lengthHalf); if (blockAccessor.GetBlock(tmpPos).IsLiquid()) { return(false); } tmpPos.Set(pos.X + widthHalf, pos.Y + 1 + OffsetY, pos.Z - lengthHalf); if (blockAccessor.GetBlock(tmpPos).IsLiquid()) { return(false); } tmpPos.Set(pos.X - widthHalf, pos.Y + 1 + OffsetY, pos.Z + lengthHalf); if (blockAccessor.GetBlock(tmpPos).IsLiquid()) { return(false); } tmpPos.Set(pos.X + widthHalf, pos.Y + 1 + OffsetY, pos.Z + lengthHalf); if (blockAccessor.GetBlock(tmpPos).IsLiquid()) { return(false); } pos.Y--; if (!satisfiesMinDistance(pos, worldForCollectibleResolve)) { return(false); } if (isStructureAt(pos, worldForCollectibleResolve)) { return(false); } LastPlacedSchematicLocation.Set(pos.X, pos.Y, pos.Z, pos.X + schematic.SizeX, pos.Y + schematic.SizeY, pos.Z + schematic.SizeZ); LastPlacedSchematic = schematic; schematic.PlaceRespectingBlockLayers(blockAccessor, worldForCollectibleResolve, pos, climateUpLeft, climateUpRight, climateBotLeft, climateBotRight, replaceblockids); return(true); }
public void Init(ICoreServerAPI api, BlockLayerConfig config, LCGRandom rand) { this.rand = rand; List <BlockSchematicStructure[]> schematics = new List <BlockSchematicStructure[]>(); for (int i = 0; i < Schematics.Length; i++) { string error = ""; IAsset[] assets; if (Schematics[i].EndsWith("*")) { assets = api.Assets.GetMany("worldgen/schematics/" + Schematics[i].Substring(0, Schematics[i].Length - 1)).ToArray(); } else { assets = new IAsset[] { api.Assets.Get("worldgen/schematics/" + Schematics[i] + ".json") }; } for (int j = 0; j < assets.Length; j++) { IAsset asset = assets[j]; BlockSchematicStructure schematic = asset.ToObject <BlockSchematicStructure>(); if (schematic == null) { api.World.Logger.Warning("Could not load {0}: {1}", Schematics[i], error); continue; } schematic.FromFileName = asset.Name; BlockSchematicStructure[] rotations = new BlockSchematicStructure[4]; rotations[0] = schematic; for (int k = 0; k < 4; k++) { if (k > 0) { rotations[k] = rotations[0].Clone(); rotations[k].TransformWhilePacked(api.World, EnumOrigin.BottomCenter, k * 90); } rotations[k].blockLayerConfig = config; rotations[k].Init(api.World.BlockAccessor); rotations[k].LoadMetaInformationAndValidate(api.World.BlockAccessor, api.World, schematic.FromFileName); } schematics.Add(rotations); } } this.schematicDatas = schematics.ToArray(); if (ReplaceWithBlocklayers != null) { replaceblockids = new ushort[ReplaceWithBlocklayers.Length]; for (int i = 0; i < replaceblockids.Length; i++) { Block block = api.World.GetBlock(ReplaceWithBlocklayers[i]); if (block == null) { throw new Exception(string.Format("Schematic with code {0} has replace block layer {1} defined, but no such block found!", Code, ReplaceWithBlocklayers[i])); } else { replaceblockids[i] = (ushort)block.Id; } } } }
protected BlockSchematicStructure GetGeneratableStructure(VillageSchematic schem, IBlockAccessor blockAccessor, IWorldAccessor worldForCollectibleResolve, BlockPos pos) { int chunksize = blockAccessor.ChunkSize; int climate = GameMath.BiLerpRgbColor((float)(pos.X % chunksize) / chunksize, (float)(pos.Z % chunksize) / chunksize, climateUpLeft, climateUpRight, climateBotLeft, climateBotRight); int num = rand.NextInt(schem.Structures.Length); BlockSchematicStructure schematic = schem.Structures[num]; int widthHalf = (int)Math.Ceiling(schematic.SizeX / 2f); int lengthHalf = (int)Math.Ceiling(schematic.SizeZ / 2f); pos.Y += schem.OffsetY; // Probe all 4 corners + center if they are on the same height int centerY = blockAccessor.GetTerrainMapheightAt(pos); tmpPos.Set(pos.X - widthHalf, 0, pos.Z - lengthHalf); int topLeftY = blockAccessor.GetTerrainMapheightAt(tmpPos); tmpPos.Set(pos.X + widthHalf, 0, pos.Z - lengthHalf); int topRightY = blockAccessor.GetTerrainMapheightAt(tmpPos); tmpPos.Set(pos.X - widthHalf, 0, pos.Z + lengthHalf); int botLeftY = blockAccessor.GetTerrainMapheightAt(tmpPos); tmpPos.Set(pos.X + widthHalf, 0, pos.Z + lengthHalf); int botRightY = blockAccessor.GetTerrainMapheightAt(tmpPos); int diff = GameMath.Max(centerY, topLeftY, topRightY, botLeftY, botRightY) - GameMath.Min(centerY, topLeftY, topRightY, botLeftY, botRightY); if (diff > 2) { return(null); } pos.Y += centerY - pos.Y + 1 + schem.OffsetY; if (pos.Y <= 0) { return(null); } // Ensure not floating on water tmpPos.Set(pos.X - widthHalf, pos.Y - 1, pos.Z - lengthHalf); if (blockAccessor.GetBlock(tmpPos).IsLiquid()) { return(null); } tmpPos.Set(pos.X + widthHalf, pos.Y - 1, pos.Z - lengthHalf); if (blockAccessor.GetBlock(tmpPos).IsLiquid()) { return(null); } tmpPos.Set(pos.X - widthHalf, pos.Y - 1, pos.Z + lengthHalf); if (blockAccessor.GetBlock(tmpPos).IsLiquid()) { return(null); } tmpPos.Set(pos.X + widthHalf, pos.Y - 1, pos.Z + lengthHalf); if (blockAccessor.GetBlock(tmpPos).IsLiquid()) { return(null); } // Ensure not submerged in water tmpPos.Set(pos.X - widthHalf, pos.Y, pos.Z - lengthHalf); if (blockAccessor.GetBlock(tmpPos).IsLiquid()) { return(null); } tmpPos.Set(pos.X + widthHalf, pos.Y, pos.Z - lengthHalf); if (blockAccessor.GetBlock(tmpPos).IsLiquid()) { return(null); } tmpPos.Set(pos.X - widthHalf, pos.Y, pos.Z + lengthHalf); if (blockAccessor.GetBlock(tmpPos).IsLiquid()) { return(null); } tmpPos.Set(pos.X + widthHalf, pos.Y, pos.Z + lengthHalf); if (blockAccessor.GetBlock(tmpPos).IsLiquid()) { return(null); } tmpPos.Set(pos.X - widthHalf, pos.Y + 1, pos.Z - lengthHalf); if (blockAccessor.GetBlock(tmpPos).IsLiquid()) { return(null); } tmpPos.Set(pos.X + widthHalf, pos.Y + 1, pos.Z - lengthHalf); if (blockAccessor.GetBlock(tmpPos).IsLiquid()) { return(null); } tmpPos.Set(pos.X - widthHalf, pos.Y + 1, pos.Z + lengthHalf); if (blockAccessor.GetBlock(tmpPos).IsLiquid()) { return(null); } tmpPos.Set(pos.X + widthHalf, pos.Y + 1, pos.Z + lengthHalf); if (blockAccessor.GetBlock(tmpPos).IsLiquid()) { return(null); } if (!TestUndergroundCheckPositions(blockAccessor, pos, schematic.UndergroundCheckPositions)) { return(null); } if (isStructureAt(pos, worldForCollectibleResolve)) { return(null); } return(schematic); }
public bool TryGenerate(IBlockAccessor blockAccessor, IWorldAccessor worldForCollectibleResolve, BlockPos pos, int climateUpLeft, int climateUpRight, int climateBotLeft, int climateBotRight, DidGenerate didGenerateStructure) { this.climateUpLeft = climateUpLeft; this.climateUpRight = climateUpRight; this.climateBotLeft = climateBotLeft; this.climateBotRight = climateBotRight; float cnt = QuantityStructures.nextFloat(); int minQuantity = (int)cnt; BlockPos schemPos = pos.Copy(); Cuboidi location = new Cuboidi(); rand.InitPositionSeed(pos.X, pos.Z); List <GeneratableStructure> generatables = new List <GeneratableStructure>(); while (cnt-- > 0) { if (cnt < 1 && rand.NextDouble() > cnt) { break; } int tries = 30; while (tries-- > 0) { schemPos.Set(pos); schemPos.Add(rand.NextInt(50) - 25, 0, rand.NextInt(50) - 25); schemPos.Y = blockAccessor.GetTerrainMapheightAt(schemPos); double rndVal = rand.NextDouble() * totalWeight; int i = 0; VillageSchematic schem = null; while (rndVal > 0) { schem = Schematics[i++]; rndVal -= schem.Weight; } BlockSchematicStructure struc = GetGeneratableStructure(schem, blockAccessor, worldForCollectibleResolve, schemPos); if (struc != null) { location.Set(schemPos.X, schemPos.Y, schemPos.Z, schemPos.X + struc.SizeX, schemPos.Y + struc.SizeY, schemPos.Z + struc.SizeZ); bool intersect = false; for (int k = 0; k < generatables.Count; k++) { if (location.IntersectsOrTouches(generatables[k].location)) { intersect = true; break; } } if (!intersect) { generatables.Add(new GeneratableStructure() { struc = struc, pos = schemPos.Copy(), location = location.Clone() }); } break; } } } if (generatables.Count >= minQuantity) { foreach (var val in generatables) { val.struc.PlaceRespectingBlockLayers(blockAccessor, worldForCollectibleResolve, val.pos, climateUpLeft, climateUpRight, climateBotLeft, climateBotRight, replaceblockids); didGenerateStructure(val.location, val.struc); } } return(true); }