private DungeonRoom CreateRandomExit(PlanetData data, DungeonRoom room, bool locked,
                                         int lockID, bool bossRoom, bool treasure,
                                         PlanetDifficultyModifiers difficultyModifiers)
    {
        if (room.ExitCount == 4)
        {
            return(room);
        }

        List <Direction> directions = new List <Direction>
        {
            Direction.Up, Direction.Right, Direction.Down, Direction.Left
        };

        //remove any directions that are not available
        for (int j = directions.Count - 1; j >= 0; j--)
        {
            IntPair roomPos = AddDirection(room.position, directions[j]);
            if (data.GetRoomAtPosition(roomPos) != null)
            {
                directions.RemoveAt(j);
            }
        }

        //if no available directions then we're in a dead end
        if (directions.Count == 0)
        {
            return(room);
        }

        Direction randomDirection = directions[Random.Range(0, directions.Count)];
        IntPair   pos             = AddDirection(room.position, randomDirection);

        DungeonRoom newRoom;

        if (bossRoom)
        {
            newRoom = new BossRoom(pos, room, difficultyModifiers.enemyRoomDifficulty);
        }
        else if (treasure)
        {
            newRoom = new TreasureRoom(pos, room);
        }
        else
        {
            newRoom = PickRandomRoom(pos, room, difficultyModifiers);
        }
        data.AddRoom(newRoom);

        if (locked)
        {
            ConnectWithLock(room, newRoom, randomDirection, lockID);
        }
        else
        {
            Connect(room, newRoom, randomDirection);
        }

        return(newRoom);
    }
    public PlanetData Generate(float difficultySetting)
    {
        PlanetData data = new PlanetData();
        PlanetDifficultyModifiers difficultyModifiers
            = new PlanetDifficultyModifiers(difficultySetting);

        DungeonRoom currentRoom = null;
        int         keyLevel    = 1;

        //rule 1 -	Create starter room
        DungeonRoom startRoom = new StartRoom(IntPair.zero, null);

        data.AddRoom(startRoom);
        data.startRoom = startRoom;

        //rule 2 -	"Current Room" is starter room
        currentRoom = data.startRoom;

        //rule 7 -	Repeat steps 3 - 6 Y amount of times.
        int branchCount = Mathf.Max(1, Random.Range(
                                        difficultyModifiers.minBranchCount,
                                        PlanetDifficultyModifiers.MAX_BRANCH_COUNT));

        for (int j = 0; j < branchCount; j++)
        {
            //rule 3 -	Create a single branch from the "Current room" until X rooms have been
            //			created. Branch can't overlap with existing rooms. If branch meets a dead
            //			end, end the branch.
            int branchLength = Mathf.Max(1,
                                         Random.Range(difficultyModifiers.minBranchLength, difficultyModifiers.maxBranchLength));
            for (int i = 0; i < branchLength; i++)
            {
                DungeonRoom newRoom = CreateRandomExit(data, currentRoom, false,
                                                       keyLevel, false, false, difficultyModifiers);
                if (newRoom == currentRoom)
                {
                    break;
                }
                currentRoom = newRoom;
            }

            //rule 4 -	Place a key at the end of that branch
            currentRoom.AddKey(keyLevel);

            //rule 5 -	Create a locked exit to a new room from any existing room except the end of
            //			that branch.
            List <DungeonRoom> existingRooms = data.GetRooms();
            DungeonRoom        lockRoom      = currentRoom;
            do
            {
                int randomIndex = Random.Range(0, existingRooms.Count);
                lockRoom = existingRooms[randomIndex];
            } while (lockRoom == currentRoom || lockRoom.ExitCount == 4);
            lockRoom = CreateRandomExit(data, lockRoom, true,
                                        keyLevel, j == branchCount - 1, false, difficultyModifiers);
            keyLevel++;

            //rule 6 -	"Current room" is the new room on the other side of the locked exit
            currentRoom = lockRoom;
        }

        //rule 8 -	"Current room" is the final room
        data.finalRoom = currentRoom;

        //rule 9 -	Create "Dead end" branches Z times of X length from any room except the boss
        //			room.
        branchCount = Mathf.Max(0, Random.Range(difficultyModifiers.minDeadEndCount, difficultyModifiers.maxDeadEndCount));
        for (int i = 0; i < branchCount; i++)
        {
            List <DungeonRoom> existingRooms = data.GetRooms();
            DungeonRoom        deadEndStart  = null;
            do
            {
                int randomIndex = Random.Range(0, existingRooms.Count);
                deadEndStart = existingRooms[randomIndex];
            } while (deadEndStart == data.finalRoom || deadEndStart.ExitCount == 4);
            currentRoom = deadEndStart;

            int branchLength = Mathf.Max(1,
                                         Random.Range(difficultyModifiers.minBranchLength, difficultyModifiers.maxBranchLength));
            for (int j = 0; j < branchLength; j++)
            {
                DungeonRoom newRoom = CreateRandomExit(data, currentRoom, false,
                                                       keyLevel, false, j == branchLength - 1, difficultyModifiers);
                if (newRoom == currentRoom)
                {
                    break;
                }
                currentRoom = newRoom;
            }
        }

        for (int i = 0; i < data.GetRoomCount(); i++)
        {
            data.GetRooms()[i].GenerateContent();
        }

        for (int i = 0; i < data.GetRoomCount(); i++)
        {
            data.GetRooms()[i].GenerateOuterWalls();
        }

        return(data);
    }