示例#1
0
 //Check if the level block meets the given requirements.
 public bool MeetsRequirements(LevelBlockRequirements requirements)
 {
     return (
         //If the requirements dictate that a wall or exit should be at a certain position, check if the walls and exits match.
         (requirements.LeftSideType != SideType.Wall | HasLeftWall &&
         requirements.LeftSideType != SideType.Exit | HasLeftExit &&
         requirements.RightSideType != SideType.Wall | HasRightWall &&
         requirements.RightSideType != SideType.Exit | HasRightExit &&
         requirements.TopSideType != SideType.Wall | HasTopWall &&
         requirements.TopSideType != SideType.Exit | HasTopExit &&
         requirements.BottomSideType != SideType.Wall | HasBottomWall &&
         requirements.BottomSideType != SideType.Exit | HasBottomExit) &&
         //Check if the exits match completely if required.
         ((!ExitsMustMatch) ||
         (requirements.LeftSideType == SideType.Exit | !HasLeftExit &&
         requirements.RightSideType == SideType.Exit | !HasRightExit &&
         requirements.TopSideType == SideType.Exit | !HasTopExit &&
         requirements.BottomSideType == SideType.Exit | !HasBottomExit)) &&
         //And check if the group matches
         (Group == requirements.Group) &&
         //And check if the theme matches (if this group has a theme)
         (Theme == "" || requirements.Theme.Contains(Theme)));
 }
示例#2
0
        //Generate the level
        public void Generate(World world, Vector2 position, List<RoomExit> roomExits = null, List<string> guaranteedSpecialBlocks = null, string theme = "",
            int enemyNum = 0, List<Type> possibleEnemyTypes = null)
        {
            //Convert null arguments into default values
            roomExits = roomExits ?? new List<RoomExit>();
            guaranteedSpecialBlocks = guaranteedSpecialBlocks ?? new List<string>();
            possibleEnemyTypes = possibleEnemyTypes ?? new List<Type>();

            //Create vars
            World = world;

            //Create the basic grid for the level. This will contain the main path through the level.
            int hBlocks = Width / BlockWidth, vBlocks = Height / BlockHeight;
            LevelBlockRequirements[,] basicGrid = new LevelBlockRequirements[hBlocks, vBlocks];
            int[,] enemyNumber = new int[hBlocks, vBlocks];

            //List for positions with enemies
            FairRandomCollection<Point> pointsThatCanHaveEnemies = new FairRandomCollection<Point>();

            //By default, all sides are walls, unless this is the bossroom, in which case all sides (except the walls) are open.
            for (int i = 0; i < hBlocks; i++)
            {
                for (int j = 0; j < vBlocks; j++)
                {
                    pointsThatCanHaveEnemies.Add(new Point(i, j));

                    if (theme == "Boss")
                    {
                        basicGrid[i, j] = new LevelBlockRequirements(SideType.Exit);
                        if (i == 0)
                            basicGrid[i, j].LeftSideType = SideType.Wall;
                        if (j == 0)
                            basicGrid[i, j].TopSideType = SideType.Wall;
                        if (i == hBlocks - 1)
                            basicGrid[i, j].RightSideType = SideType.Wall;
                        if (j == vBlocks - 1)
                            basicGrid[i, j].BottomSideType = SideType.Wall;
                    }
                    else
                        basicGrid[i, j] = new LevelBlockRequirements(SideType.Wall);
                }
            }

            //Create exits where they should be
            foreach (RoomExit exit in roomExits)
            {
                if (exit.Direction == Direction.Left)
                    basicGrid[exit.Position.X, exit.Position.Y].LeftSideType = SideType.Exit;
                if (exit.Direction == Direction.Right)
                    basicGrid[exit.Position.X, exit.Position.Y].RightSideType = SideType.Exit;
                if (exit.Direction == Direction.Up)
                    basicGrid[exit.Position.X, exit.Position.Y].TopSideType = SideType.Exit;
                if (exit.Direction == Direction.Down)
                    basicGrid[exit.Position.X, exit.Position.Y].BottomSideType = SideType.Exit;

                //Level blocks with an exit can't have enemies
                pointsThatCanHaveEnemies.Remove(new Point(exit.Position.X, exit.Position.Y));
            }

            //Connect the exits
            ConnectPoints(basicGrid, roomExits.Select(exitPoint => exitPoint.Position).ToList());

            //Connect each now unconnected point to a connected point
            ConnectUnconnectedPoints(basicGrid);

            //Prune unneeded walls
            PruneWalls(basicGrid);

            //Create the actual level grid
            LevelBlock[,] levelGrid = new LevelBlock[hBlocks, vBlocks];

            //Get any border blocks from the guaranteed blocks.
            List<string> borderBlocks = guaranteedSpecialBlocks.Where(specialBlock => specialBlock.Contains("Border")).ToList();
            if (borderBlocks.Count != 0)
            {
                foreach (string borderBlock in borderBlocks)
                {
                    guaranteedSpecialBlocks.Remove(borderBlock);

                    //Check which direction should be used
                    Direction dir = Direction.Right;
                    if (borderBlock.Contains("Left")) dir = Direction.Left;
                    if (borderBlock.Contains("Right")) dir = Direction.Right;
                    if (borderBlock.Contains("Up")) dir = Direction.Up;
                    if (borderBlock.Contains("Down")) dir = Direction.Down;

                    //Find the exit.
                    RoomExit exit = roomExits.Find(ex => ex.Direction == dir);

                    //And make sure the block is placed there.
                    basicGrid[exit.Position.X, exit.Position.Y].Group = borderBlock;
                }
            }

            //And get the boss portal and place it on the bottom
            if (guaranteedSpecialBlocks.Contains("BossPortal"))
            {
                guaranteedSpecialBlocks.Remove("BossPortal");

                basicGrid[World.Random.Next(1, hBlocks - 1), vBlocks - 1].Group = "BossPortal";
            }

            //Handle any other guaranteed blocks
            foreach (string guaranteedBlock in guaranteedSpecialBlocks)
            {
                int i = 0, j = 0;
                do
                {
                    i = World.Random.Next(hBlocks);
                    j = World.Random.Next(vBlocks);
                }
                while (basicGrid[i, j].Group != "");
                basicGrid[i, j].Group = guaranteedBlock;
            }

            //Choose the level blocks
            for (int i = 0; i < hBlocks; i++)
            {
                for (int j = 0; j < vBlocks; j++)
                {
                    if (levelGrid[i, j] == null)
                    {
                        //Add the level theme
                        basicGrid[i, j].Theme += theme;

                        //Get a block that would fit at this position.
                        LevelBlock foundBlock = GetPossibleLevelBlock(basicGrid[i, j]);
                        if (foundBlock != null)
                            levelGrid[i, j] = foundBlock;
                        else
                            throw new Exception("There's no block that would fit here! Please create more blocks (of group '" + basicGrid[i, j].Group +
                                "') and add them to LevelBlocks.txt.");
                    }
                }
            }

            //Add enemies
            for (int i = 0; i < enemyNum; i++)
            {
                Point placeEnemyAt = pointsThatCanHaveEnemies.Get();
                enemyNumber[placeEnemyAt.X, placeEnemyAt.Y]++;
            }

            //And place them
            for (int i = 0; i < hBlocks; i++)
            {
                for (int j = 0; j < vBlocks; j++)
                {
                    levelGrid[i, j].Place(World, specialTiles, (int) position.X + i * BlockWidth * World.TileWidth,
                        (int) position.Y + j * BlockHeight * World.TileHeight, basicGrid[i, j].LeftSideType == SideType.Wall,
                        basicGrid[i, j].RightSideType == SideType.Wall, basicGrid[i, j].TopSideType == SideType.Wall,
                        basicGrid[i, j].BottomSideType == SideType.Wall, j == vBlocks - 1, enemyNumber[i, j], possibleEnemyTypes);
                }
            }
        }
示例#3
0
        //Remove walls where they are too thick for no reason.
        //For example, if two generation blocks on top of each other would declare a wall on the side of the other block, this'd remove
        //one of the two walls.
        void PruneWalls(LevelBlockRequirements[,] basicGrid)
        {
            int hBlocks = Width / BlockWidth, vBlocks = Height / BlockHeight;
            for (int i = 0; i < hBlocks; i++)
            {
                for (int j = 0; j < vBlocks; j++)
                {
                    LevelBlockRequirements block = basicGrid[i, j];

                    //We only have to look to the right and bottom, as other blocks check for the left and top (their right and bottom) automatically.

                    //Check to the right
                    if (i < hBlocks - 1 && block.RightSideType == SideType.Wall)
                    {
                        LevelBlockRequirements rightBlock = basicGrid[i + 1, j];
                        if (rightBlock.LeftSideType == SideType.Wall)
                        {
                            switch (random.Next(2))
                            {
                                case 0:
                                    block.RightSideType = SideType.Any;
                                    break;
                                case 1:
                                    rightBlock.LeftSideType = SideType.Any;
                                    break;
                            }
                        }
                    }

                    //Check to the bottom
                    if (j < vBlocks - 1 && block.BottomSideType == SideType.Wall)
                    {
                        LevelBlockRequirements bottomBlock = basicGrid[i, j + 1];
                        if (bottomBlock.BottomSideType == SideType.Wall)
                        {
                            switch (random.Next(2))
                            {
                                case 0:
                                    block.BottomSideType = SideType.Any;
                                    break;
                                case 1:
                                    bottomBlock.TopSideType = SideType.Any;
                                    break;
                            }
                        }
                    }
                }
            }
        }
示例#4
0
        //Get a block that meets certain requirements.
        LevelBlock GetPossibleLevelBlock(LevelBlockRequirements requirements)
        {
            WeightedRandomCollection<LevelBlock> possibleLevelBlocks = new WeightedRandomCollection<LevelBlock>();

            //Check all level blocks and see which ones would meet the requirements.
            foreach (LevelBlock levelBlock in levelBlocks)
            {
                if (levelBlock.MeetsRequirements(requirements))
                    possibleLevelBlocks.Add(levelBlock, levelBlock.AppearancePreference);
            }

            //If we've not found anything, return null.
            if (possibleLevelBlocks.IsWriteOnly)
                return null;

            //Then return one.
            return possibleLevelBlocks.Get();
        }
示例#5
0
        //Connect all completely unconnected points to a connected point, making sure all cells are reachable.
        void ConnectUnconnectedPoints(LevelBlockRequirements[,] basicGrid)
        {
            int hBlocks = Width / BlockWidth, vBlocks = Height / BlockHeight;

            //Store which blocks weren't connected.
            bool[,] isConnected = new bool[hBlocks, vBlocks];
            List<Point> unconnectedPoints = new List<Point>();
            for (int i = 0; i < hBlocks; i++)
            {
                for (int j = 0; j < vBlocks; j++)
                {
                    isConnected[i, j] = ! basicGrid[i, j].IsOnlyWalls();
                    if (! isConnected[i, j])
                        unconnectedPoints.Add(new Point(i, j));
                }
            }

            bool connectedAnythingThisRound;

            while (unconnectedPoints.Count > 0)
            {
                connectedAnythingThisRound = false;

                List<Point> removedUnconnectedPoints = new List<Point>(); //A list to store points we remove as changing a list during foreach is impossible

                unconnectedPoints.Shuffle(); //Shuffle the points so that they connect in a more random way.

                foreach (Point point in unconnectedPoints)
                {
                    int i = point.X, j = point.Y; //Temporary store the x and y of this point.

                    List<Point> possiblePoints = new List<Point>(); //Points to connect to.
                    if (i > 0 && isConnected[i - 1, j])
                        possiblePoints.Add(new Point(i - 1, j));
                    if (i < hBlocks - 1 && isConnected[i + 1, j])
                        possiblePoints.Add(new Point(i + 1, j));
                    if (j > 0 && isConnected[i, j - 1])
                        possiblePoints.Add(new Point(i, j - 1));
                    if (j < vBlocks - 1 && isConnected[i, j + 1])
                        possiblePoints.Add(new Point(i, j + 1));

                    if (possiblePoints.Count != 0)
                    {
                        //Connect this point to a random other one.
                        ConnectPoints(basicGrid, new List<Point>() { point, possiblePoints.GetRandomItem() });
                        isConnected[i, j] = true; //Mark this point as connected.
                        connectedAnythingThisRound = true;
                        removedUnconnectedPoints.Add(point);
                    }
                }

                foreach (Point point in removedUnconnectedPoints)
                    unconnectedPoints.Remove(point);

                //Give up if we couldn't connect anything at all.
                if (! connectedAnythingThisRound)
                    break;
            }
        }
示例#6
0
        //Connect any number of points on the level grid.
        void ConnectPoints(LevelBlockRequirements[,] basicGrid, List<Point> points)
        {
            if (points.Count == 0)
                return;

            //Choose a random point and connect that to each other point.
            //This automatically guarantees that each point is connected to each other point, as
            //you can always travel to the first point and then to the target point.
            Point point = points.GetRandomItem();

            for (int i = 0; i < points.Count; i++)
            {
                Point otherPoint = points[i];
                if (point == otherPoint)
                    continue;

                //Check how far we have to travel
                Point distanceDifference = new Point(otherPoint.X - point.X, otherPoint.Y - point.Y);

                //And store the directions we have to take into a RandomCollection of directions
                RandomCollection<Direction> directionsToTravel = new RandomCollection<Direction>();

                if (distanceDifference.X < 0)
                    directionsToTravel.Add(Direction.Left, -distanceDifference.X);
                if (distanceDifference.X > 0)
                    directionsToTravel.Add(Direction.Right, distanceDifference.X);
                if (distanceDifference.Y < 0)
                    directionsToTravel.Add(Direction.Up, -distanceDifference.Y);
                if (distanceDifference.Y > 0)
                    directionsToTravel.Add(Direction.Down, distanceDifference.Y);

                //Then actually travel from the first point to the second one.
                Point position = point;
                while (directionsToTravel.Count != 0)
                {
                    switch (directionsToTravel.Take())
                    {
                        case Direction.Left:
                            basicGrid[position.X, position.Y].LeftSideType = SideType.Exit;
                            position.X -= 1;
                            basicGrid[position.X, position.Y].RightSideType = SideType.Exit;
                            break;
                        case Direction.Right:
                            basicGrid[position.X, position.Y].RightSideType = SideType.Exit;
                            position.X += 1;
                            basicGrid[position.X, position.Y].LeftSideType = SideType.Exit;
                            break;
                        case Direction.Up:
                            basicGrid[position.X, position.Y].TopSideType = SideType.Exit;
                            position.Y -= 1;
                            basicGrid[position.X, position.Y].BottomSideType = SideType.Exit;
                            break;
                        case Direction.Down:
                            basicGrid[position.X, position.Y].BottomSideType = SideType.Exit;
                            position.Y += 1;
                            basicGrid[position.X, position.Y].TopSideType = SideType.Exit;
                            break;
                    }
                }
            }
        }