Пример #1
0
        /// <summary>
        /// Constructs the primary or critical path for the dungeon - this is the central path
        /// that any extra rooms branch off from.
        /// Primary paths only move forward - they never branch.
        /// </summary>
        /// <param name="scopes"></param>
        private void ConstructPrimaryPath(RandomDungeonScopeData[] scopes)
        {
            // After choosing where a new room is going to go, close off all the paths in
            // the previous room to prevent branching.
            bool firstRoomPlaced = false;

            List <int> successfulRoomsPerScope = new List <int>();

            for (int i = 0; i < scopes.Length; ++i)
            {
                successfulRoomsPerScope.Add(0);
            }

            for (int scopeIdx = 0; scopeIdx < scopes.Length; ++scopeIdx)
            {
                RandomDungeonScopeData scope = scopes[scopeIdx];
                int roomsPerScope            = mRNG.Next(scope.criticalPathMinRooms, scope.criticalPathMaxRooms);

                for (int i = 0; i < roomsPerScope; ++i)
                {
                    RoomPlacementOptions options = RoomPlacementOptions.None;
                    if (i == 0)
                    {
                        options = RoomPlacementOptions.AllowMismatchScopes;
                    }

                    if (!firstRoomPlaced)
                    {
                        firstRoomPlaced = true;
                        if (TryPlaceRoom("guaranteed_first", true, scopeIdx, options) != null)
                        {
                            successfulRoomsPerScope[scopeIdx]++;
                        }
                        else
                        {
                            // Force another room generation
                            i--;
                        }
                    }
                    else if (TryPlaceRoom("primary", true, scopeIdx, options) != null)
                    {
                        successfulRoomsPerScope[scopeIdx]++;
                    }
                }
            }

            for (int i = 0; i < successfulRoomsPerScope.Count; ++i)
            {
                if (successfulRoomsPerScope[i] < scopes[i].criticalPathMinRooms)
                {
                    mMapInvalid = true;
                    return;
                }
            }
        }
Пример #2
0
        /// <summary>
        /// Verifies whether room placement is possible.
        /// We go through every tile in the room and ensure that it's not overlapping something invalid
        /// (ie: if we try to place a wall that would overlap a floor tile, that's invalid).
        /// </summary>
        /// <param name="roomData"></param>
        /// <param name="mapExit"></param>
        /// <param name="roomExit"></param>
        /// <param name="scope"></param>
        /// <param name="roomPlacementOptions"></param>
        /// <returns></returns>
        private bool RoomHasInvalidCollision(RoomData roomData, Vector2Int mapExit, Vector2Int roomExit, int scope, RoomPlacementOptions roomPlacementOptions = RoomPlacementOptions.None)
        {
            bool fullOverlap = true;

            for (int roomX = 0; roomX < roomData.width; ++roomX)
            {
                for (int roomY = 0; roomY < roomData.height; ++roomY)
                {
                    int        mapX   = mapExit.x - roomExit.x + roomX;
                    int        mapY   = mapExit.y - roomExit.y + roomY;
                    Vector2Int mapPos = new Vector2Int(mapX, mapY);

                    int roomTile = roomData.Tile(roomX, roomY);

                    if (!mRoomScratch.ContainsKey(mapPos) || roomTile == RandomDungeonTileData.EMPTY_TILE)
                    {
                        // Any kind of tile can be placed over an empty location
                        // However, at this point we do know that the rooms are not 100% overlapping
                        fullOverlap = false;
                        continue;
                    }
                    else
                    {
                        int mapTile      = mRoomScratch[mapPos].tileType;
                        int mapTileScope = mRoomScratch[mapPos].scope;

                        if (scope != mapTileScope && !RoomPlacementOptionSet(roomPlacementOptions, RoomPlacementOptions.AllowMismatchScopes))
                        {
                            // We're trying to connect with a room in a different scope, which we can never do to ensure
                            // that a scope only has a single point of entry.
                            return(true);
                        }
                        else if (RoomPlacementOptionSet(roomPlacementOptions, RoomPlacementOptions.AllowWallsToBecomeExits) &&
                                 mapTile == RandomDungeonTileData.WALL_TILE && roomTile == RandomDungeonTileData.EXIT_TILE)
                        {
                            // If the options indicate that walls can become exits, support that.
                            continue;
                        }
                        else if (mapTile != roomTile)
                        {
                            // Only identical tiles can be placed over one another.
                            return(true);
                        }
                    }
                }
            }

            // There's still the chance that this room is being placed completely over a previous room, which
            // we don't want to allow because it throws off our room counts.
            // We should be trying to place against at least one empty square

            return(fullOverlap);
        }
Пример #3
0
 private bool RoomPlacementOptionSet(RoomPlacementOptions options, RoomPlacementOptions testOption)
 {
     return((options & testOption) == testOption);
 }
Пример #4
0
        /// <summary>
        /// Repeatedly tries to place a room, choosing a random room from the room set and a random exit to place it off of.
        /// This will retry some maximum number of times and quit if it never finds a valid choice.
        /// </summary>
        /// <param name="roomCategory"></param>
        /// <param name="primaryPath"></param>
        /// <param name="scope"></param>
        /// <param name="roomPlacementOptions"></param>
        /// <param name="roomDataOverride"></param>
        /// <returns>The room data for the room that was placed, or null if no room could be placed.</returns>
        private RoomData TryPlaceRoom(string roomCategory, bool primaryPath, int scope, RoomPlacementOptions roomPlacementOptions = RoomPlacementOptions.None, RoomData roomDataOverride = null)
        {
            int maxAttempts = 200;

            while (maxAttempts > 0)
            {
                Vector2Int mapExit    = ChooseMapExitForNextRoom();
                RoomData   randomRoom = roomDataOverride != null ? roomDataOverride : mRoomSet.RandomRoomOfCategory(roomCategory, mPreviousRoomPlaced, mRNG);

                if (randomRoom == null)
                {
                    --maxAttempts;
                    continue;
                }

                Vector2Int roomExit = randomRoom.RandomExit(mRNG);

                if (scope == -1)
                {
                    scope = mRoomScratch[mapExit].scope;
                }

                if (!RoomHasInvalidCollision(randomRoom, mapExit, roomExit, scope, roomPlacementOptions))
                {
                    // If we're constructing a primary path, clear out any exits currently
                    // in our list. Placing a room will add new available exits to build off of.
                    // This also guards against the primary path looping in on itself.
                    if (primaryPath)
                    {
                        mAvailableExits.Clear();

                        mPrimaryPathPositions.Add(new Vector2Int(mapExit.x, mapExit.y));
                    }

                    mAvailableExits.Remove(mapExit);
                    PlaceRoom(randomRoom, mapExit, roomExit, scope, primaryPath);

                    // The probability of picking a room lowers each time it's picked
                    randomRoom.probability /= 2;

                    mPreviousRoomPlaced  = randomRoom;
                    mPreviousMapExitUsed = mapExit;

                    return(randomRoom);
                }

                --maxAttempts;
            }

            return(null);
        }