void ConnectIslandRecursive(int IslandId, Dictionary <int, List <FloorIslandAdjacency> > AdjacencyByIslands, HashSet <int> IslandVisited, System.Random random, FloorDoorManager DoorManager, Dictionary <int, FloorChunk> IslandToChunkMap) { if (IslandVisited.Contains(IslandId)) { return; } IslandVisited.Add(IslandId); if (!AdjacencyByIslands.ContainsKey(IslandId)) { // No adjacent islands return; } List <FloorIslandAdjacency> AdjacentNodes = AdjacencyByIslands[IslandId]; HashSet <int> AdjacentIslands = new HashSet <int>(); foreach (FloorIslandAdjacency AdjacentNode in AdjacentNodes) { AdjacentIslands.Add(AdjacentNode.A.IslandId); AdjacentIslands.Add(AdjacentNode.B.IslandId); } AdjacentIslands.Remove(IslandId); IslandNodePriorityPredicate SortPredicate = new IslandNodePriorityPredicate(IslandToChunkMap); int[] AdjacentIslandArray = new List <int>(AdjacentIslands).ToArray(); System.Array.Sort(AdjacentIslandArray, SortPredicate); foreach (int AdjacentIsland in AdjacentIslandArray) { if (IslandVisited.Contains(AdjacentIsland)) { continue; } // Find all the adjacent cells between these two islands List <FloorIslandAdjacency> EdgeNodes = new List <FloorIslandAdjacency>(); foreach (FloorIslandAdjacency AdjacentNode in AdjacentNodes) { if (AdjacentNode.A.IslandId == AdjacentIsland || AdjacentNode.B.IslandId == AdjacentIsland) { EdgeNodes.Add(AdjacentNode); } } // Connect a door in any one of the edge nodes if (EdgeNodes.Count > 0) { int Index = random.Next(0, EdgeNodes.Count); FloorIslandAdjacency DoorEdge = EdgeNodes[Index]; // Create a door here DoorManager.RegisterDoor(DoorEdge.A.Location, DoorEdge.B.Location); // Move into this room now ConnectIslandRecursive(AdjacentIsland, AdjacencyByIslands, IslandVisited, random, DoorManager, IslandToChunkMap); } } }
void BuildLayout() { ChunkDB = new FloorChunkDB(); DoorManager = new FloorDoorManager(); Visited.Clear(); random = new System.Random((int)floorPlanConfig.Seed); int NumFloors = Mathf.RoundToInt(floorPlanConfig.BuildingSize.y); for (int y = 0; y < NumFloors; y++) { // Build the hallways and the intermediate floor chunks (which will hold the rooms) List <FloorChunk> FloorChunks = new List <FloorChunk>(); { FloorChunk InitialFloor = ChunkDB.Create(); InitialFloor.Bounds.Location = new IntVector(0, y, 0); InitialFloor.Bounds.Size = MathUtils.ToIntVector(floorPlanConfig.BuildingSize); Stack <FloorChunk> Stack = new Stack <FloorChunk>(); Stack.Push(InitialFloor); while (Stack.Count > 0) { FloorChunk Top = Stack.Pop(); float Area = Top.Area(); if (Area <= 0) { continue; } if (Top.Area() <= floorPlanConfig.MinRoomChunkArea) { FloorChunks.Add(Top); } else { // Needs to be split further FloorChunk Left = ChunkDB.Create(); FloorChunk Right = ChunkDB.Create(); FloorChunk Hallway = ChunkDB.Create(); SplitChunk(Top, Left, Right, Hallway); ChunkDB.Register(Hallway); Stack.Push(Left); Stack.Push(Right); } } } // Split the floor chunks (space between the hallways) to create rooms foreach (FloorChunk Chunk in FloorChunks) { Stack <FloorChunk> Stack = new Stack <FloorChunk>(); Stack.Push(Chunk); int MinArea = floorPlanConfig.MinRoomSize * floorPlanConfig.MinRoomSize; int MaxArea = floorPlanConfig.MaxRoomSize * floorPlanConfig.MaxRoomSize; while (Stack.Count > 0) { FloorChunk Top = Stack.Pop(); bool bRequiresSplit = true; int Area = Top.Area(); int Length = Top.GetLength(); if (Length > floorPlanConfig.MaxRoomSize) { // Length is too big. force a split bRequiresSplit = true; } else if (Area <= MinArea) { // This room is too small and should not be split bRequiresSplit = false; } else if (Area <= MaxArea) { float SplitProbability = (Area - MinArea) / (MaxArea - MinArea); SplitProbability += floorPlanConfig.RoomSplitProbabilityOffset; if (Chunk.GetLength() >= Chunk.GetWidth() * 2) { SplitProbability += floorPlanConfig.RoomSplitProbabilityOffset; } bRequiresSplit = (random.Next() < SplitProbability); } if (bRequiresSplit) { FloorChunk Left = ChunkDB.Create(); FloorChunk Right = ChunkDB.Create(); SplitChunk(Top, Left, Right); Stack.Push(Left); Stack.Push(Right); } else { ChunkDB.Register(Top); } } } /* * // Get the chunks defined by the room volumes * List<FloorChunk> RoomVolumeChunks = new List<FloorChunk>(); * if (World) { * FRectangle BuildingBounds; * BuildingBounds.Location = FIntVector::ZeroValue; * BuildingBounds.Size = FMathUtils::ToIntVector(floorPlanConfig.BuildingSize); * for (TActorIterator<AFloorPlanRoomVolume> It(World); It; ++It) { * AFloorPlanRoomVolume* Volume = *It; * if (Volume.Dungeon != Dungeon) { * // This volume is not supposed to affect this dungeon * continue; * } * FVector GridSize = floorPlanConfig.GridSize; * FRectangle VolumeBounds; * Volume.GetDungeonVolumeBounds(GridSize, VolumeBounds); * int VolumeStartZ = VolumeBounds.Location.Z; * int VolumeEndZ = VolumeStartZ + VolumeBounds.Size.Z; * * if (z < VolumeStartZ || z >= VolumeEndZ) { * // Out of the volume bounds * continue; * } * * FRectangle ChunkBounds = VolumeBounds; * ChunkBounds.Location.Z = z; * ChunkBounds.Size.Z = 1; * ChunkBounds.Clip(BuildingBounds); * if (ChunkBounds.Size.Z == 0) continue; * int ChunkZ = ChunkBounds.Location.Z; * * FloorChunk RoomVolumeChunk = ChunkDB.Create(); * RoomVolumeChunk.Bounds = ChunkBounds; * RoomVolumeChunk.ChunkType = FloorChunkType.Room; * RoomVolumeChunk.bEmitGroundMarker = (ChunkZ == VolumeStartZ); * RoomVolumeChunk.bEmitCeilingMarker = (ChunkZ == VolumeEndZ - 1); * RoomVolumeChunk.bConnectDoors = Volume.bConnectDoors; * RoomVolumeChunk.WallMarker = Volume.WallMarker; * RoomVolumeChunk.GroundMarker = Volume.GroundMarker; * RoomVolumeChunk.CeilingMarker = Volume.CeilingMarker; * RoomVolumeChunk.DoorMarker = Volume.DoorMarker; * RoomVolumeChunk.CenterMarker = Volume.CenterMarker; * RoomVolumeChunk.bCreateWalls = Volume.bCreateWalls; * * // Since this chunk is defined by a volume, we can have a more detailed rasterised cell shape * GetVolumeCells(Volume, z, RoomVolumeChunk.BoundCells); * * // Give a higher priority to the volume chunk so it overrides everything in its path * RoomVolumeChunk.Priority = Volume.Priority; * * RoomVolumeChunks.Add(RoomVolumeChunk); * ChunkDB.Register(RoomVolumeChunk); * } * } */ } ChunkDB.CacheChunkPositions(); for (int y = 0; y < floorPlanConfig.BuildingSize.y; y++) { CreateDoors(y); } }
void BuildLayout() { ChunkDB = new FloorChunkDB(); DoorManager = new FloorDoorManager(); Visited.Clear(); random = new System.Random((int)floorPlanConfig.Seed); int NumFloors = Mathf.RoundToInt(floorPlanConfig.BuildingSize.y); for (int y = 0; y < NumFloors; y++) { // Build the hallways and the intermediate floor chunks (which will hold the rooms) List <FloorChunk> FloorChunks = new List <FloorChunk>(); { FloorChunk InitialFloor = ChunkDB.Create(); InitialFloor.Bounds.Location = new IntVector(0, y, 0); InitialFloor.Bounds.Size = MathUtils.ToIntVector(floorPlanConfig.BuildingSize); Stack <FloorChunk> Stack = new Stack <FloorChunk>(); Stack.Push(InitialFloor); while (Stack.Count > 0) { FloorChunk Top = Stack.Pop(); float Area = Top.Area(); if (Area <= 0) { continue; } if (Top.Area() <= floorPlanConfig.MinRoomChunkArea) { FloorChunks.Add(Top); } else { // Needs to be split further FloorChunk Left = ChunkDB.Create(); FloorChunk Right = ChunkDB.Create(); FloorChunk Hallway = ChunkDB.Create(); SplitChunk(Top, Left, Right, Hallway); ChunkDB.Register(Hallway); Stack.Push(Left); Stack.Push(Right); } } } // Split the floor chunks (space between the hallways) to create rooms foreach (FloorChunk Chunk in FloorChunks) { Stack <FloorChunk> Stack = new Stack <FloorChunk>(); Stack.Push(Chunk); int MinRoomSize = floorPlanConfig.MinRoomSize; int MaxRoomSize = floorPlanConfig.MaxRoomSize; MaxRoomSize = Mathf.Max(MinRoomSize, MaxRoomSize); int MinArea = MinRoomSize * MinRoomSize; int MaxArea = MaxRoomSize * MaxRoomSize; while (Stack.Count > 0) { FloorChunk Top = Stack.Pop(); bool bRequiresSplit = true; int Area = Top.Area(); int Length = Top.GetLength(); if (Length > MaxRoomSize) { // Length is too big. force a split bRequiresSplit = true; } else if (Area <= MinArea) { // This room is too small and should not be split bRequiresSplit = false; } else if (Area <= MaxArea) { float SplitProbability = (Area - MinArea) / (MaxArea - MinArea); SplitProbability += floorPlanConfig.RoomSplitProbabilityOffset; if (Chunk.GetLength() >= Chunk.GetWidth() * 2) { SplitProbability += floorPlanConfig.RoomSplitProbabilityOffset; } bRequiresSplit = (random.Next() < SplitProbability); } if (bRequiresSplit) { FloorChunk Left = ChunkDB.Create(); FloorChunk Right = ChunkDB.Create(); SplitChunk(Top, Left, Right); Stack.Push(Left); Stack.Push(Right); } else { ChunkDB.Register(Top); } } } // TODO: Add support for floor plan specific volumes // ... } ChunkDB.CacheChunkPositions(); for (int y = 0; y < floorPlanConfig.BuildingSize.y; y++) { CreateDoors(y); } }