public void ExportMapToTextFile(Map mapToExport, string filename) { try { //Open logfile Directory.CreateDirectory("maps"); using (StreamWriter writer = new StreamWriter("maps/" + filename, false)) { for (int j = 0; j < mapToExport.height; j++) { StringBuilder mapLine = new StringBuilder(); for (int i = 0; i < mapToExport.width; i++) { //Defaults char screenChar = StringEquivalent.TerrainChars[mapToExport.mapSquares[i, j].Terrain]; mapLine.Append(screenChar); } writer.WriteLine(mapLine); } } } catch (Exception e) { LogFile.Log.LogEntryDebug("Failed to write file " + e.Message, LogDebugLevel.High); } }
/** Produce a new map with randomized terrain. Its pathing and sight may need to be recalculated */ public static Map RandomizeTerrainInMap(Map mapToRandomize, Dictionary<MapTerrain, List<MapTerrain>> randomizeMapping) { var outputMap = mapToRandomize.Clone(); for (int i = 0; i < mapToRandomize.width; i++) { for (int j = 0; j < mapToRandomize.height; j++) { var thisTerrain = mapToRandomize.mapSquares[i, j].Terrain; if (randomizeMapping.ContainsKey(thisTerrain)) { outputMap.mapSquares[i, j].Terrain = randomizeMapping[thisTerrain].RandomElement(); } } } return outputMap; }
public Map GenerateMap(int extraConnections) { LogFile.Log.LogEntry(String.Format("Generating BSP dungeon")); baseMap = new Map(width, height); //Make a BSP tree for the rooms rootNode = new MapNode(0, 0, width, height); rootNode.Split(); //Draw a room in each BSP leaf rootNode.DrawRoomAtLeaf(baseMap); //debug //Screen.Instance.DrawMapDebug(baseMap); //Draw connecting corridors rootNode.DrawCorridorConnectingChildren(baseMap); //Add any extra connecting corridors as specified for (int i = 0; i < extraConnections; i++) { rootNode.AddRandomConnection(baseMap); } //Add doors where single corridors terminate into rooms AddDoors(); //Turn corridors into normal squares and surround with walls CorridorsIntoRooms(); //Set which squares are light blocking //Now done during creation //SetLightBlocking(baseMap); //Set the PC start location in a random room baseMap.PCStartLocation = rootNode.RandomRoomPoint(); return baseMap; }
/// <summary> /// Make serializable map from map /// </summary> /// <param name="original"></param> public SerializableMap(Map original) { this.PCStartLocation = original.PCStartLocation; this.width = original.width; this.height = original.height; this.LightLevel = original.LightLevel; this.GuaranteedConnected = original.GuaranteedConnected; //Need to make mapSquares 1 dimensional mapSquares = new MapSquare[width * height]; for (int j = 0; j < height; j++) { for (int i = 0; i < width; i++) { mapSquares[j * width + i] = original.mapSquares[i, j]; } } }
public Map GenerateMap(int extraConnections) { LogFile.Log.LogEntry(String.Format("Generating BSP dungeon")); do { baseMap = new Map(width, height); connectivityGraph = new ConnectivityMap(); //BSP is always connected baseMap.GuaranteedConnected = true; //Make a BSP tree for the rooms rootNode = new MapNode(this, 0, 0, width, height); rootNode.Split(); //Draw a room in each BSP leaf rootNode.DrawRoomAtLeaf(baseMap); //debug //Screen.Instance.DrawMapDebug(baseMap); //Draw connecting corridors rootNode.DrawCorridorConnectingChildren(baseMap); //Add any extra connecting corridors as specified for (int i = 0; i < extraConnections; i++) { rootNode.AddRandomConnection(baseMap); } //Add doors where single corridors terminate into rooms AddDoorsGraph(); //Turn corridors into normal squares and surround with walls CorridorsIntoRooms(); //Work out where the staircases will be //We just want 2 places that aren't too close to each other. The map is guaranteed connected double RequiredStairDistance = (width * 0.5); double stairDistance; do { upStaircase = RandomWalkablePoint(); downStaircase = RandomWalkablePoint(); stairDistance = Math.Sqrt(Math.Pow(upStaircase.x - downStaircase.x, 2) + Math.Pow(upStaircase.y - downStaircase.y, 2)); } while (stairDistance < RequiredStairDistance); //Set which squares are light blocking //Now done during creation //SetLightBlocking(baseMap); //Set the PC start location in a random room baseMap.PCStartLocation = AddEntryRoomForPlayer(); } while (baseMap.PCStartLocation == null); //Fake a start room PointInRoom randomRoom = RandomPointInRoom(); //baseMap.PCStartLocation = randomRoom.GetPointInRoomOnly(); baseMap.PCStartRoomId = randomRoom.RoomId; //Build the map model for the graph, based on the PC's true starting position (useful for locking doors) graphModel = new MapModel(connectivityGraph, baseMap.PCStartRoomId); //Save out the graph GraphvizExport.OutputUndirectedGraph(connectivityGraph.RoomConnectionGraph, "bsptree-base"); //Save out a copy of the graph with no cycles GraphvizExport.OutputUndirectedGraph(graphModel.GraphNoCycles.mapNoCycles, "bsptree-nocycles"); return baseMap.Clone(); }
/// <summary> /// Adds the complete map to the dungeon. Throw exception on failure, but shouldn't fail /// </summary> /// <returns></returns> public void AddMapToDungeon() { if (!fileLoaded) { LogFile.Log.LogEntry("MapGeneratorFromASCIIFile::AddMapToDungeon: No map loaded"); throw new ApplicationException("No map loaded"); } baseMap = new Map(width, height); int row = 0; //Sort out the terrain first //Features and special areas are empty foreach (string mapRow in storedMapRows) { for (int i = 0; i < width; i++) { char mapChar = mapRow[i]; MapTerrain thisTerrain; MapSquare thisSquare = baseMap.mapSquares[i, row]; //if this square is terrain if (terrainMapping.ContainsKey(mapChar)) { thisTerrain = terrainMapping[mapChar]; } else { //if this square is a feature or special thisTerrain = MapTerrain.Empty; } //Set terrain and features thisSquare.Terrain = thisTerrain; //This should be done in the map gen functions - right now dungeon does a bit of it too switch (thisTerrain) { case MapTerrain.Wall: case MapTerrain.Void: thisSquare.Walkable = false; thisSquare.BlocksLight = true; break; case MapTerrain.Empty: thisSquare.Walkable = true; thisSquare.BlocksLight = false; break; } } row++; } //Add the (terrain complete) map to the dungeon before adding features and specials int levelNo = Game.Dungeon.AddMap(baseMap); //Sort out features row = 0; foreach (string mapRow in storedMapRows) { for (int i = 0; i < width; i++) { char mapChar = mapRow[i]; if (featureChars.Contains(mapChar)) { bool featureAddSuccess = false; switch (mapChar) { case '>': featureAddSuccess = Game.Dungeon.AddFeature(new Features.StaircaseDown(), levelNo, new Point(i, row)); break; case '<': featureAddSuccess = Game.Dungeon.AddFeature(new Features.StaircaseUp(), levelNo, new Point(i, row)); break; } if (!featureAddSuccess) { LogFile.Log.LogEntry("MapGeneratorFromASCIIFile::AddMapToDungeon: Failed to add terrain feature"); throw new ApplicationException("Failed to add feature"); } } } row++; } //Sort out special characters foreach (string mapRow in storedMapRows) { for (int i = 0; i < width; i++) { char mapChar = mapRow[i]; if (specialChars.Contains(mapChar)) { switch (mapChar) { //PC start location is meaningless for everything except the first level case 'x': baseMap.PCStartLocation = new Point(i, row); break; } } } row++; } }
public void DrawRoomAtLeaf(Map baseMap) { //Check we are a leaf if(childLeft != null) { childLeft.DrawRoomAtLeaf(baseMap); } if(childRight != null) { childRight.DrawRoomAtLeaf(baseMap); } //Only actually draw the room if both childLeft and childRight are null if(childLeft == null && childRight == null) { DrawRoom(baseMap); } }
/// <summary> /// This function builds connections between rooms and is responsible for building the connectivity graph /// </summary> /// <param name="baseMap"></param> private void DrawConnectingCorriderBetweenChildren(Map baseMap) { Random rand = MapGeneratorBSP.rand; //Route of the corridor List<Point> corridorRoute; //Calculate an L-shaped corridor between two rooms //One room is in a leaf node BSP child of this room //The other may any room in the BSP tree other child of this room if (split == SplitType.Horizontal) { //We are guaranteed that rays from the split into the left and right child will not hit an obstruction //(from any y) //However, routing a L shaped corridor between the rays is only guaranteed to be possible down the edge of the BSP tile //So after finding start and end Y from projected ray we just guess an L and check that it works before doing //Cast rays from the split into the left child //Look for a corridor or accessable room int leftY; int leftX = -1; do { //Random y coord leftY = y + rand.Next(height); //Don't go quite to the left extent (x) - the 2 look ahead will fix this for (int i = x + actualSplit - 1; i > x; i--) { //Look ahead 2 MapTerrain terrainNext = baseMap.mapSquares[i, leftY].Terrain; MapTerrain terrainNext2 = baseMap.mapSquares[i - 1, leftY].Terrain; //A corridor is OK if (terrainNext == MapTerrain.Corridor) { leftX = i; break; } //A wall with empty or corridor behind it is OK else if (terrainNext == MapTerrain.Wall && terrainNext2 != MapTerrain.Wall) { leftX = i; break; } //A corridor 'seen coming' we can short cut to else if (terrainNext2 == MapTerrain.Corridor) { leftX = i - 1; break; } //A 1-thick wall is OK else if (terrainNext == MapTerrain.Wall && terrainNext2 == MapTerrain.Wall) { //No good break; } } } while (leftX == -1); //Cast ray into right child int rightY; int rightX = -1; do { //Random y coord rightY = y + rand.Next(height); //Don't go quite to the right extent (x) - the 2 look ahead will fix this for (int i = x + actualSplit; i < x + width - 1; i++) { //Look ahead 2 MapTerrain terrainNext = baseMap.mapSquares[i, rightY].Terrain; MapTerrain terrainNext2 = baseMap.mapSquares[i + 1, rightY].Terrain; //Any corridor is OK if (terrainNext == MapTerrain.Corridor) { rightX = i; break; } //A wall with empty or corridor behind it is OK else if (terrainNext == MapTerrain.Wall && terrainNext2 != MapTerrain.Wall) { rightX = i; break; } //A corridor 'seen coming' we can short cut too else if (terrainNext2 == MapTerrain.Corridor) { rightX = i + 1; break; } //A 1-thick wall is OK else if (terrainNext == MapTerrain.Wall && terrainNext2 == MapTerrain.Wall) { //No good break; } } } while (rightX == -1); //Screen.Instance.DrawMapDebugHighlight(baseMap, leftX, leftY, rightX, rightY); //Now route a L corridor from (leftX, leftY) to (rightX, rightY) //The L bend can occur within X: leftSafeX -> rightSafeX corridorRoute = new List<Point>(rightX - leftX + Math.Abs(rightY - leftY)); bool notValidPath = false; //Keep trying until we get a valid path (at least one is guaranteed) do { corridorRoute.Clear(); notValidPath = false; //L bend set randonly int lBendX = leftX + 1 + rand.Next(rightX - leftX); for (int i = leftX; i <= lBendX; i++) { corridorRoute.Add(new Point(i, leftY)); } int startY; int endY; if (leftY > rightY) { //down startY = rightY; endY = leftY; } else { startY = leftY; endY = rightY; } for (int j = startY + 1; j < endY; j++) { corridorRoute.Add(new Point(lBendX, j)); } for (int i = lBendX; i <= rightX; i++) { corridorRoute.Add(new Point(i, rightY)); } //Check this path for validity //Look for walls but ignore the first and last squares for (int i = 1; i < corridorRoute.Count - 1; i++) { if (baseMap.mapSquares[corridorRoute[i].x, corridorRoute[i].y].Terrain == MapTerrain.Wall) { notValidPath = true; break; } } } while (notValidPath); } else { //Vertical //We are guaranteed that rays from the split into the left and right child will not hit an obstruction //(from any y) //However, routing a L shaped corridor between the rays is only guaranteed to be possible down the edge of the BSP tile //So after finding start and end Y from projected ray we just guess an L and check that it works before doing //Cast rays from the split into the left child //Look for a corridor or accessable room int leftX; int leftY = -1; do { //Random x coord leftX = x + rand.Next(width); //Don't go quite to the left extent (y) - the 2 look ahead will fix this for (int i = y + actualSplit - 1; i > y; i--) { //Look ahead 2 MapTerrain terrainNext = baseMap.mapSquares[leftX, i].Terrain; MapTerrain terrainNext2 = baseMap.mapSquares[leftX, i - 1].Terrain; //Any corridor is OK if (terrainNext == MapTerrain.Corridor) { leftY = i; break; } //A wall with empty or corridor behind it is OK else if (terrainNext == MapTerrain.Wall && terrainNext2 != MapTerrain.Wall) { leftY = i; break; } //A corridor 'seen coming' we can short cut too else if (terrainNext2 == MapTerrain.Corridor) { leftY = i - 1; break; } //A 1-thick wall is OK else if (terrainNext == MapTerrain.Wall && terrainNext2 == MapTerrain.Wall) { //No good break; } } } while (leftY == -1); //Cast ray into right child int rightX; int rightY = -1; do { //Random y coord rightX = x + rand.Next(width); //Don't go quite to the right extent (x) - the 2 look ahead will fix this for (int i = y + actualSplit; i < y + height - 1; i++) { //Look ahead 2 MapTerrain terrainNext = baseMap.mapSquares[rightX, i].Terrain; MapTerrain terrainNext2 = baseMap.mapSquares[rightX, i + 1].Terrain; //Any corridor is OK if (terrainNext == MapTerrain.Corridor) { rightY = i; break; } //A wall with empty or corridor behind it is OK else if (terrainNext == MapTerrain.Wall && terrainNext2 != MapTerrain.Wall) { rightY = i; break; } //A corridor 'seen coming' we can short cut too else if (terrainNext2 == MapTerrain.Corridor) { rightY = i + 1; break; } //A 1-thick wall is OK else if (terrainNext == MapTerrain.Wall && terrainNext2 == MapTerrain.Wall) { //No good break; } } } while (rightY == -1); //Now route a L corridor from (leftX, leftY) to (rightX, rightY) //The L bend can occur within X: leftSafeX -> rightSafeX corridorRoute = new List<Point>(Math.Abs(rightX - leftX) + rightY - leftY); bool notValidPath = false; //Keep trying until we get a valid path (at least one is guaranteed) do { corridorRoute.Clear(); notValidPath = false; //L bend set randonly int lBendY = leftY + 1 + rand.Next(rightY - leftY); for (int i = leftY; i <= lBendY; i++) { corridorRoute.Add(new Point(leftX, i)); } int startX; int endX; if (leftX > rightX) { //down startX = rightX; endX = leftX; } else { startX = leftX; endX = rightX; } for (int j = startX + 1; j < endX; j++) { corridorRoute.Add(new Point(j, lBendY)); } for (int i = lBendY; i <= rightY; i++) { corridorRoute.Add(new Point(rightX, i)); } //Check this path for validity //Look for walls but ignore the first and last squares for (int i = 1; i < corridorRoute.Count - 1; i++) { if (baseMap.mapSquares[corridorRoute[i].x, corridorRoute[i].y].Terrain == MapTerrain.Wall) { notValidPath = true; break; } } } while (notValidPath); } //Add corridor to connection graph //Now we'll actually going to make this corridor a new 'room' in its own right. However, parts of it may be shared with other rooms. //That's OK, it will have a unidirectional connection to any corridors that cross it //Create a room id for the corridor int corridorId = parentGenerator.GetNextRoomIdAndIncrease(); //Associate the corridor (not terminals squares in the rooms) path with the new ID List<Point> corridorOnlyPath = corridorRoute.GetRange(1, corridorRoute.Count - 2); parentGenerator.AssociatePathWithCorridorId(corridorId, corridorOnlyPath); //Because this BSP node's children may have children of their own (we actually only have 1 leaf node child), we don't actually know the id of the room we're going to hit, //we need to look it up from the bitmap //We have already set the bitmap to the current corridor id //Add the left and right rooms. If any extent terminated in a corridor, then don't add a door (can be got around diagonally!) if (baseMap.mapSquares[corridorRoute[0].x, corridorRoute[0].y].Terrain != MapTerrain.Corridor) parentGenerator.AddDoorConnection(corridorRoute[0].x, corridorRoute[0].y, corridorRoute[1].x, corridorRoute[1].y, MapGeneratorBSP.ConnectionInfo.DoorPos.Left); //Build the doored connection to the dest room, unless we terminated in a corridor, in which case make an undoored connection automatically int lastPoint = corridorRoute.Count - 1; int secondLastPoint = corridorRoute.Count - 2; if (baseMap.mapSquares[corridorRoute[lastPoint].x, corridorRoute[lastPoint].y].Terrain != MapTerrain.Corridor) parentGenerator.AddDoorConnection(corridorRoute[lastPoint].x, corridorRoute[lastPoint].y, corridorRoute[secondLastPoint].x, corridorRoute[secondLastPoint].y, MapGeneratorBSP.ConnectionInfo.DoorPos.Left); //Find a list of all the rooms that this corridor connects to (e.g. it may run over another corridor) //Include the start and end rooms, in case we haven't set doored connections above HashSet<int> connectedTo = new HashSet<int>(); foreach (Point p in corridorRoute) { connectedTo.Add(baseMap.roomIdMap[p.x, p.y]); } //Add all non-doored connections to other things we have run into foreach (var roomId in connectedTo) { //The base map contains 0s for any unallocated areas in the map - these aren't real rooms, so we don't add if (roomId > 0 && roomId != corridorId) parentGenerator.AddNonDoorConnector(corridorId, roomId); } //Commit the corridor path to the terrain bitmap foreach (Point sq in corridorRoute) { baseMap.mapSquares[sq.x, sq.y].Terrain = MapTerrain.Corridor; baseMap.mapSquares[sq.x, sq.y].SetOpen(); } //If we wanted a general way of checking for connectivity, we could look at all the squares surrounding the corridor //check if any were walkable and then add these connections //Not necessary at the moment, since the algorithm guarantees that the only connections will be between the start and terminus of the path //(all in-between walls cause recalculation) }
public void DrawCorridorConnectingChildren(Map baseMap) { //Children should do their own drawing first //However, if we don't have children, return to our parent if (childLeft != null) childLeft.DrawCorridorConnectingChildren(baseMap); if (childRight != null) childRight.DrawCorridorConnectingChildren(baseMap); //If we only have 1 child we can't connect them, but our parent will connect to them if (childLeft == null || childRight == null) return; //Draw a connecting corridor between our two children DrawConnectingCorriderBetweenChildren(baseMap); }
public void DrawRoom(Map baseMap) { //Associate this area with a new room id Id = parentGenerator.GetNextRoomIdAndIncrease(); parentGenerator.AssociateAreaWithId(Id, x, y, width, height); Random rand = MapGeneratorBSP.rand; //Width and height are reduced by 1 from max filling to ensure there is always a free column / row for an L-shaped corridor roomWidth = (int)(width * minFill + rand.Next((int) ( (width * maxFill) - (width * minFill)) )); roomHeight = (int)(height * minFill + rand.Next((int) ( (height * maxFill) - (height * minFill) ) )); if(width <= minRoomWidth) { throw new ApplicationException("BSP too small for room"); } if(height <= minRoomHeight) { throw new ApplicationException("BSP too small for room"); } if(roomWidth < minRoomWidth) roomWidth = minRoomWidth; if (roomHeight < minRoomHeight) roomHeight = minRoomHeight; /*if (roomWidth < MapGeneratorBSP.minimumRoomSize) roomWidth = MapGeneratorBSP.minimumRoomSize; if (roomHeight < MapGeneratorBSP.minimumRoomSize) roomHeight = MapGeneratorBSP.minimumRoomSize;*/ //At least 1 column free on left roomX = x + 1 + rand.Next(width - roomWidth); //At least 1 column free on right (because roomWidth is at least 1 smaller than width) int rx = roomX + roomWidth - 1; //Likewise for top and bottom roomY = y + 1 + rand.Next(height - roomHeight); int by = roomY + roomHeight - 1; //Walls for (int i = roomX; i <= rx; i++) { //Top row baseMap.mapSquares[i, roomY].Terrain = MapTerrain.Wall; //Bottom row baseMap.mapSquares[i, by].Terrain = MapTerrain.Wall; } for (int i = roomY; i <= by; i++) { //Left row baseMap.mapSquares[roomX, i].Terrain = MapTerrain.Wall; //Right row baseMap.mapSquares[rx, i].Terrain = MapTerrain.Wall; } //Inside //Set as empty for (int i = roomX + 1; i < rx; i++) { for (int j = roomY + 1; j < by; j++) { baseMap.mapSquares[i, j].Terrain = MapTerrain.Empty; baseMap.mapSquares[i, j].SetOpen(); } } }
/// <summary> /// Add map and return its level index /// </summary> /// <param name="mapToAdd"></param> /// <returns></returns> public int AddMap(Map mapToAdd) { levels.Add(mapToAdd); //Add TCOD version levelTCODMaps.Add(new TCODFov(mapToAdd.width, mapToAdd.height)); return levels.Count - 1; }
public Map GenerateMap() { LogFile.Log.LogEntry(String.Format("Generating Rogue-like dungeon {0}x{0}", subSquareDim)); corridorRedos = 0; baseMap = new Map(width, height); //Divide the grid up into subsquares int subsquareWidth = width / subSquareDim; int subsquareHeight = height / subSquareDim; int totalRooms = subSquareDim * subSquareDim; //Setup room array mapRooms = new RogueRoom[totalRooms]; for(int i=0;i<totalRooms;i++) { mapRooms[i] = new RogueRoom(); } //Give rooms random sizes //-2 is to ensure there are same columns at the extremes of the squares //-1 would probably be OK too //The maxroom size possible is 1 below filling the allowed area completely. int roomSizeWidthVariation = subsquareWidth - minimumRoomSize - 1; int roomSizeHeightVariation = subsquareHeight - minimumRoomSize - 1; //int roomSizeWidthVariation = 2; //int roomSizeHeightVariation = 2; foreach (RogueRoom room in mapRooms) { room.roomWidth = minimumRoomSize + rand.Next(roomSizeWidthVariation); room.roomHeight = minimumRoomSize + rand.Next(roomSizeHeightVariation); } //Calculate room centres for (int i = 0; i < subSquareDim; i++) { for (int j = 0; j < subSquareDim; j++) { //Available space //x: SubsquareWidth - 2 (safe columns) - roomSizeWidth //+1 used in Rand so we can generate the maximum int squareCentreX = subsquareWidth / 2 + subsquareWidth * i; int squareCentreY = subsquareHeight / 2 + subsquareHeight * j; int roomWidth = MapRoom(i, j).roomWidth; int roomHeight = MapRoom(i, j).roomHeight; int availableX = subsquareWidth - 2 - roomWidth; int availableY = subsquareHeight - 2 - roomHeight; int offsetX = rand.Next(availableX + 1); int offsetY = rand.Next(availableY + 1); offsetX -= availableX / 2; offsetY -= availableY / 2; MapRoom(i, j).roomX = squareCentreX + offsetX; MapRoom(i, j).roomY = squareCentreY + offsetY; } } //Connect rooms //First stage is to snake through the rooms until we reach a 'snake' corner //(we could snake more than once for big maps but unnecessary for small maps) int connectedRooms = 0; //Current processing room int startRoom = rand.Next(totalRooms); //Link each room to an unconnected neighbour until we run out of unconnected neighbours bool unconnectedNeighbourFound = false; do { //Say this room is connected (it is at least connected to the room we came from) mapRooms[startRoom].connected = true; connectedRooms++; //Connect to a random unconnected neighbour and then make that the current processing room List<int> unconnectedNeighbours = FindConnectedNeighbours(startRoom, false); if (unconnectedNeighbours.Count > 0) { int neighbourRand = rand.Next(unconnectedNeighbours.Count); int neighbourToConnectTo = unconnectedNeighbours[neighbourRand]; mapRooms[startRoom].connectedTo.Add(neighbourToConnectTo); startRoom = neighbourToConnectTo; unconnectedNeighbourFound = true; } else { unconnectedNeighbourFound = false; } } while (unconnectedNeighbourFound); /* //The snake above can be commented out if you like and replaced with these two lines mapRooms[startRoom].connected = true; connectedRooms++; */ //Cycle through all unconnected rooms //Connect each to a connected neighbour startRoom = 0; int fixingIterations = 0; //profiling while (connectedRooms < totalRooms) { fixingIterations++; //Find next unconnected room while (mapRooms[startRoom].connected == true) { startRoom++; //Allow cycle to reset back to 0 if (startRoom == totalRooms) { startRoom = 0; } } //Find connected neighbours List<int> connectedNeighbours = FindConnectedNeighbours(startRoom, true); //Connect to a random connected neighbour if (connectedNeighbours.Count > 0) { int neighbourRand = rand.Next(connectedNeighbours.Count); int neighbourToConnectTo = connectedNeighbours[neighbourRand]; mapRooms[startRoom].connectedTo.Add(neighbourToConnectTo); //Mark this room as connected mapRooms[startRoom].connected = true; connectedRooms++; } //If we don't find a connected neighbour this time, we'll return here on the cycle startRoom++; //Allow cycle to reset back to 0 if (startRoom == totalRooms) { startRoom = 0; } } //Add some more random connections int totalRandomConnections = rand.Next(subSquareDim); int noRandomConnections = 0; while (noRandomConnections < totalRandomConnections) { //Pick a room at random int randomRoom = rand.Next(totalRooms); //Pick a random neighbour to connect to List<int> allNeighbours = FindAllNeighbours(randomRoom); if (allNeighbours.Count > 0) { int neighbourRand = rand.Next(allNeighbours.Count); int neighbourToConnectTo = allNeighbours[neighbourRand]; //If we are not already connected, add a connection if (!mapRooms[randomRoom].connectedTo.Contains(neighbourToConnectTo) && !mapRooms[neighbourToConnectTo].connectedTo.Contains(randomRoom)) { mapRooms[randomRoom].connectedTo.Add(neighbourToConnectTo); noRandomConnections++; } } } LogFile.Log.LogEntry(String.Format("Generation complete, fixing iterations {0}, corridor redos {1}", fixingIterations, corridorRedos)); //Draw rooms on map foreach (RogueRoom room in mapRooms) { DrawRoomOnMap(room); } //Draw corridors for(int i=0;i<totalRooms;i++) { foreach (int dest in mapRooms[i].connectedTo) { DrawMapCorridor(i, dest); } } //Place the PC in room 1 baseMap.PCStartLocation = new Point(mapRooms[0].roomX, mapRooms[0].roomY); //Make a square /* for (int i = 10; i < 20; i++) { for (int j = 10; j < 20; j++) { baseMap.mapSquares[i,j] = Map.MapTerrain.Wall; } } for (int i = 11; i < 19; i++) { for (int j = 11; j < 19; j++) { baseMap.mapSquares[i,j] = Map.MapTerrain.Empty; } } */ //Stick the PC in the middle //baseMap.PCStartLocation = new Point(15, 15); return baseMap; }
/// <summary> /// Create a playable map by merging the terrain of the templates in z-order and mapping to real terrain types /// </summary> /// <returns></returns> public Map MergeTemplatesIntoMap(Dictionary<RoomTemplateTerrain, MapTerrain> terrainMapping) { var mergedArea = mapCache.GetMergedArea(); var mergedIdArea = idCache.GetMergedArea(); Map masterMap = new Map(mergedArea.GetLength(0), mergedArea.GetLength(1)); for (int i = 0; i < mergedArea.GetLength(0); i++) { for (int j = 0; j < mergedArea.GetLength(1); j++) { masterMap.mapSquares[i, j].Terrain = terrainMapping[mergedArea[i, j]]; masterMap.roomIdMap[i, j] = mergedIdArea[i, j]; } } return masterMap; }
private void DrawMap(Map map) { //Get screen handle RootConsole rootConsole = RootConsole.GetInstance(); for (int i = 0; i < map.width; i++) { for (int j = 0; j < map.height; j++) { int screenX = mapTopLeft.x + i; int screenY = mapTopLeft.y + j; char screenChar = StringEquivalent.TerrainChars[map.mapSquares[i, j].Terrain]; Color drawColor = inFOVTerrainColor; if (map.mapSquares[i, j].InPlayerFOV) { //In FOV rootConsole.ForegroundColor = inFOVTerrainColor; } else if (map.mapSquares[i, j].InMonsterFOV) { //Monster can see it rootConsole.ForegroundColor = inMonsterFOVTerrainColor; } else if (map.mapSquares[i, j].SeenByPlayer) { //Not in FOV but seen rootConsole.ForegroundColor = seenNotInFOVTerrainColor; } else { //Never in FOV rootConsole.ForegroundColor = neverSeenFOVTerrainColor; } rootConsole.PutChar(screenX, screenY, screenChar); } } }
/// <summary> /// Get proper map back /// </summary> /// <returns></returns> public Map MapFromSerializableMap() { Map newMap = new Map(width, height); newMap.PCStartLocation = this.PCStartLocation; newMap.LightLevel = this.LightLevel; newMap.GuaranteedConnected = this.GuaranteedConnected; //Make mapSquares for (int j = 0; j < height; j++) { for (int i = 0; i < width; i++) { newMap.mapSquares[i, j] = this.mapSquares[j * width + i]; } } return newMap; }
/// <summary> /// Returns the points in a triangular target (i.e. shotgun weapon) from origin to target. /// Only returns points within FOV. Moral: If you can see it, you can shoot it. /// fovAngle = spread of target /// </summary> /// <param name="location"></param> /// <param name="size"></param> /// <returns></returns> public List<Point> GetPointsForTriangularTargetInFOV(Point origin, Point target, Map mapLevel, int range, double fovAngle) { if (origin == target) return new List<Point>(); List<Point> triangularPoints = new List<Point>(); double angle = DirectionUtil.AngleFromOriginToTarget(origin, target); for (int i = origin.x - range; i < origin.x + range; i++) { for (int j = origin.y - range; j < origin.y + range; j++) { if(new Point(i,j) == origin) continue; //Check for creature's FOV //If OK, check to see if it falls within a TriangularFOV (blast radius) if (i >= 0 && i < mapLevel.width && j >= 0 && j < mapLevel.height) { if (CheckTileFOV(i, j) && CreatureFOV.TriangularFOV(origin, angle, range, i, j, fovAngle)) { triangularPoints.Add(new Point(i, j)); } } } } return triangularPoints; }
public void DrawRoom(Map baseMap) { Random rand = MapGeneratorBSP.rand; //Width and height are reduced by 1 from max filling to ensure there is always a free column / row for an L-shaped corridor roomWidth = (int)(width * minFill + rand.Next((int) ( (width * maxFill) - (width * minFill)) )); roomHeight = (int)(height * minFill + rand.Next((int) ( (height * maxFill) - (height * minFill) ) )); if(width <= minRoomWidth) { throw new ApplicationException("BSP too small for room"); } if(height <= minRoomHeight) { throw new ApplicationException("BSP too small for room"); } if(roomWidth < minRoomWidth) roomWidth = minRoomWidth; if (roomHeight < minRoomHeight) roomHeight = minRoomHeight; /*if (roomWidth < MapGeneratorBSP.minimumRoomSize) roomWidth = MapGeneratorBSP.minimumRoomSize; if (roomHeight < MapGeneratorBSP.minimumRoomSize) roomHeight = MapGeneratorBSP.minimumRoomSize;*/ roomX = x + 1 + rand.Next(width - roomWidth); int rx = roomX + roomWidth - 1; roomY = y + 1 + rand.Next(height - roomHeight); int by = roomY + roomHeight - 1; //Walls for (int i = roomX; i <= rx; i++) { //Top row baseMap.mapSquares[i, roomY].Terrain = MapTerrain.Wall; //Bottom row baseMap.mapSquares[i, by].Terrain = MapTerrain.Wall; } for (int i = roomY; i <= by; i++) { //Left row baseMap.mapSquares[roomX, i].Terrain = MapTerrain.Wall; //Right row baseMap.mapSquares[rx, i].Terrain = MapTerrain.Wall; } //Inside //Set as empty for (int i = roomX + 1; i < rx; i++) { for (int j = roomY + 1; j < by; j++) { baseMap.mapSquares[i, j].Terrain = MapTerrain.Empty; baseMap.mapSquares[i, j].SetOpen(); } } }
public Map GenerateMap() { LogFile.Log.LogEntry("Making cave map"); if (Width < 1 || Height < 1) { LogFile.Log.LogEntry("Can't make 0 dimension map"); throw new ApplicationException("Can't make with 0 dimension"); } DiggingChance = 20; MineChance = 15; PercOpenRequired = 0.4; RequiredStairDistance = 40; int maxStairConnectAttempts = 10; //Since we can't gaurantee connectivity, we will run the map gen many times until we get a map that meets our criteria bool badMap = true; int mapsMade = 0; do { baseMap = new Map(Width, Height); mapsMade++; //Fill map with walls for (int i = 0; i < Width; i++) { for (int j = 0; j < Height; j++) { SetSquareClosed(i, j); } } //Start digging from a random point int noDiggingPoints = 4 + Game.Random.Next(4); for (int i = 0; i < noDiggingPoints; i++) { int x = Game.Random.Next(Width); int y = Game.Random.Next(Height); //Don't dig right to the edge if (x == 0) x = 1; if (x == Width - 1) x = Width - 2; if (y == 0) y = 1; if (y == Height - 1) y = Height - 2; Dig(x, y); } //Check if we are too small, and add more digs while (CalculatePercentageOpen() < PercOpenRequired) { int x = Game.Random.Next(Width); int y = Game.Random.Next(Height); //Don't dig right to the edge if (x == 0) x = 1; if (x == Width - 1) x = Width - 2; if (y == 0) y = 1; if (y == Height - 1) y = Height - 2; Dig(x, y); } //Find places for the stairs //We will attempt this several times before we give up and redo the whole map int attempts = 0; do { double stairDistance; do { upStaircase = RandomPoint(); downStaircase = RandomPoint(); stairDistance = Math.Sqrt(Math.Pow(upStaircase.x - downStaircase.x, 2) + Math.Pow(upStaircase.y - downStaircase.y, 2)); } while (stairDistance < RequiredStairDistance); //If the stairs aren't connected, try placing them in different random spots, by relooping if (ArePointsConnected(upStaircase, downStaircase)) { badMap = false; break; } attempts++; } while (attempts < maxStairConnectAttempts); } while (badMap); //If requested do a final pass and fill in with some interesting terrain //Fill map with walls if (DoFillInPass) { for (int i = 0; i < Width; i++) { for (int j = 0; j < Height; j++) { if (openTerrainType.Contains(baseMap.mapSquares[i, j].Terrain)) { if (Game.Random.Next(100) < FillInChance) baseMap.mapSquares[i, j].Terrain = FillInTerrain; } } } } //Caves are not guaranteed connected baseMap.GuaranteedConnected = false; //Set the player start location to that of the up staircase (only used on the first level) pcStartLocation = upStaircase; LogFile.Log.LogEntry("Total maps made: " + mapsMade.ToString()); return baseMap; }
private void DrawConnectingCorriderBetweenChildren(Map baseMap) { Random rand = MapGeneratorBSP.rand; if (split == SplitType.Horizontal) { //We are guaranteed that rays from the split into the left and right child will not hit an obstruction //(from any y) //However, routing a L shaped corridor between the rays is only guaranteed to be possible down the edge of the BSP tile //So after finding start and end Y from projected ray we just guess an L and check that it works before doing //Cast rays from the split into the left child //Look for a corridor or accessable room int leftY; int leftX = -1; do { //Random y coord leftY = y + rand.Next(height); //Don't go quite to the left extent (x) - the 2 look ahead will fix this for (int i = x + actualSplit - 1; i > x; i--) { //Look ahead 2 MapTerrain terrainNext = baseMap.mapSquares[i, leftY].Terrain; MapTerrain terrainNext2 = baseMap.mapSquares[i - 1, leftY].Terrain; //A corridor is OK if (terrainNext == MapTerrain.Corridor) { leftX = i; break; } //A wall with empty or corridor behind it is OK else if (terrainNext == MapTerrain.Wall && terrainNext2 != MapTerrain.Wall) { leftX = i; break; } //A corridor 'seen coming' we can short cut to else if (terrainNext2 == MapTerrain.Corridor) { leftX = i - 1; break; } //A 1-thick wall is OK else if (terrainNext == MapTerrain.Wall && terrainNext2 == MapTerrain.Wall) { //No good break; } } } while (leftX == -1); //Cast ray into right child int rightY; int rightX = -1; do { //Random y coord rightY = y + rand.Next(height); //Don't go quite to the right extent (x) - the 2 look ahead will fix this for (int i = x + actualSplit; i < x + width - 1; i++) { //Look ahead 2 MapTerrain terrainNext = baseMap.mapSquares[i, rightY].Terrain; MapTerrain terrainNext2 = baseMap.mapSquares[i + 1, rightY].Terrain; //Any corridor is OK if (terrainNext == MapTerrain.Corridor) { rightX = i; break; } //A wall with empty or corridor behind it is OK else if (terrainNext == MapTerrain.Wall && terrainNext2 != MapTerrain.Wall) { rightX = i; break; } //A corridor 'seen coming' we can short cut too else if (terrainNext2 == MapTerrain.Corridor) { rightX = i + 1; break; } //A 1-thick wall is OK else if (terrainNext == MapTerrain.Wall && terrainNext2 == MapTerrain.Wall) { //No good break; } } } while (rightX == -1); //Screen.Instance.DrawMapDebugHighlight(baseMap, leftX, leftY, rightX, rightY); //Now route a L corridor from (leftX, leftY) to (rightX, rightY) //The L bend can occur within X: leftSafeX -> rightSafeX List<Point> corridorRoute = new List<Point>(rightX - leftX + Math.Abs(rightY - leftY)); bool notValidPath = false; //Keep trying until we get a valid path (at least one is guaranteed) do { corridorRoute.Clear(); notValidPath = false; //L bend set randonly int lBendX = leftX + 1 + rand.Next(rightX - leftX); for (int i = leftX; i <= lBendX; i++) { corridorRoute.Add(new Point(i, leftY)); } int startY; int endY; if (leftY > rightY) { //down startY = rightY; endY = leftY; } else { startY = leftY; endY = rightY; } for (int j = startY + 1; j < endY; j++) { corridorRoute.Add(new Point(lBendX, j)); } for (int i = lBendX; i <= rightX; i++) { corridorRoute.Add(new Point(i, rightY)); } //Check this path for validity //Look for walls but ignore the first and last squares for (int i = 1; i < corridorRoute.Count - 1; i++) { if (baseMap.mapSquares[corridorRoute[i].x, corridorRoute[i].y].Terrain == MapTerrain.Wall) { notValidPath = true; break; } } } while (notValidPath); //We now have a valid path so draw it foreach (Point sq in corridorRoute) { baseMap.mapSquares[sq.x, sq.y].Terrain = MapTerrain.Corridor; baseMap.mapSquares[sq.x, sq.y].SetOpen(); } } else { //Vertical //We are guaranteed that rays from the split into the left and right child will not hit an obstruction //(from any y) //However, routing a L shaped corridor between the rays is only guaranteed to be possible down the edge of the BSP tile //So after finding start and end Y from projected ray we just guess an L and check that it works before doing //Cast rays from the split into the left child //Look for a corridor or accessable room int leftX; int leftY = -1; do { //Random x coord leftX = x + rand.Next(width); //Don't go quite to the left extent (y) - the 2 look ahead will fix this for (int i = y + actualSplit - 1; i > y; i--) { //Look ahead 2 MapTerrain terrainNext = baseMap.mapSquares[leftX, i].Terrain; MapTerrain terrainNext2 = baseMap.mapSquares[leftX, i - 1].Terrain; //Any corridor is OK if (terrainNext == MapTerrain.Corridor) { leftY = i; break; } //A wall with empty or corridor behind it is OK else if (terrainNext == MapTerrain.Wall && terrainNext2 != MapTerrain.Wall) { leftY = i; break; } //A corridor 'seen coming' we can short cut too else if (terrainNext2 == MapTerrain.Corridor) { leftY = i - 1; break; } //A 1-thick wall is OK else if (terrainNext == MapTerrain.Wall && terrainNext2 == MapTerrain.Wall) { //No good break; } } } while (leftY == -1); //Cast ray into right child int rightX; int rightY = -1; do { //Random y coord rightX = x + rand.Next(width); //Don't go quite to the right extent (x) - the 2 look ahead will fix this for (int i = y + actualSplit; i < y + height - 1; i++) { //Look ahead 2 MapTerrain terrainNext = baseMap.mapSquares[rightX, i].Terrain; MapTerrain terrainNext2 = baseMap.mapSquares[rightX, i + 1].Terrain; //Any corridor is OK if (terrainNext == MapTerrain.Corridor) { rightY = i; break; } //A wall with empty or corridor behind it is OK else if (terrainNext == MapTerrain.Wall && terrainNext2 != MapTerrain.Wall) { rightY = i; break; } //A corridor 'seen coming' we can short cut too else if (terrainNext2 == MapTerrain.Corridor) { rightY = i + 1; break; } //A 1-thick wall is OK else if (terrainNext == MapTerrain.Wall && terrainNext2 == MapTerrain.Wall) { //No good break; } } } while (rightY == -1); //Now route a L corridor from (leftX, leftY) to (rightX, rightY) //The L bend can occur within X: leftSafeX -> rightSafeX List<Point> corridorRoute = new List<Point>(Math.Abs(rightX - leftX) + rightY - leftY); bool notValidPath = false; //Keep trying until we get a valid path (at least one is guaranteed) do { corridorRoute.Clear(); notValidPath = false; //L bend set randonly int lBendY = leftY + 1 + rand.Next(rightY - leftY); for (int i = leftY; i <= lBendY; i++) { corridorRoute.Add(new Point(leftX, i)); } int startX; int endX; if (leftX > rightX) { //down startX = rightX; endX = leftX; } else { startX = leftX; endX = rightX; } for (int j = startX + 1; j < endX; j++) { corridorRoute.Add(new Point(j, lBendY)); } for (int i = lBendY; i <= rightY; i++) { corridorRoute.Add(new Point(rightX, i)); } //Check this path for validity //Look for walls but ignore the first and last squares for (int i = 1; i < corridorRoute.Count - 1; i++) { if (baseMap.mapSquares[corridorRoute[i].x, corridorRoute[i].y].Terrain == MapTerrain.Wall) { notValidPath = true; break; } } } while (notValidPath); //We now have a valid path so draw it foreach (Point sq in corridorRoute) { baseMap.mapSquares[sq.x, sq.y].Terrain = MapTerrain.Corridor; baseMap.mapSquares[sq.x, sq.y].SetOpen(); } } }
//Draw a map only (useful for debugging) public void DrawMapDebugHighlight(Map map, int x1, int y1, int x2, int y2) { //Get screen handle RootConsole rootConsole = RootConsole.GetInstance(); //Clear screen rootConsole.Clear(); for (int i = 0; i < map.width; i++) { for (int j = 0; j < map.height; j++) { int screenX = mapTopLeft.x + i; int screenY = mapTopLeft.y + j; char screenChar = StringEquivalent.TerrainChars[map.mapSquares[i, j].Terrain]; Color drawColor = inFOVTerrainColor; if (i == x1 && j == y1) { drawColor = ColorPresets.Red; } if (i == x2 && j == y2) { drawColor = ColorPresets.Red; } rootConsole.ForegroundColor = drawColor; /* if (!map.mapSquares[i, j].BlocksLight) { //In FOV rootConsole.ForegroundColor = inFOVTerrainColor; } else { //Not in FOV but seen rootConsole.ForegroundColor = seenNotInFOVTerrainColor; }*/ rootConsole.PutChar(screenX, screenY, screenChar); } } //Flush the console rootConsole.Flush(); }
private void SetLightBlocking(Map baseMap) { foreach (MapSquare square in baseMap.mapSquares) { if (square.Terrain == MapTerrain.Empty) { square.BlocksLight = false; } else { square.BlocksLight = true; } } }
public void AddExtraConnectionBetweenChildren(Map baseMap) { //Wander down the tree to a leaf, keeping track of the number of nodes with 2 children (that could be possible connections) //When we come back down the tree, try to get one of the 2 children nodes to draw a connection if (childLeft != null && childRight != null) { treeDepth++; //Pick one node at random if(MapGeneratorBSP.rand.Next(2) < 1) { childLeft.AddExtraConnectionBetweenChildren(baseMap); } else { childRight.AddExtraConnectionBetweenChildren(baseMap); } //We reach here after we have hit the leaf //If we haven't made a connection yet there's a chance we will connect our children if (newConnectionMade == false && MapGeneratorBSP.rand.Next(10) < 5) { newConnectionMade = true; //Draw a connecting corridor between our two children DrawConnectingCorriderBetweenChildren(baseMap); } } else if(childLeft != null) { childLeft.AddExtraConnectionBetweenChildren(baseMap); } else if(childRight != null) { childRight.AddExtraConnectionBetweenChildren(baseMap); } }
/// <summary> /// Add an extra connecting corridor somewhere on the map. Returns whether a new connection was drawn (may fail due to randomness, in which case retry) /// </summary> /// <param name="baseMap"></param> public bool AddRandomConnection(Map baseMap) { treeDepth = 0; newConnectionMade = false; AddExtraConnectionBetweenChildren(baseMap); return newConnectionMade; }
/// <summary> /// Adds the complete map to the dungeon. Throw exception on failure, but shouldn't fail /// </summary> /// <returns></returns> public void AddMapToDungeon() { if (!fileLoaded) { LogFile.Log.LogEntry("MapGeneratorFromASCIIFile::AddMapToDungeon: No map loaded"); throw new ApplicationException("No map loaded"); } baseMap = new Map(width, height); baseMap.LightLevel = 0; int row = 0; //Sort out the terrain first //Features and special areas are empty foreach (string mapRow in storedMapRows) { for (int i = 0; i < width; i++) { char mapChar = mapRow[i]; MapTerrain thisTerrain; MapSquare thisSquare = baseMap.mapSquares[i, row]; //if this square is terrain if (terrainMapping.ContainsKey(mapChar)) { thisTerrain = terrainMapping[mapChar]; } else { //if this square is a feature or special //hack if (mapFilename.Contains("last")) { thisTerrain = MapTerrain.Grass; } else thisTerrain = MapTerrain.Empty; } //Set terrain and features thisSquare.Terrain = thisTerrain; //This should be done in the map gen functions - right now dungeon does a bit of it too switch (thisTerrain) { case MapTerrain.Wall: case MapTerrain.Void: thisSquare.Walkable = false; thisSquare.BlocksLight = true; break; case MapTerrain.Empty: thisSquare.Walkable = true; thisSquare.BlocksLight = false; break; case MapTerrain.Mountains: thisSquare.Walkable = false; thisSquare.BlocksLight = true; break; case MapTerrain.Trees: thisSquare.Walkable = true; thisSquare.BlocksLight = false; break; case MapTerrain.River: thisSquare.Walkable = false; thisSquare.BlocksLight = false; break; case MapTerrain.Road: thisSquare.Walkable = true; thisSquare.BlocksLight = false; break; case MapTerrain.Grass: thisSquare.Walkable = true; thisSquare.BlocksLight = false; break; case MapTerrain.Gravestone: thisSquare.Walkable = true; thisSquare.BlocksLight = false; break; case MapTerrain.Forest: thisSquare.Walkable = false; thisSquare.BlocksLight = true; break; } } row++; } //Add the (terrain complete) map to the dungeon before adding features and specials int levelNo = Game.Dungeon.AddMap(baseMap); //Sort out features row = 0; foreach (string mapRow in storedMapRows) { for (int i = 0; i < width; i++) { char mapChar = mapRow[i]; if (featureChars.Contains(mapChar)) { bool featureAddSuccess = false; switch (mapChar) { case '~': //featureAddSuccess = Game.Dungeon.AddFeature(new Features.StaircaseEntry(Game.Dungeon.DungeonInfo Dungeon1StartLevel), levelNo, new Point(i, row)); break; case '/': //featureAddSuccess = Game.Dungeon.AddFeature(new Features.StaircaseEntry(Game.Dungeon.Dungeon2StartLevel), levelNo, new Point(i, row)); break; case '<': //featureAddSuccess = Game.Dungeon.AddFeature(new Features.StaircaseUp(), levelNo, new Point(i, row)); break; } if (!featureAddSuccess) { LogFile.Log.LogEntry("MapGeneratorFromASCIIFile::AddMapToDungeon: Failed to add terrain feature"); throw new ApplicationException("Failed to add feature"); } } } row++; } //Sort out special characters row = 0; foreach (string mapRow in storedMapRows) { for (int i = 0; i < width; i++) { char mapChar = mapRow[i]; if (specialChars.Contains(mapChar)) { bool addingSuccess = true; switch (mapChar) { //PC start location is meaningless for everything except the first level case 'x': baseMap.PCStartLocation = new Point(i, row); break; case '1': Game.Dungeon.AddTrigger(levelNo, new Point(i, row), new Triggers.BackToSchool()); break; case '2': addingSuccess = Game.Dungeon.AddFeature(new Features.StaircaseEntry(0, Game.Dungeon.DungeonInfo.GetDungeonStartLevel(0)), levelNo, new Point(i, row)); break; case '3': addingSuccess = Game.Dungeon.AddFeature(new Features.StaircaseEntry(1, Game.Dungeon.DungeonInfo.GetDungeonStartLevel(1)), levelNo, new Point(i, row)); break; case '4': addingSuccess = Game.Dungeon.AddFeature(new Features.StaircaseEntry(2, Game.Dungeon.DungeonInfo.GetDungeonStartLevel(2)), levelNo, new Point(i, row)); break; case '5': addingSuccess = Game.Dungeon.AddFeature(new Features.StaircaseEntry(3, Game.Dungeon.DungeonInfo.GetDungeonStartLevel(3)), levelNo, new Point(i, row)); break; case '6': addingSuccess = Game.Dungeon.AddFeature(new Features.StaircaseEntry(4, Game.Dungeon.DungeonInfo.GetDungeonStartLevel(4)), levelNo, new Point(i, row)); break; case '7': addingSuccess = Game.Dungeon.AddFeature(new Features.StaircaseEntry(5, Game.Dungeon.DungeonInfo.GetDungeonStartLevel(5)), levelNo, new Point(i, row)); break; case '8': addingSuccess = Game.Dungeon.AddFeature(new Features.StaircaseEntry(6, Game.Dungeon.DungeonInfo.GetDungeonStartLevel(6)), levelNo, new Point(i, row)); break; case 'A': Game.Dungeon.AddTrigger(levelNo, new Point(i, row), new Triggers.TerrainFlipTrigger(MapTerrain.Road, "river")); baseMap.mapSquares[i, row].Terrain = MapTerrain.River; break; case 'B': Game.Dungeon.AddTrigger(levelNo, new Point(i, row), new Triggers.TerrainFlipTrigger(MapTerrain.Trees, "forest")); baseMap.mapSquares[i, row].Terrain = MapTerrain.Forest; break; case 'C': Game.Dungeon.AddTrigger(levelNo, new Point(i, row), new Triggers.TerrainFlipTrigger(MapTerrain.Empty, "grave")); baseMap.mapSquares[i, row].Terrain = MapTerrain.Mountains; break; case 'D': Game.Dungeon.AddTrigger(levelNo, new Point(i, row), new Triggers.TerrainFlipTrigger(MapTerrain.Empty, "final")); baseMap.mapSquares[i, row].Terrain = MapTerrain.Mountains; break; //case '%': // Game.Dungeon.AddDecorationFeature(new Features.Corpse(), levelNo, new Point(i, row)); //break; case 'Y': //Game.Dungeon.AddMonster(new Creatures.Lich(), levelNo, new Point(i, row)); break; case 'G': Game.Dungeon.AddMonster(new Creatures.Friend(), levelNo, new Point(i, row)); break; } if (!addingSuccess) { LogFile.Log.LogEntry("MapGeneratorFromASCIIFile::AddMapToDungeon: Failed to add special terrain feature"); throw new ApplicationException("Failed to add feature"); } } } row++; } }
public Map GenerateMap(int extraConnections) { LogFile.Log.LogEntry(String.Format("Generating BSPCave dungeon")); baseMap = new Map(width, height); //BSP is always connected baseMap.GuaranteedConnected = true; //Make a BSP tree for the rooms rootNode = new MapNode(this, 0, 0, width, height); rootNode.Split(); //Draw a room in each BSP leaf rootNode.DrawRoomAtLeaf(baseMap); //debug //Screen.Instance.DrawMapDebug(baseMap); //Draw connecting corridors rootNode.DrawCorridorConnectingChildren(baseMap); //Add any extra connecting corridors as specified for (int i = 0; i < extraConnections; i++) { rootNode.AddRandomConnection(baseMap); } //Add doors where single corridors terminate into rooms AddDoors(); //Turn corridors into normal squares and surround with walls CorridorsIntoRooms(); //Now fill all void with walls //Fill the map with walls for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { if (baseMap.mapSquares[i, j].Terrain == MapTerrain.Void) { baseMap.mapSquares[i, j].Terrain = MapTerrain.Wall; } } } //Work out where the staircases will be //We just want 2 places that aren't too close to each other. The map is guaranteed connected double RequiredStairDistance = (width * 0.5); double stairDistance; do { upStaircase = RandomWalkablePoint(); downStaircase = RandomWalkablePoint(); stairDistance = Math.Sqrt(Math.Pow(upStaircase.x - downStaircase.x, 2) + Math.Pow(upStaircase.y - downStaircase.y, 2)); } while (stairDistance < RequiredStairDistance); //Set which squares are light blocking //Now done during creation //SetLightBlocking(baseMap); //Set the PC start location in a random room baseMap.PCStartLocation = rootNode.RandomRoomPoint().GetPointInRoomOnly(); //Now we use the cave algorithm to eat the map //Instead of setting this Empty like in cave, set them to Corridor temporarily (so the algo knows where it's been) DiggingChance = 22; //Start digging from a random point int noDiggingPoints = 6 + Game.Random.Next(2); for (int i = 0; i < noDiggingPoints; i++) { int x = Game.Random.Next(width); int y = Game.Random.Next(height); //Don't dig right to the edge if (x == 0) x = 1; if (x == width - 1) x = width - 2; if (y == 0) y = 1; if (y == height - 1) y = height - 2; Dig(x, y); } //Turn the corridors into empty for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { if (baseMap.mapSquares[i, j].Terrain == MapTerrain.Corridor) { baseMap.mapSquares[i, j].Terrain = MapTerrain.Empty; } } } //Do a final pass to convert Wall into something more exciting if (wallType.Count > 0) { for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { if (baseMap.mapSquares[i, j].Terrain == MapTerrain.Wall) { baseMap.mapSquares[i, j].Terrain = wallType[rand.Next(wallType.Count)]; } } } } return baseMap.Clone(); }