private void PlaceVoxel(VoxelHandle Vox, VoxelType Type, WorldManager World) { Vox.IsPlayerBuilt = true; Vox.Type = Type; Vox.QuickSetLiquid(LiquidType.None, 0); for (int i = 0; i < 4; i++) { World.ParticleManager.Trigger("puff", MathFunctions.RandVector3Box(Vox.GetBoundingBox().Expand(0.25f)), Color.White, 5); } // Todo: Should this be handled by the chunk manager while processing voxel update events? foreach (var phys in World.EnumerateIntersectingObjects(Vox.GetBoundingBox(), CollisionType.Dynamic).OfType <Physics>()) { phys.ApplyForce((phys.GlobalTransform.Translation - (Vox.WorldPosition + new Vector3(0.5f, 0.5f, 0.5f))) * 100, 0.01f); BoundingBox box = Vox.GetBoundingBox(); Physics.Contact contact = new Physics.Contact(); Physics.TestStaticAABBAABB(box, phys.GetBoundingBox(), ref contact); if (!contact.IsIntersecting) { continue; } Vector3 diff = contact.NEnter * contact.Penetration; Matrix m = phys.LocalTransform; m.Translation += diff; phys.LocalTransform = m; } }
public void GenerateWater(VoxelChunk chunk, float maxHeight) { int waterHeight = Math.Min((int)(VoxelConstants.ChunkSizeY * NormalizeHeight(SeaLevel + 1.0f / VoxelConstants.ChunkSizeY, maxHeight)), VoxelConstants.ChunkSizeY - 1); var iceID = VoxelLibrary.GetVoxelType("Ice"); for (var x = 0; x < VoxelConstants.ChunkSizeX; ++x) { for (var z = 0; z < VoxelConstants.ChunkSizeZ; ++z) { var biome = Overworld.GetBiomeAt(new Vector3(x, 0, z) + chunk.Origin, chunk.Manager.World.WorldScale, chunk.Manager.World.WorldOrigin); var topVoxel = VoxelHelpers.FindFirstVoxelBelow(new VoxelHandle( chunk, new LocalVoxelCoordinate(x, VoxelConstants.ChunkSizeY - 1, z))); for (var y = 0; y <= waterHeight; ++y) { var vox = new VoxelHandle(chunk, new LocalVoxelCoordinate(x, y, z)); if (vox.IsEmpty && y > topVoxel.Coordinate.Y) { if (biome.WaterSurfaceIce && y == waterHeight) { vox.RawSetType(iceID); } else { vox.QuickSetLiquid(biome.WaterIsLava ? LiquidType.Lava : LiquidType.Water, WaterManager.maxWaterLevel); } } } } } }
public void HandleLiquidInteraction(VoxelHandle Vox, LiquidType From, LiquidType To) { if ((From == LiquidType.Lava && To == LiquidType.Water) || (From == LiquidType.Water && To == LiquidType.Lava)) { Vox.Type = Library.GetVoxelType("Stone"); Vox.QuickSetLiquid(LiquidType.None, 0); } }
public void GenerateWater(VoxelChunk chunk) { int waterHeight = (int)(SeaLevel * VoxelConstants.ChunkSizeY) + 1; var iceID = VoxelLibrary.GetVoxelType("Ice"); for (var x = 0; x < VoxelConstants.ChunkSizeX; ++x) { for (var z = 0; z < VoxelConstants.ChunkSizeZ; ++z) { var biome = Overworld.GetBiomeAt(new Vector3(x, 0, z) + chunk.Origin, chunk.Manager.World.WorldScale, chunk.Manager.World.WorldOrigin); var topVoxel = VoxelHelpers.FindFirstVoxelBelow(new VoxelHandle( chunk, new LocalVoxelCoordinate(x, VoxelConstants.ChunkSizeY - 1, z))); for (var y = 0; y <= waterHeight; ++y) { var vox = new VoxelHandle(chunk, new LocalVoxelCoordinate(x, y, z)); if (vox.IsEmpty && y > topVoxel.Coordinate.Y) { if (biome.WaterSurfaceIce && y == waterHeight) { vox.RawSetType(iceID); } else { vox.QuickSetLiquid(LiquidType.Water, WaterManager.maxWaterLevel); } } } Vector2 vec = Overworld.WorldToOverworld(new Vector2(x + chunk.Origin.X, z + chunk.Origin.Z), chunk.Manager.World.WorldScale, chunk.Manager.World.WorldOrigin); if (topVoxel.Coordinate.Y < VoxelConstants.ChunkSizeY - 1 && Overworld.GetWater(Overworld.Map, vec) == Overworld.WaterType.Volcano) { var localCoord = topVoxel.Coordinate.GetLocalVoxelCoordinate(); topVoxel = new VoxelHandle(topVoxel.Chunk, new LocalVoxelCoordinate( localCoord.X, localCoord.Y + 1, localCoord.Z)); if (topVoxel.IsEmpty) { topVoxel.QuickSetLiquid(LiquidType.Lava, WaterManager.maxWaterLevel); } } } } }
public void GenerateLava(VoxelChunk chunk) { int lavaHeight = LavaLevel; for (var x = 0; x < VoxelConstants.ChunkSizeX; ++x) { for (var z = 0; z < VoxelConstants.ChunkSizeZ; ++z) { for (var y = 0; y < lavaHeight; ++y) { var voxel = new VoxelHandle(chunk, new LocalVoxelCoordinate(x, y, z)); if (voxel.IsEmpty && voxel.LiquidLevel == 0) { voxel.QuickSetLiquid(LiquidType.Lava, WaterManager.maxWaterLevel); } } } } }
private void DiscreteUpdate(ChunkManager ChunkManager, VoxelChunk chunk) { for (var y = 0; y < VoxelConstants.ChunkSizeY; ++y) { // Apply 'liquid present' tracking in voxel data to skip entire slices. if (chunk.Data.LiquidPresent[y] == 0) { continue; } var layerOrder = SlicePermutations[MathFunctions.RandInt(0, SlicePermutations.Length)]; for (var i = 0; i < layerOrder.Length; ++i) { var x = layerOrder[i] % VoxelConstants.ChunkSizeX; var z = (layerOrder[i] >> VoxelConstants.XDivShift) % VoxelConstants.ChunkSizeZ; var currentVoxel = VoxelHandle.UnsafeCreateLocalHandle(chunk, new LocalVoxelCoordinate(x, y, z)); if (currentVoxel.TypeID != 0) { continue; } if (currentVoxel.LiquidType == LiquidType.None || currentVoxel.LiquidLevel < 1) { continue; } // Evaporate. if (currentVoxel.LiquidLevel <= EvaporationLevel && MathFunctions.RandEvent(0.01f)) { if (currentVoxel.LiquidType == LiquidType.Lava) { currentVoxel.Type = Library.GetVoxelType("Stone"); } NeedsMinimapUpdate = true; currentVoxel.QuickSetLiquid(LiquidType.None, 0); continue; } var voxBelow = ChunkManager.CreateVoxelHandle(new GlobalVoxelCoordinate(currentVoxel.Coordinate.X, currentVoxel.Coordinate.Y - 1, currentVoxel.Coordinate.Z)); if (voxBelow.IsValid && voxBelow.IsEmpty) { // Fall into the voxel below. // Special case: No liquid below, just drop down. if (voxBelow.LiquidType == LiquidType.None) { NeedsMinimapUpdate = true; CreateSplash(currentVoxel.Coordinate.ToVector3(), currentVoxel.LiquidType); voxBelow.QuickSetLiquid(currentVoxel.LiquidType, currentVoxel.LiquidLevel); currentVoxel.QuickSetLiquid(LiquidType.None, 0); continue; } var belowType = voxBelow.LiquidType; var aboveType = currentVoxel.LiquidType; var spaceLeftBelow = maxWaterLevel - voxBelow.LiquidLevel; if (spaceLeftBelow >= currentVoxel.LiquidLevel) { NeedsMinimapUpdate = true; CreateSplash(currentVoxel.Coordinate.ToVector3(), aboveType); voxBelow.LiquidLevel += currentVoxel.LiquidLevel; currentVoxel.QuickSetLiquid(LiquidType.None, 0); HandleLiquidInteraction(voxBelow, aboveType, belowType); continue; } if (spaceLeftBelow > 0) { NeedsMinimapUpdate = true; CreateSplash(currentVoxel.Coordinate.ToVector3(), aboveType); currentVoxel.LiquidLevel = (byte)(currentVoxel.LiquidLevel - maxWaterLevel + voxBelow.LiquidLevel); voxBelow.LiquidLevel = maxWaterLevel; HandleLiquidInteraction(voxBelow, aboveType, belowType); continue; } } else if (voxBelow.IsValid && currentVoxel.LiquidType == LiquidType.Lava && !voxBelow.IsEmpty && voxBelow.GrassType > 0) { voxBelow.GrassType = 0; } if (currentVoxel.LiquidLevel <= 1) { continue; } // Nothing left to do but spread. RollArray(NeighborPermutations[MathFunctions.RandInt(0, NeighborPermutations.Length)], NeighborScratch, MathFunctions.RandInt(0, 4)); for (var n = 0; n < NeighborScratch.Length; ++n) { var neighborOffset = VoxelHelpers.ManhattanNeighbors2D[NeighborScratch[n]]; var neighborVoxel = new VoxelHandle(Chunks, currentVoxel.Coordinate + neighborOffset); if (neighborVoxel.IsValid && neighborVoxel.IsEmpty) { if (neighborVoxel.LiquidLevel < currentVoxel.LiquidLevel) { NeedsMinimapUpdate = true; var amountToMove = (int)(currentVoxel.LiquidLevel * GetSpreadRate(currentVoxel.LiquidType)); if (neighborVoxel.LiquidLevel + amountToMove > maxWaterLevel) { amountToMove = maxWaterLevel - neighborVoxel.LiquidLevel; } if (amountToMove > 2) { CreateSplash(neighborVoxel.Coordinate.ToVector3(), currentVoxel.LiquidType); } var newWater = currentVoxel.LiquidLevel - amountToMove; var sourceType = currentVoxel.LiquidType; var destType = neighborVoxel.LiquidType; currentVoxel.QuickSetLiquid(newWater == 0 ? LiquidType.None : sourceType, (byte)newWater); neighborVoxel.QuickSetLiquid(destType == LiquidType.None ? sourceType : destType, (byte)(neighborVoxel.LiquidLevel + amountToMove)); HandleLiquidInteraction(neighborVoxel, sourceType, destType); break; } } } } } }
/// <summary> /// Creates a flat, wooden balloon port for the balloon to land on, and Dwarves to sit on. /// </summary> /// <param name="roomDes">The player's BuildRoom designator (so that we can create a balloon port)</param> /// <param name="chunkManager">The terrain handler</param> /// <param name="x">The position of the center of the balloon port</param> /// <param name="z">The position of the center of the balloon port</param> /// <param name="size">The size of the (square) balloon port in voxels on a side</param> public Room GenerateInitialBalloonPort(RoomBuilder roomDes, ChunkManager chunkManager, float x, float z, int size) { var centerCoordinate = GlobalVoxelCoordinate.FromVector3(new Vector3(x, VoxelConstants.ChunkSizeY - 1, z)); var accumulator = 0; var count = 0; for (var offsetX = -size; offsetX <= size; ++offsetX) { for (var offsetY = -size; offsetY <= size; ++offsetY) { var topVoxel = VoxelHelpers.FindFirstVoxelBelowIncludeWater( new VoxelHandle(chunkManager.ChunkData, centerCoordinate + new GlobalVoxelOffset(offsetX, 0, offsetY))); if (topVoxel.Coordinate.Y > 0) { accumulator += topVoxel.Coordinate.Y + 1; count += 1; } } } var averageHeight = (int)Math.Round(((float)accumulator / (float)count)); if (StartUnderground) { accumulator = 0; count = 0; List <string> illegalTypes = new List <string>() { "Sand", "Dirt", "DarkDirt", "Ice" }; for (var offsetX = -size; offsetX <= size; ++offsetX) { for (var offsetY = -size; offsetY <= size; ++offsetY) { var topVoxel = VoxelHelpers.FindFirstVoxelBelow( new VoxelHandle(chunkManager.ChunkData, centerCoordinate + new GlobalVoxelOffset(offsetX, 0, offsetY))); if (topVoxel.Coordinate.Y > 0) { var vox = topVoxel; for (int dy = topVoxel.Coordinate.Y; dy > 0; dy--) { vox = new VoxelHandle(chunkManager.ChunkData, new GlobalVoxelCoordinate(topVoxel.Coordinate.X, dy, topVoxel.Coordinate.Z)); if (vox.IsValid && !vox.IsEmpty && !illegalTypes.Contains(vox.Type.Name)) { break; } } accumulator += vox.Coordinate.Y + 1; count += 1; } } } averageHeight = Math.Max((int)Math.Round(((float)accumulator / (float)count)) - 5, 0); } // Next, create the balloon port by deciding which voxels to fill. var balloonPortDesignations = new List <VoxelHandle>(); var treasuryDesignations = new List <VoxelHandle>(); for (int dx = -size; dx <= size; dx++) { for (int dz = -size; dz <= size; dz++) { Vector3 worldPos = new Vector3(centerCoordinate.X + dx, centerCoordinate.Y, centerCoordinate.Z + dz); var baseVoxel = VoxelHelpers.FindFirstVoxelBelow(new VoxelHandle( chunkManager.ChunkData, GlobalVoxelCoordinate.FromVector3(worldPos))); if (!baseVoxel.IsValid) { continue; } var h = baseVoxel.Coordinate.Y + 1; var localCoord = baseVoxel.Coordinate.GetLocalVoxelCoordinate(); for (int y = averageHeight; y < (StartUnderground ? averageHeight + 2 : h); y++) { var v = new VoxelHandle(baseVoxel.Chunk, new LocalVoxelCoordinate((int)localCoord.X, y, (int)localCoord.Z)); v.RawSetType(VoxelLibrary.GetVoxelType(0)); v.RawSetIsExplored(); v.QuickSetLiquid(LiquidType.None, 0); } if (averageHeight < h) { h = averageHeight; } bool isPosX = (dx == size && dz == 0); bool isPosZ = (dz == size & dx == 0); bool isNegX = (dx == -size && dz == 0); bool isNegZ = (dz == -size && dz == 0); bool isSide = (isPosX || isNegX || isPosZ || isNegZ); Vector3 offset = Vector3.Zero; if (isSide) { if (isPosX) { offset = Vector3.UnitX; } else if (isPosZ) { offset = Vector3.UnitZ; } else if (isNegX) { offset = -Vector3.UnitX; } else if (isNegZ) { offset = -Vector3.UnitZ; } } bool encounteredFilled = false; // Fill from the top height down to the bottom. for (int y = Math.Min(0, h - 1); y < averageHeight && y < VoxelConstants.ChunkSizeY; y++) { var v = new VoxelHandle(baseVoxel.Chunk, new LocalVoxelCoordinate((int)localCoord.X, y, (int)localCoord.Z)); if (!v.IsValid) { throw new InvalidProgramException("Voxel was invalid while creating a new game's initial zones. This should not happen."); } v.RawSetType(VoxelLibrary.GetVoxelType("Scaffold")); v.IsPlayerBuilt = true; v.QuickSetLiquid(LiquidType.None, 0); if (y == averageHeight - 1) { v.RawSetIsExplored(); if (dz >= 0) { balloonPortDesignations.Add(v); } else { treasuryDesignations.Add(v); } } if (isSide && !encounteredFilled) { var ladderPos = new Vector3(worldPos.X, y, worldPos.Z) + offset + Vector3.One * 0.5f; var ladderVox = new VoxelHandle(chunkManager.ChunkData, GlobalVoxelCoordinate.FromVector3(ladderPos)); if (ladderVox.IsValid && ladderVox.IsEmpty) { var ladder = EntityFactory.CreateEntity <Ladder>("Ladder", ladderPos); Master.Faction.OwnedObjects.Add(ladder); ladder.Tags.Add("Moveable"); ladder.Tags.Add("Deconstructable"); } else { encounteredFilled = true; } } } } } // Actually create the BuildRoom. var toBuild = RoomLibrary.CreateRoom(PlayerFaction, "Balloon Port", this); roomDes.DesignatedRooms.Add(toBuild); RoomLibrary.CompleteRoomImmediately(toBuild, balloonPortDesignations); // Also add a treasury var treasury = RoomLibrary.CreateRoom(PlayerFaction, "Treasury", this); roomDes.DesignatedRooms.Add(treasury); RoomLibrary.CompleteRoomImmediately(treasury, treasuryDesignations); return(toBuild); }
/// <summary> /// Creates a flat, wooden balloon port for the balloon to land on, and Dwarves to sit on. /// </summary> /// <param name="roomDes">The player's BuildRoom designator (so that we can create a balloon port)</param> /// <param name="chunkManager">The terrain handler</param> /// <param name="x">The position of the center of the balloon port</param> /// <param name="z">The position of the center of the balloon port</param> /// <param name="size">The size of the (square) balloon port in voxels on a side</param> public BalloonPort GenerateInitialBalloonPort(RoomBuilder roomDes, ChunkManager chunkManager, float x, float z, int size) { var centerCoordinate = GlobalVoxelCoordinate.FromVector3(new Vector3(x, VoxelConstants.ChunkSizeY - 1, z)); var accumulator = 0; var count = 0; for (var offsetX = -size; offsetX <= size; ++offsetX) { for (var offsetY = -size; offsetY <= size; ++offsetY) { var topVoxel = VoxelHelpers.FindFirstVoxelBelowIncludeWater( new VoxelHandle(chunkManager.ChunkData, centerCoordinate + new GlobalVoxelOffset(offsetX, 0, offsetY))); if (topVoxel.Coordinate.Y > 0) { accumulator += topVoxel.Coordinate.Y + 1; count += 1; } } } var averageHeight = (int)Math.Round(((float)accumulator / (float)count)); // Next, create the balloon port by deciding which voxels to fill. var balloonPortDesignations = new List <VoxelHandle>(); var treasuryDesignations = new List <VoxelHandle>(); for (int dx = -size; dx <= size; dx++) { for (int dz = -size; dz <= size; dz++) { Vector3 worldPos = new Vector3(centerCoordinate.X + dx, centerCoordinate.Y, centerCoordinate.Z + dz); var baseVoxel = VoxelHelpers.FindFirstVoxelBelow(new VoxelHandle( chunkManager.ChunkData, GlobalVoxelCoordinate.FromVector3(worldPos))); if (!baseVoxel.IsValid) { continue; } var h = baseVoxel.Coordinate.Y + 1; var localCoord = baseVoxel.Coordinate.GetLocalVoxelCoordinate(); for (int y = averageHeight; y < h; y++) { var v = new VoxelHandle(baseVoxel.Chunk, new LocalVoxelCoordinate((int)localCoord.X, y, (int)localCoord.Z)); v.RawSetType(VoxelLibrary.GetVoxelType(0)); v.QuickSetLiquid(LiquidType.None, 0); } if (averageHeight < h) { h = averageHeight; } bool isPosX = (dx == size && dz == 0); bool isPosZ = (dz == size & dx == 0); bool isNegX = (dx == -size && dz == 0); bool isNegZ = (dz == -size && dz == 0); bool isSide = (isPosX || isNegX || isPosZ || isNegZ); Vector3 offset = Vector3.Zero; if (isSide) { if (isPosX) { offset = Vector3.UnitX; } else if (isPosZ) { offset = Vector3.UnitZ; } else if (isNegX) { offset = -Vector3.UnitX; } else if (isNegZ) { offset = -Vector3.UnitZ; } } // Fill from the top height down to the bottom. for (int y = h - 1; y < averageHeight; y++) { var v = new VoxelHandle(baseVoxel.Chunk, new LocalVoxelCoordinate((int)localCoord.X, y, (int)localCoord.Z)); v.RawSetType(VoxelLibrary.GetVoxelType("Scaffold")); v.QuickSetLiquid(LiquidType.None, 0); if (y == averageHeight - 1) { if (dz >= 0) { balloonPortDesignations.Add(v); } else { treasuryDesignations.Add(v); } } if (isSide) { var ladderPos = new Vector3(worldPos.X, y, worldPos.Z) + offset + Vector3.One * 0.5f; var ladderVox = new VoxelHandle(chunkManager.ChunkData, GlobalVoxelCoordinate.FromVector3(ladderPos)); if (ladderVox.IsValid && ladderVox.IsEmpty) { var ladder = EntityFactory.CreateEntity <Ladder>("Ladder", ladderPos); Master.Faction.OwnedObjects.Add(ladder); ladder.Tags.Add("Moveable"); ladder.Tags.Add("Deconstructable"); } } } } } // Actually create the BuildRoom. BalloonPort toBuild = new BalloonPort(PlayerFaction, balloonPortDesignations, this); BuildRoomOrder buildDes = new BuildRoomOrder(toBuild, roomDes.Faction, this); buildDes.Build(true); roomDes.DesignatedRooms.Add(toBuild); // Also add a treasury Treasury treasury = new Treasury(PlayerFaction, treasuryDesignations, this); treasury.OnBuilt(); roomDes.DesignatedRooms.Add(treasury); return(toBuild); }