Example #1
0
        //Place a level block in a level. The preferred walls can be specified. These can only be guaranteed
        // if the level has the wall type (for example, preferLeftWall can be ignored if HasLeftWall is false).
        //If a wall isn't preferred at a position, an exit is used instead if Has*Exit is true.
        public void Place(World world, Dictionary<char, ISpecialTileDefinition> specialTiles, int x, int y, bool preferLeftWall = true,
            bool preferRightWall = true, bool preferTopWall = true, bool preferBottomWall = true, bool isBottomOfRoom = false, int numberOfEnemies = 0,
            List<Type> possibleEnemyTypes = null)
        {
            possibleEnemyTypes = possibleEnemyTypes ?? new List<Type>();

            int BlockID = random.Next(int.MaxValue);

            bool[,] isActuallyEmpty = new bool[Width, Height];
            bool[,] isActuallyWall = new bool[Width, Height];
            FairRandomCollection<Vector2> possibleEnemyPositions = new FairRandomCollection<Vector2>();
            FairRandomCollection<Vector2> possibleTurretPositions = new FairRandomCollection<Vector2>();

            //Place the tiles in the level
            for (int i = 0; i < Width; i++)
            {
                for (int j = 0; j < Height; j++)
                {
                    char data = Data[i, j];

                    //Check special tiles until we find a normal tile.
                    int loopCount = 0;

                    //Some tiles can be turned into walls if they end up on the sides of the level.
                    bool specialTileTurnsIntoWallOnSides = false;

                    while (specialTiles.ContainsKey(data))
                    {
                        specialTileTurnsIntoWallOnSides = specialTileTurnsIntoWallOnSides || specialTiles[data].CanBeWall;
                        List<string> specialKeywords = new List<string>(); //Contains the special keywords that are true at this moment.

                        if (preferLeftWall) specialKeywords.Add("LEFTWALL");
                        if (preferRightWall) specialKeywords.Add("RIGHTWALL");
                        if (preferTopWall) specialKeywords.Add("TOPWALL");
                        if (preferBottomWall) specialKeywords.Add("BOTTOMWALL");

                        data = specialTiles[data].GetTile(BlockID, specialKeywords);

                        if (loopCount > 1000)
                            throw new Exception("Possible infinite loop detected! Please change the level definition file.");
                    }

                    //If the special tile can be turned into a wall, check if this should happen.
                    if (specialTileTurnsIntoWallOnSides)
                    {
                        if (i == 0) //Left wall
                        {
                            if (preferLeftWall)
                                data = '1';
                        }
                        if (i == Width - 1) //Right wall
                        {
                            if (preferRightWall)
                                data = '1';
                        }
                        if (j == 0) //Top wall
                        {
                            if (preferTopWall)
                                data = '1';
                        }
                        if (j == Height - 1) //Bottom wall
                        {
                            if (preferBottomWall)
                                data = '1';
                        }
                    }

                    int baseX = x + World.TileWidth * i, baseY = y + World.TileHeight * j;
                    float centerX = baseX + World.TileWidth / 2, centerY = baseY + World.TileHeight / 2;
                    Rectangle stdCollisionRect = new Rectangle(baseX, baseY, World.TileWidth, World.TileHeight);

                    //If it's nothing or something the player can move through, and the bottom of the room, then add a jumpthrough.
                    if (data == '.' | data == '/' | data == '\\' && j == Height - 1 && isBottomOfRoom)
                        world.AddObject(new JumpThrough(new Rectangle(baseX, baseY, World.TileWidth, 1)));

                    if (data == '1') //A wall
                    {
                        world.AddObject(new Wall(stdCollisionRect));

                        isActuallyWall[i, j] = true;
                        if (j != 0 && isActuallyEmpty[i, j - 1])
                        {
                            //We can place an enemy here!
                            possibleEnemyPositions.Add(new Vector2(centerX, centerY - World.TileHeight + 10));
                        }
                    }
                    else if (data == 'T') //A tile
                        world.AddObject(new WindowTile(stdCollisionRect));
                    else if (data == 'D') //A gun door
                        world.AddObject(new GunDoor(), baseX + World.TileWidth / 2, baseY);
                    else if (data == 'M') //A melee door
                        world.AddObject(new MeleeDoor(), baseX + World.TileWidth / 2, baseY);
                    else if (data == 'C') //A rocket door
                        world.AddObject(new RocketDoor(), baseX + World.TileWidth / 2, baseY);
                    else if (data == 'B') //A boss door
                        world.AddObject(new BossDoor(), baseX + World.TileWidth / 2, baseY);
                    else if (data == 'O')
                        world.AddObject(new Portal(), baseX + 6, baseY);
                    else if (data == 'S') //Spikes
                        world.AddObject(new Spikes(), baseX, baseY + (int)(World.TileHeight * (100f / 128f)));
                    else if (data == 'G') //A gun pickup block
                        world.AddObject(new GunPickup(), centerX, centerY);
                    else if (data == 'U') //A gun upgrade pickup block
                        world.AddObject(new GunUpgrade(), centerX, centerY);
                    else if (data == 'W') //A wrench pickup block
                        world.AddObject(new WrenchPickup(), centerX, centerY);
                    else if (data == 'R') //A rocket pickup block
                        world.AddObject(new RocketPickup(), centerX, centerY);
                    else if (data == 'H') //A health pickup block
                        world.AddObject(new HealthDrop(10), centerX, centerY);
                    else if (data == 'A') //First health upgrade
                        world.AddObject(new MaxHPUpgrade(), centerX, centerY);
                    else if (data == 'E') //Huge health upgrade
                        world.AddObject(new HugeMaxHPUpgrade(), centerX, centerY);
                    else if (data == 'F') //Drone upgrade
                        world.AddObject(new DroneRangeUpgrade(), centerX, centerY);
                    else if (data == 'P') //The player
                    {
                        world.Player = new Player();
                        world.AddObject(world.Player, centerX, centerY);
                    }
                    else if (data == 'J') //A jumpthrough block
                        world.AddObject(new JumpThrough(new Rectangle(baseX, baseY, World.TileWidth, 1)));
                    else if (data == '\\') //A slope
                        world.AddObject(new SlopeLeft(stdCollisionRect));
                    else if (data == '/') //A slope
                        world.AddObject(new SlopeRight(stdCollisionRect));
                    else if (data == '.')
                    {
                        //Nothing
                        isActuallyEmpty[i, j] = true;

                        if (j != 0 && isActuallyWall[i, j - 1])
                        {
                            //We can place a turret here!
                            possibleTurretPositions.Add(new Vector2(centerX, baseY + 15));
                        }
                    }
                }
            }

            //Place some enemies.
            for (int i = 0; i < numberOfEnemies; i++)
            {
                if (possibleEnemyTypes.Contains(typeof(Turret)) && World.Random.Next(3) == 0)
                {
                    if (possibleTurretPositions.Count != 0)
                        world.AddObject(new Turret(), possibleTurretPositions.Get());
                }
                else if (possibleEnemyPositions.Count != 0)
                {
                    world.AddObject(Activator.CreateInstance(possibleEnemyTypes.Except(new List<Type>() { typeof(Turret) }).ToList().GetRandomItem())
                        as GameObject, possibleEnemyPositions.Get());
                }
            }
        }
Example #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);
                }
            }
        }