private void BlockAccessor_OnStoreHistoryState(HistoryState obj) { obj.OldStartMarker = prevStartMarker?.Copy(); obj.OldEndMarker = prevEndMarker?.Copy(); obj.NewStartMarker = StartMarker?.Copy(); obj.NewEndMarker = EndMarker?.Copy(); prevStartMarker = StartMarker?.Copy(); prevEndMarker = EndMarker?.Copy(); }
public EntityBlockFalling(Block block, BlockEntity blockEntity, BlockPos initialPos, AssetLocation fallSound, float impactDamageMul, bool canFallSideways, float dustIntensity) { this.impactDamageMul = impactDamageMul; this.fallSound = fallSound; this.canFallSideways = canFallSideways; this.dustIntensity = dustIntensity; WatchedAttributes.SetBool("canFallSideways", canFallSideways); WatchedAttributes.SetFloat("dustIntensity", dustIntensity); if (fallSound != null) { WatchedAttributes.SetString("fallSound", fallSound.ToShortString()); } this.Code = new AssetLocation("blockfalling"); this.blockCode = block.Code; this.removedBlockentity = blockEntity; this.initialPos = initialPos.Copy(); // Must have a Copy() here! ServerPos.SetPos(initialPos); ServerPos.X += 0.5; ServerPos.Z += 0.5; Pos.SetFrom(ServerPos); }
private BlockPos BfsSearchPath(IWorldAccessor world, Queue <BlockPos> uncheckedPositions, BlockPos target, Block ourBlock) { BlockPos pos, npos = new BlockPos(); while (uncheckedPositions.Count > 0) { pos = uncheckedPositions.Dequeue(); int curDist = pos.ManhattenDistance(target); for (int i = 0; i < BlockFacing.HORIZONTALS.Length; i++) { BlockFacing facing = BlockFacing.HORIZONTALS[i]; npos.Set(pos.X + facing.Normali.X, target.Y, pos.Z + facing.Normali.Z); if (npos.ManhattenDistance(target) > curDist) { continue; } if (npos.Equals(target)) { return(pos); } Block block = world.BlockAccessor.GetBlock(npos); if (!block.IsLiquid() && block.Replaceable < ReplacableThreshold) { continue; } uncheckedPositions.Enqueue(npos.Copy()); } } return(null); }
// http://members.chello.at/~easyfilter/bresenham.html public List <BlockPos> PlotLine3d(int x0, int y0, int z0, int x1, int y1, int z1) { int dx = Math.Abs(x1 - x0), sx = x0 < x1 ? 1 : -1; int dy = Math.Abs(y1 - y0), sy = y0 < y1 ? 1 : -1; int dz = Math.Abs(z1 - z0), sz = z0 < z1 ? 1 : -1; int dm = GameMath.Max(dx, dy, dz), i = dm; /* maximum difference */ x1 = y1 = z1 = dm / 2; /* error offset */ BlockPos pos = new BlockPos(); HashSet <BlockPos> blocks = new HashSet <BlockPos>(); for (;;) { /* loop */ pos.Set(x0, y0, z0); blocks.Add(pos.Copy()); if (i-- == 0) { break; } x1 -= dx; if (x1 < 0) { x1 += dm; x0 += sx; } y1 -= dy; if (y1 < 0) { y1 += dm; y0 += sy; } z1 -= dz; if (z1 < 0) { z1 += dm; z0 += sz; } } return(blocks.ToList()); }
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); }
// Returns the block pos that is adjacent to a hole BlockPos FindHoleInPit() { smokeLocations.Clear(); HashSet <BlockPos> visitedPositions = new HashSet <BlockPos>(); Queue <BlockPos> bfsQueue = new Queue <BlockPos>(); bfsQueue.Enqueue(Pos); int firewoodBlockId = Api.World.GetBlock(new AssetLocation("firewoodpile")).BlockId; int charcoalPitBlockId = Api.World.GetBlock(new AssetLocation("charcoalpit")).BlockId; int maxHalfSize = 6; while (bfsQueue.Count > 0) { BlockPos bpos = bfsQueue.Dequeue(); BlockPos bposGround = bpos.Copy(); bposGround.Y = 0; int yMax = 0; smokeLocations.TryGetValue(bposGround, out yMax); smokeLocations[bposGround] = Math.Max(yMax, bpos.Y); foreach (BlockFacing facing in BlockFacing.ALLFACES) { BlockPos npos = bpos.AddCopy(facing); IWorldChunk chunk = Api.World.BlockAccessor.GetChunkAtBlockPos(npos); if (chunk == null) { continue; // Maybe at the endge of the loaded chunk } Block nBlock = chunk.GetLocalBlockAtBlockPos(Api.World, npos); if (!nBlock.SideSolid[facing.Opposite.Index] && nBlock.BlockId != firewoodBlockId && nBlock.BlockId != charcoalPitBlockId) { return(bpos); } // Only traverse inside the firewood pile if (nBlock.BlockId != firewoodBlockId) { continue; } // Only traverse within a 12x12x12 block cube bool inCube = Math.Abs(npos.X - Pos.X) <= maxHalfSize && Math.Abs(npos.Y - Pos.Y) <= maxHalfSize && Math.Abs(npos.Z - Pos.Z) <= maxHalfSize; if (inCube && !visitedPositions.Contains(npos)) { bfsQueue.Enqueue(npos); visitedPositions.Add(npos); } } } return(null); }
public override void OnBuild(WorldEdit worldEdit, int oldBlockId, BlockSelection blockSel, ItemStack withItemStack) { if (startPos == null) { return; } BlockPos destPos = blockSel.Position.AddCopy(blockSel.Face.Opposite); Block block = blockAccessRev.GetBlock(blockSel.Position); if (PlaceMode) { block = blockAccessRev.GetBlock(0); } worldEdit.sapi.World.BlockAccessor.SetBlock(oldBlockId, blockSel.Position); if (!worldEdit.MayPlace(block, startPos.ManhattenDistance(destPos))) { return; } GameMath.BresenHamPlotLine3d(startPos.X, startPos.Y, startPos.Z, destPos.X, destPos.Y, destPos.Z, (pos) => blockAccessRev.SetBlock(block.BlockId, pos, withItemStack)); if (LineMode == EnumLineStartPoint.LineStrip) { startPos = destPos.Copy(); } blockAccessRev.SetHistoryStateBlock(blockSel.Position.X, blockSel.Position.Y, blockSel.Position.Z, oldBlockId, blockAccessRev.GetBlockId(blockSel.Position)); blockAccessRev.Commit(); }
public override void GetBlockInfo(IPlayer forPlayer, StringBuilder dsc) { if (!FullyRepaired) { dsc.AppendLine(Lang.Get("Seems to be missing a couple of gears. I think I've seen such gears before.")); return; } else { if (tpLocation == null) { string[] lines = new string[] { Lang.Get("Warping spacetime."), Lang.Get("Warping spacetime.."), Lang.Get("Warping spacetime...") }; dsc.AppendLine(lines[(int)(Api.World.ElapsedMilliseconds / 1000f) % 3]); return; } } if (forPlayer.WorldData.CurrentGameMode == EnumGameMode.Creative) { BlockPos pos = Api.World.DefaultSpawnPosition.AsBlockPos; dsc.AppendLine(Lang.Get("Teleports to {0}", tpLocation.Copy().Sub(pos.X, 0, pos.Z))); } else { dsc.AppendLine(Lang.Get("Spacetime subduction completed.")); } }
private void PlaceMeteorResources(BlockPos bpos, int metalBlockID, int stoneBlockID) { int placeMeteorBlockRand = explosionRand.Next(0, 101); switch (placeMeteorBlockRand < meteorImpactResourceChance) { case true: int metalBlockRand = explosionRand.Next(0, 101); switch (metalBlockRand < metalResourceChance) { case true: blockAccessor.SetBlock(metalBlockID, bpos); break; case false: blockAccessor.SetBlock(stoneBlockID, bpos); break; } positionsChanged.Add(bpos.Copy()); break; case false: break; } }
public static List <BlockPos> Cuboid(BlockPos start, BlockPos end) { List <BlockPos> positions = new List <BlockPos>(); 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)); BlockPos curPos = startPos.Copy(); while (curPos.X < finalPos.X) { curPos.Y = startPos.Y; while (curPos.Y < finalPos.Y) { curPos.Z = startPos.Z; while (curPos.Z < finalPos.Z) { positions.Add(curPos.Copy()); curPos.Z++; } curPos.Y++; } curPos.X++; } return(positions); }
public override bool TryPlaceBlockForWorldGen(IBlockAccessor blockAccessor, BlockPos pos, BlockFacing onBlockFace, LCGRandom worldGenRand) { bool didplace = false; if (blockAccessor.GetBlock(pos).Replaceable < 6000) { return(false); } BlockPos npos = pos.Copy(); for (int i = 0; i < 150 + worldGenRand.NextInt(30); i++) { npos.X = pos.X + worldGenRand.NextInt(11) - 5; npos.Y = pos.Y + worldGenRand.NextInt(11) - 5; npos.Z = pos.Z + worldGenRand.NextInt(11) - 5; if (npos.Y > api.World.SeaLevel - 10 || npos.Y < 25) { continue; // To hot for glowworms } if (blockAccessor.GetBlock(npos).Replaceable < 6000) { continue; } didplace |= TryGenGlowWorm(blockAccessor, npos, worldGenRand); } return(didplace); }
public void FloodFillAt(WorldEdit worldEdit, Block blockToPlace, ItemStack withItemStack, int posX, int posY, int posZ) { bfsQueue.Clear(); fillablePositions.Clear(); if (posY <= 0 || posY >= mapheight - 1) { return; } bfsQueue.Enqueue(new Vec4i(posX, posY, posZ, 0)); fillablePositions.Add(new BlockPos(posX, posY, posZ)); float radius = SearchRadius; BlockFacing[] faces = Mode == 2 ? BlockFacing.HORIZONTALS : BlockFacing.ALLFACES; BlockPos curPos = new BlockPos(); while (bfsQueue.Count > 0) { Vec4i bpos = bfsQueue.Dequeue(); foreach (BlockFacing facing in faces) { curPos.Set(bpos.X + facing.Normali.X, bpos.Y + facing.Normali.Y, bpos.Z + facing.Normali.Z); Block block = blockAccessRev.GetBlock(curPos); bool inBounds = bpos.W < radius; if (inBounds) { if (block.Replaceable >= 6000 && !fillablePositions.Contains(curPos)) { bfsQueue.Enqueue(new Vec4i(curPos.X, curPos.Y, curPos.Z, bpos.W + 1)); fillablePositions.Add(curPos.Copy()); } } else { if (CheckEnclosure) { fillablePositions.Clear(); bfsQueue.Clear(); worldEdit.Bad("Cannot flood fill here, not enclosed area. Enforce enclosed area or disable enclosure check."); break; } } } } foreach (BlockPos p in fillablePositions) { blockAccessRev.SetBlock(blockToPlace.BlockId, p, withItemStack); } worldEdit.Bad(fillablePositions.Count + " blocks placed"); }
private int FillArea(ItemStack blockStack, BlockPos start, BlockPos end) { int updated = 0; 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)); BlockPos curPos = startPos.Copy(); int dx = finalPos.X - startPos.X; int dy = finalPos.Y - startPos.Y; int dz = finalPos.Z - startPos.Z; int quantityBlocks = dx * dy * dz; int blockId = 0; Block block = blockStack?.Block; if (block != null) { blockId = block.Id; } if (block != null && !MayPlace(block, quantityBlocks)) { return(0); } if (quantityBlocks > 1000) { Good((block == null ? "Clearing" : "Placing") + " " + (dx * dy * dz) + " blocks..."); } while (curPos.X < finalPos.X) { curPos.Y = startPos.Y; while (curPos.Y < finalPos.Y) { curPos.Z = startPos.Z; while (curPos.Z < finalPos.Z) { workspace.revertableBlockAccess.SetLiquidBlock(0, curPos); workspace.revertableBlockAccess.SetBlock(blockId, curPos, blockStack); curPos.Z++; updated++; } curPos.Y++; } curPos.X++; } workspace.revertableBlockAccess.Commit(); return(updated); }
private void ImportArea(string filename, BlockPos startPos, EnumOrigin origin) { string infilepath = Path.Combine(exportFolderPath, filename); if (!File.Exists(infilepath) && File.Exists(infilepath + ".json")) { infilepath += ".json"; } if (!File.Exists(infilepath)) { Bad("Can't import " + filename + ", it does not exist"); return; } BlockSchematic blockdata = null; try { using (TextReader textReader = new StreamReader(infilepath)) { blockdata = JsonConvert.DeserializeObject <BlockSchematic>(textReader.ReadToEnd()); textReader.Close(); } } catch (IOException e) { Good("Failed loading " + filename + " : " + e.Message); return; } BlockPos originPos = startPos.Copy(); if (origin == EnumOrigin.TopCenter) { originPos.X -= blockdata.SizeX / 2; originPos.Y -= blockdata.SizeY; originPos.Z -= blockdata.SizeZ / 2; } if (origin == EnumOrigin.BottomCenter) { originPos.X -= blockdata.SizeX / 2; originPos.Z -= blockdata.SizeZ / 2; } IBlockAccessor blockAcccessor = api.WorldManager.GetBlockAccessorBulkUpdate(true, true, false); blockdata.Place(blockAcccessor, api.World, originPos); blockAcccessor.Commit(); }
internal TeleporterLocation GetOrCreateLocation(BlockPos pos) { TeleporterLocation loc = null; if (Locations.TryGetValue(pos, out loc)) { return(loc); } loc = new TeleporterLocation() { SourceName = "Location-" + (Locations.Count + 1), SourcePos = pos.Copy() }; Locations[loc.SourcePos] = loc; return(loc); }
internal static void TryCreateData(BlockPos pos, bool available = false) { if (pos == null) { throw new ArgumentNullException(); } if (Teleports.ContainsKey(pos)) { return; } TeleportData data = new TeleportData() { Available = available, Name = defNames.ElementAt(api.World.Rand.Next(defNames.Count)) }; AddTeleport(pos.Copy(), data); }
public void OnFirePlaced(BlockPos firePos, BlockPos fuelPos, string startedByPlayerUid) { if (IsBurning || !ShouldBurn()) { return; } this.startedByPlayerUid = startedByPlayerUid; FirePos = firePos.Copy(); FuelPos = fuelPos.Copy(); if (FuelPos == null || !canBurn(FuelPos)) { foreach (BlockFacing facing in BlockFacing.ALLFACES) { BlockPos npos = FirePos.AddCopy(facing); fuelBlock = Api.World.BlockAccessor.GetBlock(npos); if (canBurn(npos)) { FuelPos = npos; startDuration = remainingBurnDuration = fuelBlock.CombustibleProps.BurnDuration; return; } } startDuration = 1; remainingBurnDuration = 1; FuelPos = FirePos.Copy(); // No fuel left } else { fuelBlock = Api.World.BlockAccessor.GetBlock(FuelPos); if (fuelBlock.CombustibleProps != null) { startDuration = remainingBurnDuration = fuelBlock.CombustibleProps.BurnDuration; } } startBurning(); }
private int FillArea(ushort blockId, BlockPos start, BlockPos end) { int updated = 0; IBlockAccessor blockAcccessor = api.WorldManager.GetBlockAccessorBulkUpdate(true, true); 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)); BlockPos curPos = startPos.Copy(); int dx = finalPos.X - startPos.X; int dy = finalPos.Y - startPos.Y; int dz = finalPos.Z - startPos.Z; if (dx * dy * dz > 1000) { Good((blockId == 0 ? "Clearing" : "Placing") + " " + (dx * dy * dz) + " blocks..."); } while (curPos.X < finalPos.X) { curPos.Y = startPos.Y; while (curPos.Y < finalPos.Y) { curPos.Z = startPos.Z; while (curPos.Z < finalPos.Z) { blockAcccessor.SetBlock(blockId, curPos); curPos.Z++; updated++; } curPos.Y++; } curPos.X++; } blockAcccessor.Commit(); return(updated); }
private void GenRareColorPatch(IBlockAccessor blockAccessor, BlockPos pos, Block block, LCGRandom worldGenRand) { int cnt = 2 + worldGenRand.NextInt(6); int tries = 30; BlockPos npos = pos.Copy(); while (cnt > 0 && tries-- > 0) { npos.Set(pos).Add(worldGenRand.NextInt(5) - 2, 0, worldGenRand.NextInt(5) - 2); npos.Y = blockAccessor.GetTerrainMapheightAt(npos) + 1; Block nblock = blockAccessor.GetBlock(npos); if ((nblock.IsReplacableBy(block) || nblock is BlockLupine) && CanPlantStay(blockAccessor, npos)) { blockAccessor.SetBlock(block.BlockId, npos); cnt--; } } }
public void GrowTree(IBlockAccessor blockAccessor, BlockPos pos, bool skipForestFloor, float sizeModifier = 1, float vineGrowthChance = 0, float otherBlockChance = 1, int treesInChunkGenerated = 0) { float f = otherBlockChance == 0 ? (3 + (float)rand.NextDouble() * 6) : (3 + (float)rand.NextDouble() * 4) * 3 * 3; int quantity = GameMath.RoundRandom(rand, f); BlockPos npos = pos.Copy(); sizeModifier = GameMath.Mix(sizeModifier, 1, 0.5f); sizeModifier *= 1 + ((float)rand.NextDouble() * 0.5f); while (quantity-- > 0) { float dist = Math.Max(1, pos.DistanceTo(npos) - 2); GrowStalk(blockAccessor, npos.UpCopy(), dist, sizeModifier, vineGrowthChance); // Potentially grow another one nearby npos.Set(pos); npos.X += rand.Next(8) - 4; npos.Z += rand.Next(8) - 4; // Test up to 2 blocks up and down. bool foundSuitableBlock = false; for (int y = 2; y >= -2; y--) { Block block = blockAccessor.GetBlock(npos.X, npos.Y + y, npos.Z); if (block.Fertility > 0) { npos.Y = npos.Y + y; foundSuitableBlock = true; break; } } if (!foundSuitableBlock) { break; } } }
/// <summary> /// Returns true if the given air block position is next to a combustible block, false otherwise. The /// block must be combustible and at a burnable temperature /// </summary> /// <param name="world"></param> /// <param name="lavaPos"></param> /// <param name="airBlockPos"></param> /// <returns></returns> private BlockFacing IsNextToCombustibleBlock(IWorldAccessor world, BlockPos lavaPos, BlockPos airBlockPos) { Block airBlock = world.BlockAccessor.GetBlock(airBlockPos); if (airBlock.BlockId == 0) { foreach (BlockFacing facing in BlockFacing.ALLFACES) { if (facing != BlockFacing.DOWN) { BlockPos combustibleBlockPos = airBlockPos.Copy().Add(facing); Block combustibleBlock = world.BlockAccessor.GetBlock(combustibleBlockPos); if (combustibleBlock.CombustibleProps != null && combustibleBlock.CombustibleProps.BurnTemperature <= GetTemperatureAtLocation(lavaPos, airBlockPos)) { return(facing); } } } } return(null); }
public override bool TryPlaceBlockForWorldGen(IBlockAccessor blockAccessor, BlockPos pos, BlockFacing onBlockFace, Random worldGenRand) { bool didplace = false; if (blockAccessor.GetBlock(pos).Replaceable < 6000) { return(false); } pos = pos.Copy(); for (int i = 0; i < 5 + worldGenRand.Next(25); i++) { if (pos.Y < 15) { continue; // To hot for stalactites } didplace |= TryGenStalag(blockAccessor, pos, worldGenRand.Next(4), worldGenRand); pos.X += worldGenRand.Next(9) - 4; pos.Y += worldGenRand.Next(3) - 1; pos.Z += worldGenRand.Next(9) - 4; } return(didplace); }
/// <summary> /// For placement of ruins during worldgen, replaces the topsoil with the area specific soil (e.g. sand) /// </summary> /// <param name="blockAccessor"></param> /// <param name="blocks"></param> /// <param name="startPos"></param> /// <param name="climateUpLeft"></param> /// <param name="climateUpRight"></param> /// <param name="climateBotLeft"></param> /// <param name="climateBotRight"></param> /// <param name="replaceblockids"></param> /// <returns></returns> public int PlaceRespectingBlockLayers(IBlockAccessor blockAccessor, IWorldAccessor worldForCollectibleResolve, BlockPos startPos, int climateUpLeft, int climateUpRight, int climateBotLeft, int climateBotRight, int[] replaceblockids, bool replaceMetaBlocks = true) { BlockPos curPos = new BlockPos(); int placed = 0; int chunksize = blockAccessor.ChunkSize; for (int x = 0; x < SizeX; x++) { for (int z = 0; z < SizeZ; z++) { curPos.Set(x + startPos.X, startPos.Y, z + startPos.Z); IMapChunk mapchunk = blockAccessor.GetMapChunkAtBlockPos(curPos); int rockblockid = mapchunk.TopRockIdMap[(curPos.Z % chunksize) * chunksize + curPos.X % chunksize]; int depth = 0; int maxY = -1; for (int y = SizeY - 1; y >= 0; y--) { curPos.Set(x + startPos.X, y + startPos.Y, z + startPos.Z); Block newBlock = blocksByPos[x, y, z]; if (newBlock == null) { continue; } if (replaceMetaBlocks && newBlock == undergroundBlock) { continue; } if (newBlock.Replaceable < 1000) { if (replaceblockids.Length > depth && newBlock.BlockId == replaceblockids[depth]) { int climate = GameMath.BiLerpRgbColor((float)x / chunksize, (float)z / chunksize, climateUpLeft, climateUpRight, climateBotLeft, climateBotRight); newBlock = GetBlockLayerBlock((climate >> 8) & 0xff, (climate >> 16) & 0xff, startPos.Y, rockblockid, depth, newBlock, worldForCollectibleResolve.Blocks); } depth++; } Block oldBlock = blockAccessor.GetBlock(curPos); int p = handler(blockAccessor, curPos, oldBlock, newBlock); placed += p; if (p > 0 && !newBlock.RainPermeable) { if (newBlock == fillerBlock || newBlock == pathwayBlock) { int lx = curPos.X % chunksize; int lz = curPos.Z % chunksize; if (mapchunk.RainHeightMap[lz * chunksize + lx] == curPos.Y) { mapchunk.RainHeightMap[lz * chunksize + lx]--; } } else { maxY = Math.Max(curPos.Y, maxY); } } byte[] lightHsv = newBlock.GetLightHsv(blockAccessor, curPos); if (lightHsv[2] > 0 && blockAccessor is IWorldGenBlockAccessor) { ((IWorldGenBlockAccessor)blockAccessor).ScheduleBlockLightUpdate(curPos.Copy(), oldBlock.BlockId, newBlock.BlockId); } } // In the post pass the rain map does not update, so let's set it ourselves if (maxY >= 0) { int lx = curPos.X % chunksize; int lz = curPos.Z % chunksize; int y = mapchunk.RainHeightMap[lz * chunksize + lx]; mapchunk.RainHeightMap[lz * chunksize + lx] = (ushort)Math.Max(y, maxY); } } } PlaceEntitiesAndBlockEntities(blockAccessor, worldForCollectibleResolve, startPos); return(placed); }
/// <summary> /// Will place all blocks using the supplied replace mode. Note: If you use a revertable or bulk block accessor you will have to call PlaceBlockEntities() after the Commit() /// </summary> /// <param name="blockAccessor"></param> /// <param name="worldForCollectibleResolve"></param> /// <param name="startPos"></param> /// <param name="mode"></param> /// <param name="replaceMetaBlocks"></param> /// <returns></returns> public virtual int Place(IBlockAccessor blockAccessor, IWorldAccessor worldForCollectibleResolve, BlockPos startPos, EnumReplaceMode mode, bool replaceMetaBlocks = true) { BlockPos curPos = new BlockPos(); int placed = 0; PlaceBlockDelegate handler = null; switch (ReplaceMode) { case EnumReplaceMode.ReplaceAll: if (replaceMetaBlocks) { handler = PlaceReplaceAllReplaceMeta; } else { handler = PlaceReplaceAllKeepMeta; } break; case EnumReplaceMode.Replaceable: if (replaceMetaBlocks) { handler = PlaceReplaceableReplaceMeta; } else { handler = PlaceReplaceableKeepMeta; } break; case EnumReplaceMode.ReplaceAllNoAir: if (replaceMetaBlocks) { handler = PlaceReplaceAllNoAirReplaceMeta; } else { handler = PlaceReplaceAllNoAirKeepMeta; } break; case EnumReplaceMode.ReplaceOnlyAir: if (replaceMetaBlocks) { handler = PlaceReplaceOnlyAirReplaceMeta; } else { handler = PlaceReplaceOnlyAirKeepMeta; } break; } for (int i = 0; i < Indices.Count; i++) { uint index = Indices[i]; int storedBlockid = BlockIds[i]; int dx = (int)(index & 0x1ff); int dy = (int)((index >> 20) & 0x1ff); int dz = (int)((index >> 10) & 0x1ff); AssetLocation blockCode = BlockCodes[storedBlockid]; Block newBlock = blockAccessor.GetBlock(blockCode); if (newBlock == null || (replaceMetaBlocks && newBlock == undergroundBlock)) { continue; } curPos.Set(dx + startPos.X, dy + startPos.Y, dz + startPos.Z); Block oldBlock = blockAccessor.GetBlock(curPos); placed += handler(blockAccessor, curPos, oldBlock, newBlock); if (newBlock.LightHsv[2] > 0 && blockAccessor is IWorldGenBlockAccessor) { ((IWorldGenBlockAccessor)blockAccessor).ScheduleBlockLightUpdate(curPos.Copy(), oldBlock.BlockId, newBlock.BlockId); } } if (!(blockAccessor is IBlockAccessorRevertable)) { PlaceEntitiesAndBlockEntities(blockAccessor, worldForCollectibleResolve, startPos); } return(placed); }
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); }
/// <summary> /// Places all the entities and blocks in the schematic at the position. /// </summary> /// <param name="blockAccessor"></param> /// <param name="worldForCollectibleResolve"></param> /// <param name="startPos"></param> public void PlaceEntitiesAndBlockEntities(IBlockAccessor blockAccessor, IWorldAccessor worldForCollectibleResolve, BlockPos startPos) { BlockPos curPos = new BlockPos(); int schematicSeed = worldForCollectibleResolve.Rand.Next(); foreach (var val in BlockEntities) { uint index = val.Key; int dx = (int)(index & 0x1ff); int dy = (int)((index >> 20) & 0x1ff); int dz = (int)((index >> 10) & 0x1ff); curPos.Set(dx + startPos.X, dy + startPos.Y, dz + startPos.Z); BlockEntity be = blockAccessor.GetBlockEntity(curPos); // Block entities need to be manually initialized for world gen block access if (be == null && blockAccessor is IWorldGenBlockAccessor) { Block block = blockAccessor.GetBlock(curPos); if (block.EntityClass != null) { blockAccessor.SpawnBlockEntity(block.EntityClass, curPos); be = blockAccessor.GetBlockEntity(curPos); } } if (be != null) { Block block = blockAccessor.GetBlock(curPos); if (block.EntityClass != worldForCollectibleResolve.ClassRegistry.GetBlockEntityClass(be.GetType())) { worldForCollectibleResolve.Logger.Warning("Could not import block entity data for schematic at {0}. There is already {1}, expected {2}. Probably overlapping ruins.", curPos, be.GetType(), block.EntityClass); continue; } ITreeAttribute tree = DecodeBlockEntityData(val.Value); tree.SetInt("posx", curPos.X); tree.SetInt("posy", curPos.Y); tree.SetInt("posz", curPos.Z); be.FromTreeAttributes(tree, worldForCollectibleResolve); be.OnLoadCollectibleMappings(worldForCollectibleResolve, BlockCodes, ItemCodes, schematicSeed); be.Pos = curPos.Copy(); } } foreach (string entityData in Entities) { using (MemoryStream ms = new MemoryStream(Ascii85.Decode(entityData))) { BinaryReader reader = new BinaryReader(ms); string className = reader.ReadString(); Entity entity = worldForCollectibleResolve.ClassRegistry.CreateEntity(className); entity.FromBytes(reader, false); entity.DidImportOrExport(startPos); // Not ideal but whatever if (blockAccessor is IWorldGenBlockAccessor) { (blockAccessor as IWorldGenBlockAccessor).AddEntity(entity); } else { worldForCollectibleResolve.SpawnEntity(entity); } } } }
/// <summary> /// Gets the starting position of the schematic. /// </summary> /// <param name="pos"></param> /// <param name="origin"></param> /// <returns></returns> public virtual BlockPos GetStartPos(BlockPos pos, EnumOrigin origin) { return(AdjustStartPos(pos.Copy(), origin)); }
public override void GenDeposit(IBlockAccessor blockAccessor, IServerChunk[] chunks, int chunkX, int chunkZ, BlockPos depoCenterPos, ref Dictionary <BlockPos, DepositVariant> subDepositsToPlace) { int depositGradeIndex = PlaceBlock.MaxGrade == 0 ? 0 : DepositRand.NextInt(PlaceBlock.MaxGrade); int radius = Math.Min(64, (int)Radius.nextFloat(1, DepositRand)); if (radius <= 0) { return; } // Let's deform that perfect circle a bit (+/- 25%) float deform = GameMath.Clamp(DepositRand.NextFloat() - 0.5f, -0.25f, 0.25f); radiusX = radius - (int)(radius * deform); radiusZ = radius + (int)(radius * deform); int baseX = chunkX * chunksize; int baseZ = chunkZ * chunksize; // No need to caluclate further if this deposit won't be part of this chunk if (depoCenterPos.X + radiusX < baseX - 6 || depoCenterPos.Z + radiusZ < baseZ - 6 || depoCenterPos.X - radiusX >= baseX + chunksize + 6 || depoCenterPos.Z - radiusZ >= baseZ + chunksize + 6) { return; } IMapChunk heremapchunk = chunks[0].MapChunk; beforeGenDeposit(heremapchunk, depoCenterPos); // Ok generate float th = Thickness.nextFloat(1, DepositRand); depoitThickness = (int)th + (DepositRand.NextFloat() < th - (int)th ? 1 : 0); float xRadSqInv = 1f / (radiusX * radiusX); float zRadSqInv = 1f / (radiusZ * radiusZ); bool parentBlockOk = false; ResolvedDepositBlock resolvedPlaceBlock = null; bool shouldGenSurfaceDeposit = DepositRand.NextFloat() <= GenSurfaceBlockChance && SurfaceBlock != null; int lx = GameMath.Mod(depoCenterPos.X, chunksize); int lz = GameMath.Mod(depoCenterPos.Z, chunksize); int distx, distz; // No need to go search far beyond chunk boundaries int minx = baseX - 6; int maxx = baseX + chunksize + 6; int minz = baseZ - 6; int maxz = baseZ + chunksize + 6; minx = GameMath.Clamp(depoCenterPos.X - radiusX, minx, maxx); maxx = GameMath.Clamp(depoCenterPos.X + radiusX, minx, maxx); minz = GameMath.Clamp(depoCenterPos.Z - radiusZ, minz, maxz); maxz = GameMath.Clamp(depoCenterPos.Z + radiusZ, minz, maxz); //int placed = 0; float invChunkAreaSize = 1f / (chunksize * chunksize); double val = 1; for (int posx = minx; posx < maxx; posx++) { targetPos.X = posx; lx = targetPos.X - baseX; distx = posx - depoCenterPos.X; float xSq = distx * distx * xRadSqInv; for (int posz = minz; posz < maxz; posz++) { targetPos.Y = depoCenterPos.Y; targetPos.Z = posz; lz = targetPos.Z - baseZ; distz = posz - depoCenterPos.Z; // Kinda weird mathematically speaking, but seems to work as a means to distort the perfect circleness of deposits ¯\_(ツ)_/¯ // Also not very efficient to use direct perlin noise in here :/ // But after ~10 hours of failing (=weird lines of missing deposit material) with a pre-generated 2d distortion map i give up >.> val = 1 - (radius > 3 ? DistortNoiseGen.Noise(targetPos.X / 3.0, targetPos.Z / 3.0) * 0.2 : 0); double distanceToEdge = val - (xSq + distz * distz * zRadSqInv); if (distanceToEdge < 0 || lx < 0 || lz < 0 || lx >= chunksize || lz >= chunksize) { continue; } loadYPosAndThickness(heremapchunk, lx, lz, targetPos, distanceToEdge); // Some deposits may not appear all over cliffs if (Math.Abs(depoCenterPos.Y - targetPos.Y) > MaxYRoughness) { continue; } for (int y = 0; y < hereThickness; y++) { if (targetPos.Y <= 1 || targetPos.Y >= worldheight) { continue; } int index3d = ((targetPos.Y % chunksize) * chunksize + lz) * chunksize + lx; int blockId = chunks[targetPos.Y / chunksize].Blocks[index3d]; if (!IgnoreParentTestPerBlock || !parentBlockOk) { parentBlockOk = placeBlockByInBlockId.TryGetValue(blockId, out resolvedPlaceBlock); } if (parentBlockOk && resolvedPlaceBlock.Blocks.Length > 0) { int gradeIndex = Math.Min(resolvedPlaceBlock.Blocks.Length - 1, depositGradeIndex); Block placeblock = resolvedPlaceBlock.Blocks[gradeIndex]; if (variant.WithBlockCallback || (WithLastLayerBlockCallback && y == hereThickness - 1)) { placeblock.TryPlaceBlockForWorldGen(blockAccessor, targetPos.Copy(), BlockFacing.UP, rand); } else { chunks[targetPos.Y / chunksize].Blocks[index3d] = placeblock.BlockId; //placed++; } if (variant.ChildDeposits != null) { for (int i = 0; i < variant.ChildDeposits.Length; i++) { float rndVal = DepositRand.NextFloat(); float quantity = variant.ChildDeposits[i].TriesPerChunk * invChunkAreaSize; if (quantity > rndVal) { if (ShouldPlaceAdjustedForOreMap(variant.ChildDeposits[i], targetPos.X, targetPos.Z, quantity, rndVal)) { subDepositsToPlace[targetPos.Copy()] = variant.ChildDeposits[i]; } } } } if (shouldGenSurfaceDeposit) { int surfaceY = heremapchunk.RainHeightMap[lz * chunksize + lx]; int depth = surfaceY - targetPos.Y; float chance = SurfaceBlockChance * Math.Max(0, 1.11f - depth / 9f); if (surfaceY < worldheight && DepositRand.NextFloat() < chance) { Block belowBlock = Api.World.Blocks[chunks[surfaceY / chunksize].Blocks[((surfaceY % chunksize) * chunksize + lz) * chunksize + lx]]; index3d = (((surfaceY + 1) % chunksize) * chunksize + lz) * chunksize + lx; if (belowBlock.SideSolid[BlockFacing.UP.Index] && chunks[(surfaceY + 1) / chunksize].Blocks[index3d] == 0) { chunks[(surfaceY + 1) / chunksize].Blocks[index3d] = surfaceBlockByInBlockId[blockId].Blocks[0].BlockId; } } } } targetPos.Y--; } } } //Console.WriteLine("placed {0} blocks", placed); }
private void onCmdAStar(IServerPlayer player, int groupId, CmdArgs args) { string subcmd = args.PopWord(); BlockPos plrPos = player.Entity.ServerPos.XYZ.AsBlockPos; PathfindSystem pfs = sapi.ModLoader.GetModSystem <PathfindSystem>(); Cuboidf narrow = new Cuboidf(-0.4f, 0, -0.4f, 0.4f, 1.5f, 0.4f); Cuboidf narrower = new Cuboidf(-0.2f, 0, -0.2f, 0.2f, 1.5f, 0.2f); Cuboidf wide = new Cuboidf(-0.6f, 0, -0.6f, 0.6f, 1.5f, 0.6f); Cuboidf collbox = narrow; int maxFallHeight = 3; float stepHeight = 1.01f; switch (subcmd) { case "start": start = plrPos.Copy(); sapi.World.HighlightBlocks(player, 26, new List <BlockPos>() { start }, new List <int>() { ColorUtil.ColorFromRgba(255, 255, 0, 128) }, EnumHighlightBlocksMode.Absolute, EnumHighlightShape.Arbitrary); break; case "end": end = plrPos.Copy(); sapi.World.HighlightBlocks(player, 27, new List <BlockPos>() { end }, new List <int>() { ColorUtil.ColorFromRgba(255, 0, 255, 128) }, EnumHighlightBlocksMode.Absolute, EnumHighlightShape.Arbitrary); break; case "bench": if (start == null || end == null) { return; } Stopwatch sw = new Stopwatch(); sw.Start(); for (int i = 0; i < 15; i++) { List <PathNode> nodes = pfs.FindPath(start, end, maxFallHeight, stepHeight, collbox); } sw.Stop(); float timeMs = (float)sw.ElapsedMilliseconds / 15f; player.SendMessage(groupId, string.Format("15 searches average: {0} ms", (int)timeMs), EnumChatType.Notification); return; case "clear": start = null; end = null; sapi.World.HighlightBlocks(player, 2, new List <BlockPos>(), EnumHighlightBlocksMode.Absolute, EnumHighlightShape.Arbitrary); sapi.World.HighlightBlocks(player, 26, new List <BlockPos>(), EnumHighlightBlocksMode.Absolute, EnumHighlightShape.Arbitrary); sapi.World.HighlightBlocks(player, 27, new List <BlockPos>(), EnumHighlightBlocksMode.Absolute, EnumHighlightShape.Arbitrary); break; } if (start == null || end == null) { sapi.World.HighlightBlocks(player, 2, new List <BlockPos>(), EnumHighlightBlocksMode.Absolute, EnumHighlightShape.Arbitrary); } if (start != null && end != null) { Stopwatch sw = new Stopwatch(); sw.Start(); List <PathNode> nodes = pfs.FindPath(start, end, maxFallHeight, stepHeight, collbox); sw.Stop(); int timeMs = (int)sw.ElapsedMilliseconds; player.SendMessage(groupId, string.Format("Search took {0} ms, {1} nodes checked", timeMs, pfs.astar.NodesChecked), EnumChatType.Notification); if (nodes == null) { player.SendMessage(groupId, "No path found", EnumChatType.CommandError); sapi.World.HighlightBlocks(player, 2, new List <BlockPos>(), EnumHighlightBlocksMode.Absolute, EnumHighlightShape.Arbitrary); sapi.World.HighlightBlocks(player, 3, new List <BlockPos>(), EnumHighlightBlocksMode.Absolute, EnumHighlightShape.Arbitrary); return; } List <BlockPos> poses = new List <BlockPos>(); foreach (var node in nodes) { poses.Add(node); } sapi.World.HighlightBlocks(player, 2, poses, new List <int>() { ColorUtil.ColorFromRgba(128, 128, 128, 30) }, EnumHighlightBlocksMode.Absolute, EnumHighlightShape.Arbitrary); List <Vec3d> wps = pfs.ToWaypoints(nodes); poses = new List <BlockPos>(); foreach (var node in wps) { poses.Add(node.AsBlockPos); } sapi.World.HighlightBlocks(player, 3, poses, new List <int>() { ColorUtil.ColorFromRgba(128, 0, 0, 100) }, EnumHighlightBlocksMode.Absolute, EnumHighlightShape.Arbitrary); } }
public Stack <BlockPos> FindTree(IWorldAccessor world, BlockPos startPos, out string treeType) { Queue <Vec4i> queue = new Queue <Vec4i>(); HashSet <BlockPos> checkedPositions = new HashSet <BlockPos>(); Stack <BlockPos> foundPositions = new Stack <BlockPos>(); treeType = ""; Block block = world.BlockAccessor.GetBlock(startPos); if (block.Code == null) { return(foundPositions); } if (block.Code.Path.StartsWith("log-grown") || block.Code.Path.StartsWith("beehive-inlog-") || block.Code.Path.StartsWith("log-resin") || block.Code.Path.StartsWith("bamboo-grown-")) { treeType = block.FirstCodePart(2); queue.Enqueue(new Vec4i(startPos.X, startPos.Y, startPos.Z, 2)); foundPositions.Push(startPos); checkedPositions.Add(startPos); } string logcode = block.Code.Path.StartsWith("bamboo") ? "bamboo-grown-" + treeType : "log-grown-" + treeType; if (block is BlockFernTree) { treeType = "fern"; logcode = "ferntree-normal"; queue.Enqueue(new Vec4i(startPos.X, startPos.Y, startPos.Z, 2)); foundPositions.Push(startPos); checkedPositions.Add(startPos); } string logcode2 = "log-resin-" + treeType; string logcode3 = "log-resinharvested-" + treeType; string leavescode = block.Code.Path.StartsWith("bamboo") ? "bambooleaves-" + treeType + "-grown" : "leaves-grown-" + treeType; string leavesbranchycode = "leavesbranchy-grown-" + treeType; while (queue.Count > 0) { if (foundPositions.Count > 2000) { break; } Vec4i pos = queue.Dequeue(); for (int i = 0; i < Vec3i.DirectAndIndirectNeighbours.Length; i++) { Vec3i facing = Vec3i.DirectAndIndirectNeighbours[i]; BlockPos neibPos = new BlockPos(pos.X + facing.X, pos.Y + facing.Y, pos.Z + facing.Z); float hordist = GameMath.Sqrt(neibPos.HorDistanceSqTo(startPos.X, startPos.Z)); float vertdist = (neibPos.Y - startPos.Y); // "only breaks blocks inside an upside down square base pyramid" if (hordist - 1 >= 2 * vertdist) { continue; } if (checkedPositions.Contains(neibPos)) { continue; } block = world.BlockAccessor.GetBlock(neibPos); if (block.Code == null) { continue; } if (block.Code.Path.StartsWith(logcode) || block.Code.Path.StartsWith(logcode2) || block.Code.Path.StartsWith(logcode3)) { if (pos.W < 2) { continue; } foundPositions.Push(neibPos.Copy()); queue.Enqueue(new Vec4i(neibPos.X, neibPos.Y, neibPos.Z, 2)); } else if (block.Code.Path.StartsWith(leavesbranchycode)) { if (pos.W < 1) { continue; } foundPositions.Push(neibPos.Copy()); queue.Enqueue(new Vec4i(neibPos.X, neibPos.Y, neibPos.Z, 1)); } else if (block.Code.Path.StartsWith(leavescode)) { foundPositions.Push(neibPos.Copy()); queue.Enqueue(new Vec4i(neibPos.X, neibPos.Y, neibPos.Z, 0)); } checkedPositions.Add(neibPos); } } return(foundPositions); }