Exemple #1
0
        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();
                }
            }
        }
Exemple #11
0
        /// <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;
        }
Exemple #14
0
        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);
                }
            }
        }
Exemple #15
0
        /// <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;
        }
Exemple #16
0
        /// <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();
                }

            }
        }
Exemple #20
0
        //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();
        }