public void GenerateGraphBased() { // instantiate tiles and set terrain for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { tiles[x, y] = new Tile(); //PlaceTerrain(new Position(x, y), Crea()); } } Graph dungeonGraph = GenerateGraph(); File.WriteAllText("advancedDungeon.gv", dungeonGraph.ToDot(false)); //IEnumerable<Cycle> cycles = dungeonGraph.GetCycles(true); Dictionary <Vertex, List <Cycle> > belongsToCycle = new Dictionary <Vertex, List <Cycle> >(); foreach (var vertex in dungeonGraph.Vertices) { belongsToCycle[vertex] = new List <Cycle>(); } /* * foreach (var cycle in cycles) * { * foreach (var vertex in cycle.Vertices()) * { * belongsToCycle[vertex].Add(cycle); * } * } */ HashSet <Vertex> verticesPlaced = new HashSet <Vertex>(); Dictionary <Position, Vertex> grid = new Dictionary <Position, Vertex>(); bool Placeable(Position p) { return(!grid.ContainsKey(p)); } List <Tuple <Position, int> > DjkstraMap(Position from, Position to, int threshold) { var result = new List <Tuple <Position, int> >(); var posRequired = from.GetNeighboursHexPointyTop().Where(Placeable); var posRequiredCount = posRequired.Count(); var distances = new Dictionary <Position, int>(); var todo = new Queue <Position>(); distances.Add(to, 0); todo.Enqueue(to); int requiredPositionsFound = 0; while (todo.Count > 0) { var curPos = todo.Dequeue(); foreach (var neighbour in curPos.GetNeighboursHexPointyTop().Where(Placeable)) { if (distances.ContainsKey(neighbour)) { continue; // already visited } int newDist = distances[curPos] + 1; if (newDist > threshold) { continue; } if (posRequired.Contains(neighbour)) { result.Add(Tuple.Create(neighbour, newDist)); if (++requiredPositionsFound == posRequiredCount) { return(result); } } distances.Add(neighbour, newDist); todo.Enqueue(neighbour); } } return(result); } List <Tuple <Position, int> > ValidNextPositionsForCycle( Cycle cycle, Position curPos, Vertex curVert, Vertex vertToAdd) { var cycleVerts = cycle.Vertices(); int CyclicIndex(int i) { int remainder = i % cycleVerts.Count; return(remainder < 0 ? cycleVerts.Count + remainder : remainder); } int indexOfVertToAdd = cycleVerts.IndexOf(vertToAdd); if (indexOfVertToAdd == -1) { throw new ArgumentException("Cycle doesn't contain vertToAdd"); } int nextIndex = CyclicIndex(indexOfVertToAdd + 1); int forwardStep = cycleVerts[nextIndex] == curVert ? -1 : 1; int distance = 0; int firstIndexToCheck = CyclicIndex(indexOfVertToAdd + forwardStep); for (int i = firstIndexToCheck; i != indexOfVertToAdd; i = CyclicIndex(i + forwardStep)) { distance++; var vertToCheck = cycleVerts[i]; if (!verticesPlaced.Contains(vertToCheck)) { continue; } var to = grid.First(t => t.Value == vertToCheck).Key; var distances = DjkstraMap(curPos, to, distance + 1); return(distances.Where(t => t.Item2 <= distance).ToList()); } return(curPos.GetNeighboursHexPointyTop().Where(Placeable).Select(p => Tuple.Create(p, 1)).ToList()); } IEnumerable <Position> NextPosForCycles(List <Cycle> inCycles, Position curPos, Vertex curVert, Vertex vertToAdd) { var result = new List <Position>(); foreach (var cycle in inCycles) { var nextForCycle = ValidNextPositionsForCycle(cycle, curPos, curVert, vertToAdd) .Select(t => t.Item1); result = result.Count > 0 ? result.Intersect(nextForCycle).ToList() : nextForCycle.ToList(); } return(result); } HashSet <Position> Place(Vertex vertex, Position position, bool debug = false) { Log.Message("Trying to placing vertex " + vertex); if (!Placeable(position) || verticesPlaced.Contains(vertex)) { return(null); } grid.Add(position, vertex); verticesPlaced.Add(vertex); if (debug) { Console.WriteLine(vertex + " " + position); // write layout to text file for debugging var localMin = Position.Zero; var localMax = Position.Zero; foreach (var pos in grid.Keys) { localMin.X = Math.Min(localMin.X, pos.X); localMin.Y = Math.Min(localMin.Y, pos.Y); localMax.X = Math.Max(localMax.X, pos.X); localMax.Y = Math.Max(localMax.Y, pos.Y); } File.WriteAllText("roomLayout.txt", DrawLayout(grid, localMin, localMax)); } var positionsPlacedFromCur = new HashSet <Position>() { position }; foreach (var neighbour in vertex.Edges.Select(e => e.GetOtherVertex(vertex)).Where(other => !verticesPlaced.Contains(other))) { IEnumerable <Position> validPositions; if (belongsToCycle[neighbour].Count > 0) { validPositions = NextPosForCycles(belongsToCycle[neighbour], position, vertex, neighbour); } else { validPositions = position.GetNeighboursHexPointyTop().Where(Placeable); } bool success = false; foreach (var neighbourPos in validPositions) { var newPositions = Place(neighbour, neighbourPos, debug); // recursive call if (newPositions == null) { continue; } success = true; positionsPlacedFromCur.UnionWith(newPositions); break; } if (!success) { // no neighbour position was found therefore we abort and // clean up every pos placed from here (including the current position) foreach (var pos in positionsPlacedFromCur) { verticesPlaced.Remove(grid[pos]); grid.Remove(pos); } return(null); } } return(positionsPlacedFromCur); } int vertexCount = dungeonGraph.Vertices.Count; var realPositions = PlaceForceBased(dungeonGraph); var minDist = float.MaxValue; var maxDist = float.MinValue; for (int i = 0; i < vertexCount; i++) { var vertexA = dungeonGraph.Vertices[i]; foreach (var vertexB in vertexA.Neighbours()) { var delta = realPositions[vertexB] - realPositions[vertexA]; float dist = delta.Length(); if (dist > maxDist) { maxDist = dist; } if (dist < minDist) { minDist = dist; } } } var roomTiles = new TileTemplate[vertexCount][, ]; float maxRoomCircumference = 0f; for (int i = 0; i < vertexCount; i++) { var vertex = dungeonGraph.Vertices[i]; RoomTemplate roomTemplate = GameData.Instance.RoomTemplates["empty"]; var templateName = vertex.Data; if (GameData.Instance.RoomTemplates.ContainsKey(templateName)) { roomTemplate = GameData.Instance.RoomTemplates[templateName]; } else { Log.Warning($"Unknown room template '{templateName}' for room '{vertex}'"); } roomTiles[i] = GenerateRoom(roomTemplate); var roomWidth = roomTiles[i].GetLength(0); var roomHeight = roomTiles[i].GetLength(1); var roomCircumference = MathF.Ceiling(MathF.Sqrt(roomWidth * roomWidth + roomHeight * roomHeight)); if (roomCircumference > maxRoomCircumference) { maxRoomCircumference = roomCircumference; } } var minRealX = realPositions.Select(pair => pair.Value.X).Min(); var minRealY = realPositions.Select(pair => pair.Value.Y).Min(); float scale = (maxRoomCircumference + 1) / minDist; var roomAnchorPositions = realPositions.Select ( pair => new Position ( (int)MathF.Round((pair.Value.X - minRealX) * scale) + 1, // +1 for left border (int)MathF.Round((pair.Value.Y - minRealY) * scale) + 1 // +1 for top border ) ).ToList(); var xs = roomAnchorPositions.Select(pos => pos.X); var ys = roomAnchorPositions.Select(pos => pos.Y); width = xs.Max() + (int)MathF.Round(maxRoomCircumference) + 1; // +1 for right border height = ys.Max() + (int)MathF.Round(maxRoomCircumference) + 1; // +1 for bottom border tiles = new Tile[width, height]; var asciiMap = new char[width, height]; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { asciiMap[x, y] = '#'; tiles[x, y] = new Tile(); PlaceTerrain(new Position(x, y), CreateWall()); } } CarveOutRoomSpace(roomAnchorPositions, roomTiles, asciiMap); ConnectRooms(dungeonGraph, roomAnchorPositions, roomTiles); InitRoomTiles(roomAnchorPositions, roomTiles); var sb = new StringBuilder(); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { sb.Append(asciiMap[x, y]); } sb.AppendLine(); } File.WriteAllText("asciiMap.txt", sb.ToString()); //CreateRoomsFromGrid(roomLayout); //CreateStructures(); //SpawnEnemies(); //SpawnItems(); }
public void CreateRoomsFromGrid(Vertex[,] roomLayout) { // instantiate tiles and set terrain for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { PlaceTerrain(new Position(x, y), CreateWall()); } } bool playerInitialized = false; int roomGridWidth = roomLayout.GetLength(0); int roomGridHeight = roomLayout.GetLength(1); TileTemplate[, ][,] rooms = new TileTemplate[roomGridWidth, roomGridHeight][, ]; var rowY = new int[roomGridHeight]; rowY[0] = 1; int verMargin = 5; int maxRoomWidth = 0; // Generate rooms for (int roomGridY = 0; roomGridY < roomGridHeight; roomGridY++) { int rowHeight = 0; for (int roomGridX = 0; roomGridX < roomGridWidth; roomGridX++) { var vert = roomLayout[roomGridX, roomGridY] as RoomVertex; if (vert == null) { continue; } string templateName = vert.roomTemplateName; RoomTemplate roomTemplate = GameData.Instance.RoomTemplates["empty"]; if (GameData.Instance.RoomTemplates.ContainsKey(templateName)) { roomTemplate = GameData.Instance.RoomTemplates[templateName]; } else { Log.Error($"Unknown room template '{templateName}' for room '{vert.ToString()}'"); } TileTemplate[,] room = GenerateRoom(roomTemplate); rooms[roomGridX, roomGridY] = room; int roomWidth = room.GetLength(0); int roomHeight = room.GetLength(1); maxRoomWidth = Math.Max(maxRoomWidth, roomWidth); rowHeight = Math.Max(rowHeight, roomHeight); } // set Y of next row if (roomGridY < (roomGridHeight - 1)) { rowY[roomGridY + 1] = rowY[roomGridY] + rowHeight + verMargin; } } int horMargin = 5; var anchors = new Position[roomGridWidth, roomGridHeight]; // Carve out room shapes for (int roomGridX = 0; roomGridX < roomGridWidth; roomGridX++) { for (int roomGridY = 0; roomGridY < roomGridHeight; roomGridY++) { var room = rooms[roomGridX, roomGridY]; if (room == null) { continue; } var anchor = new Position(1 + maxRoomWidth / 2 * roomGridY + (maxRoomWidth + horMargin) * roomGridX, rowY[roomGridY]); anchors[roomGridX, roomGridY] = anchor; int roomWidth = room.GetLength(0); int roomHeight = room.GetLength(1); Console.WriteLine("Room at: " + anchor); // carve room shape for (int localY = 0; localY < roomHeight; localY++) { for (int localX = 0; localX < roomWidth; localX++) { Position tilePos = anchor + new Position(localX, localY); RemoveTerrain(tilePos); } } } } // Connect rooms for (int roomGridX = 0; roomGridX < roomGridWidth; roomGridX++) { for (int roomGridY = 0; roomGridY < roomGridHeight; roomGridY++) { var room = rooms[roomGridX, roomGridY]; if (room == null) { continue; } var curRoomCenter = anchors[roomGridX, roomGridY] + new Position(room.GetLength(0) / 2, room.GetLength(1) / 2); var roomGridPos = new Position(roomGridX, roomGridY); var curVertex = roomLayout[roomGridX, roomGridY]; foreach (var neighbourRoomPos in roomGridPos.GetNeighboursHexPointyTop().Take(3)) { if (neighbourRoomPos.X < 0 || neighbourRoomPos.X >= roomGridWidth || neighbourRoomPos.Y < 0 || neighbourRoomPos.Y >= roomGridHeight) { continue; } var neighbour = rooms[neighbourRoomPos.X, neighbourRoomPos.Y]; if (neighbour == null) { continue; } var neighbourVertex = roomLayout[neighbourRoomPos.X, neighbourRoomPos.Y]; if (!curVertex.Edges.Any(e => e.AttachedTo(neighbourVertex))) { continue; } var neighbourCenter = anchors[neighbourRoomPos.X, neighbourRoomPos.Y] + new Position(neighbour.GetLength(0) / 2, neighbour.GetLength(1) / 2); var hallway = GetLineCardinal(curRoomCenter, neighbourCenter); foreach (var pos in hallway) { var tile = GetTile(pos); if (tile.Terrain > 0) { RemoveTerrain(pos); } } } } } File.WriteAllText("asciiLayout.txt", ASCII()); // Populate rooms for (int roomGridX = 0; roomGridX < roomGridWidth; roomGridX++) { for (int roomGridY = 0; roomGridY < roomGridHeight; roomGridY++) { var room = rooms[roomGridX, roomGridY]; if (room == null) { continue; } var anchor = anchors[roomGridX, roomGridY]; int roomWidth = room.GetLength(0); int roomHeight = room.GetLength(1); // place room in world for (int localY = 0; localY < roomHeight; localY++) { for (int localX = 0; localX < roomWidth; localX++) { Position tilePos = anchor + new Position(localX, localY); var template = room[localX, localY]; InitTileFromTemplate(tilePos, template); } } if (!playerInitialized) { InitPlayer(anchor + Position.One); playerInitialized = true; } } } }
public TileTemplate[,] GenerateRoom(RoomTemplate template, bool random = true, int layoutIndex = 0) { char[,] layout; if (!GameData.Instance.Tilesets.ContainsKey(template.tileset)) { Log.Error("Unkown tileset: " + template.tileset); template.tileset = "default"; } if (random) { layout = Util.PickRandomElement(template.Layouts); } else if (layoutIndex >= 0 && layoutIndex < template.Layouts.Count) { layout = template.Layouts[layoutIndex]; } else { Log.Error("Invalid layout index: " + layoutIndex); return(null); } int width = layout.GetLength(0); int height = layout.GetLength(1); var tiles = new TileTemplate[width, height]; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { char symbol = layout[x, y]; Position tilePos = new Position(x, y); if (IsOutOfBounds(tilePos)) { Log.Warning("Trying to place tile out of bounds! " + tilePos); } if (template.CustomTiles.ContainsKey(symbol)) { tiles[x, y] = template.CustomTiles[symbol]; } else { GameData.Instance.Tilesets.TryGetValue(template.tileset, out var tileset); if (tileset == null) { tiles[x, y] = new TileTemplate(); } else if (tileset.ContainsKey(symbol)) { tiles[x, y] = tileset[symbol]; } else { Log.Warning("Unkown symbol from Layout: " + symbol); tiles[x, y] = new TileTemplate(); } } } } return(tiles); }