public bool DiscreteUpdate(VoxelChunk chunk) { Vector3 gridCoord = new Vector3(0, 0, 0); bool updateOccurred = false; List <int> updateList = new List <int>(); WaterCell cellBelow = new WaterCell(); int maxSize = chunk.SizeX * chunk.SizeY * chunk.SizeZ; for (int i = 0; i < maxSize; i++) { WaterCell cell = chunk.Data.Water[i]; // Don't check empty cells or cells we've already modified. if (cell.WaterLevel < 1 || chunk.Data.Types[i] != 0) { continue; } updateList.Add(i); } if (updateList.Count == 0) { return(false); } Voxel voxBelow = chunk.MakeVoxel(0, 0, 0); List <int> indices = Datastructures.RandomIndices(updateList.Count); // Loop through each cell. foreach (int t in indices) { int idx = updateList[indices[t]]; // Don't check empty cells or cells we've already modified. if (chunk.Data.Water[idx].Type == LiquidType.None || chunk.Data.Types[idx] != 0) { continue; } gridCoord = chunk.Data.CoordsAt(idx); int x = (int)gridCoord.X; int y = (int)gridCoord.Y; int z = (int)gridCoord.Z; Vector3 worldPos = gridCoord + chunk.Origin; if (chunk.Data.Water[idx].WaterLevel <= EvaporationLevel && MathFunctions.RandEvent(0.01f)) { if (chunk.Data.Water[idx].WaterLevel > 1) { chunk.Data.Water[idx].WaterLevel--; } else { chunk.Data.Water[idx].WaterLevel = 0; if (chunk.Data.Water[idx].Type == LiquidType.Lava) { chunk.Data.Types[idx] = (byte)VoxelLibrary.GetVoxelType("Stone").ID; chunk.Data.Health[idx] = (byte)VoxelLibrary.GetVoxelType("Stone").StartingHealth; chunk.ShouldRebuild = true; chunk.ShouldRecalculateLighting = true; } chunk.Data.Water[idx].Type = LiquidType.None; } updateOccurred = true; } bool shouldFall = false; // Now check the cell immediately below this one. // There are two cases, either we are at the bottom of the chunk, // in which case we must find the water from the chunk manager. // Otherwise, we just get the cell immediately beneath us. if (y > 0) { voxBelow.GridPosition = new Vector3(x, y - 1, z); if (voxBelow.IsEmpty) { cellBelow = voxBelow.Water; shouldFall = true; } } /* * else * { * if(chunk.Manager.ChunkData.DoesWaterCellExist(worldPos)) * { * Voxel voxelsBelow = chunk.Manager.ChunkData.GetVoxel(chunk, worldPos + new Vector3(0, -1, 0)); * * if(voxelsBelow != null && voxelsBelow.IsEmpty) * { * cellBelow = chunk.Manager.ChunkData.GetWaterCellAtLocation(worldPos + new Vector3(0, -1, 0)); * shouldFall = true; * cellBelow.IsFalling = true; * } * } * } */ // Cases where the fluid can fall down. if (shouldFall) { // If the cell immediately below us is empty, // swap the contents and move on. if (cellBelow.WaterLevel < 1) { CreateSplash(worldPos, chunk.Data.Water[idx].Type); cellBelow.WaterLevel = chunk.Data.Water[idx].WaterLevel; if (cellBelow.Type == LiquidType.None) { cellBelow.Type = chunk.Data.Water[idx].Type; } chunk.Data.Water[idx].WaterLevel = 0; chunk.Data.Water[idx].Type = LiquidType.None; voxBelow.Water = cellBelow; CreateTransfer(worldPos, chunk.Data.Water[idx], cellBelow, cellBelow.WaterLevel); updateOccurred = true; continue; } // Otherwise, fill as much of the space as we can. else { byte spaceLeft = (byte)(8 - cellBelow.WaterLevel); // Special case where we can flow completely into the next cell. if (spaceLeft >= chunk.Data.Water[idx].WaterLevel) { byte transfer = chunk.Data.Water[idx].WaterLevel; cellBelow.WaterLevel += transfer; if (cellBelow.Type == LiquidType.None) { cellBelow.Type = chunk.Data.Water[idx].Type; } chunk.Data.Water[idx].WaterLevel = 0; chunk.Data.Water[idx].Type = LiquidType.None; CreateTransfer(worldPos - Vector3.UnitY, chunk.Data.Water[idx], cellBelow, transfer); voxBelow.Water = cellBelow; updateOccurred = true; continue; } // Otherwise, only flow a little bit, and spread later. else { chunk.Data.Water[idx].WaterLevel -= spaceLeft; cellBelow.WaterLevel += spaceLeft; if (cellBelow.Type == LiquidType.None) { cellBelow.Type = chunk.Data.Water[idx].Type; } CreateTransfer(worldPos - Vector3.UnitY, chunk.Data.Water[idx], cellBelow, spaceLeft); voxBelow.Water = cellBelow; } } } // Now the only fluid left can spread. // We spread to the manhattan neighbors //Array.Sort(m_spreadNeighbors, (a, b) => CompareFlowVectors(a, b, chunk.Data.Water[idx].FluidFlow)); m_spreadNeighbors.Shuffle(); Voxel neighbor = new Voxel(); foreach (Vector3 spread in m_spreadNeighbors) { bool success = chunk.Manager.ChunkData.GetVoxel(chunk, worldPos + spread, ref neighbor); if (!success) { continue; } if (!neighbor.IsEmpty) { continue; } WaterCell neighborWater = neighbor.Water; if (neighborWater.WaterLevel >= chunk.Data.Water[idx].WaterLevel) { continue; } byte amountToMove = (byte)(Math.Min(8.0f - (float)neighborWater.WaterLevel, chunk.Data.Water[idx].WaterLevel) * GetSpreadRate(chunk.Data.Water[idx].Type)); if (amountToMove == 0) { continue; } if (neighborWater.WaterLevel < 2) { updateOccurred = true; } CreateTransfer(worldPos + spread, chunk.Data.Water[idx], neighborWater, amountToMove); chunk.Data.Water[idx].WaterLevel -= amountToMove; neighborWater.WaterLevel += amountToMove; if (neighborWater.Type == LiquidType.None) { neighborWater.Type = chunk.Data.Water[idx].Type; } neighbor.Water = neighborWater; if (chunk.Data.Water[idx].WaterLevel >= 1) { continue; } chunk.Data.Water[idx].WaterLevel = 0; chunk.Data.Water[idx].Type = LiquidType.None; break; } } return(updateOccurred); }