Esempio n. 1
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);
                }
            }
        }
Esempio n. 2
0
        //Reads the possible level blocks from the level file
        void GetLevelBlocks()
        {
            string levelBlocksData = File.ReadAllText("Content/LevelBlocks.txt");
            //Handle the different types of enter (\r\n, \r, and \n)
            levelBlocksData = levelBlocksData.Replace("\r\n", "\n");
            levelBlocksData = levelBlocksData.Replace("\r", "\n");
            string[] levelBlocksLines = levelBlocksData.Split('\n');
            int thisBlockDataHeight = 0; //The current height of a level block.

            LevelBlock currentLevelBlock = null;

            int totalIgnoreCount = 0;
            string blockSpecialGroup = ""; //Groups are used for special, named, blocks, like the starting position of the player.
            string blockTheme = "";

            for (int i = 0; i < levelBlocksLines.Length; i++)
            {
                string line = levelBlocksLines[i];

                //IGNORE and ATTENTION can be used if some lines shouldn't be processed.
                if (line.StartsWith("IGNORE"))
                    totalIgnoreCount++;
                if (line.StartsWith("ATTENTION") && totalIgnoreCount > 0)
                    totalIgnoreCount--;
                if (line.StartsWith("THEMEEND"))
                    blockTheme = "";
                if (totalIgnoreCount > 0)
                    continue;

                if (line.StartsWith("SPECIAL"))
                {
                    line = line.Replace("SPECIAL", ""); //Delete the SPECIAL part of the line.

                    //If there's a space at the beginning now, remove that, too.
                    if (line.StartsWith(" "))
                        line = line.Remove(0, 1);

                    //Now, the rest of line is the special group for the next block.
                    blockSpecialGroup = line;
                }
                else if (line.StartsWith("THEMESTART"))
                {
                    line = line.Replace("THEMESTART", ""); //Delete the SPECIAL part of the line.

                    //If there's a space at the beginning now, remove that, too.
                    if (line.StartsWith(" "))
                        line = line.Remove(0, 1);

                    //Now, the rest of line is the current theme of blocks.
                    blockTheme = line;
                }
                else if (line.StartsWith("BLOCK"))
                {
                    currentLevelBlock = new LevelBlock(BlockWidth, BlockHeight);
                    thisBlockDataHeight = 0;
                    levelBlocks.Add(currentLevelBlock);

                    line = line.Replace("BLOCK", ""); //Delete the BLOCK part of the line.

                    //If there's a space at the beginning now, remove that, too.
                    if (line.StartsWith(" "))
                        line = line.Remove(0, 1);

                    //Now check the arguments of the block.
                    string[] arguments = line.Split(' ');

                    //The first argument should contain information about the exits of the block.
                    string exits = arguments[0];

                    if (exits.Contains("U"))
                        currentLevelBlock.HasTopExit = true;
                    if (exits.Contains("D"))
                        currentLevelBlock.HasBottomExit = true;
                    if (exits.Contains("L"))
                        currentLevelBlock.HasLeftExit = true;
                    if (exits.Contains("R"))
                        currentLevelBlock.HasRightExit = true;
                    if (exits.Contains("!"))
                        currentLevelBlock.ExitsMustMatch = true;

                    if (blockSpecialGroup != "") //If this block is part of a special block group, set that group.
                    {
                        currentLevelBlock.Group = blockSpecialGroup;
                        blockSpecialGroup = ""; //The next block won't be part of a group by default.
                    }

                    currentLevelBlock.Theme = blockTheme;

                    //The second argument (optional) should contain information about how often a block appears.
                    if (arguments.Length >= 2)
                    {
                        string appearancePreferenceString = arguments[1];
                        try
                        {
                            currentLevelBlock.AppearancePreference = double.Parse(appearancePreferenceString, CultureInfo.InvariantCulture);
                        }
                        catch
                        {
                            currentLevelBlock.AppearancePreference = 1;
                        }
                    }
                }
                else if (currentLevelBlock != null)
                {
                    if (!string.IsNullOrWhiteSpace(line)) //Set the level data.
                    {
                        for (int j = 0; j < line.Length; j++)
                        {
                            currentLevelBlock.Data[j, thisBlockDataHeight] = line[j];
                        }
                        thisBlockDataHeight++;
                    }
                    else //An empty line means the end of the block definition of this block.
                    {
                        currentLevelBlock.UpdateLevelInfo(GetWallTiles());

                        currentLevelBlock = null;
                    }
                }
                else if (line.StartsWith("DEFINE ")) //This is a special tile definition
                {
                    ParseSpecialTileDefinition(line.Remove(0, "DEFINE ".Length));
                }
            }
        }