Example #1
0
        /// <inheritdoc />
        protected override IEnumerator <object?> OnPerform(GenerationContext context)
        {
            // Validate configuration
            if (CutoffBigAreaFill > TotalIterations)
            {
                throw new InvalidConfigurationException(this, nameof(CutoffBigAreaFill),
                                                        $"The value must be less than or equal to the value of {nameof(TotalIterations)}.");
            }

            // Get or create/add a wall-floor context component
            var wallFloorContext = context.GetFirstOrNew <ISettableGridView <bool> >(
                () => new ArrayView <bool>(context.Width, context.Height),
                WallFloorComponentTag);

            // Create a new array map to use in the smoothing algorithms to temporarily store old values.
            // Allocating it here instead of in the smoothing minimizes allocations.
            var oldMap = new ArrayView <bool>(wallFloorContext.Width, wallFloorContext.Height);

            // Iterate over the generated values, smoothing them with the appropriate algorithm
            for (int i = 0; i < TotalIterations; i++)
            {
                CellAutoSmoothingAlgo(wallFloorContext, oldMap, i < CutoffBigAreaFill);
                yield return(null);
            }

            // Fill to a rectangle to ensure the resulting areas are enclosed
            foreach (var pos in wallFloorContext.Bounds().PerimeterPositions())
            {
                wallFloorContext[pos] = false;
            }
        }
Example #2
0
        /// <inheritdoc/>
        protected override IEnumerator <object?> OnPerform(GenerationContext context)
        {
            // Get or create/add a wall-floor context component
            var wallFloorContext = context.GetFirstOrNew <ISettableGridView <bool> >(
                () => new ArrayView <bool>(context.Width, context.Height),
                WallFloorComponentTag
                );

            var innerBounds = wallFloorContext.Bounds().Expand(-1, -1);

            foreach (var position in wallFloorContext.Positions())
            {
                wallFloorContext[position] = innerBounds.Contains(position);
            }

            // No stages as its a simple rectangle generator
            yield break;
        }
Example #3
0
        /// <inheritdoc/>
        protected override IEnumerator <object?> OnPerform(GenerationContext context)
        {
            // Validate configuration
            if (FillProbability > 100)
            {
                throw new InvalidConfigurationException(this, nameof(FillProbability),
                                                        "The value must be a valid percent (between 0 and 100).");
            }

            // Get or create/add a grid view context component to fill
            var gridViewContext = context.GetFirstOrNew <ISettableGridView <bool> >(
                () => new ArrayView <bool>(context.Width, context.Height),
                GridViewComponentTag);

            // Determine positions to fill based on exclusion settings
            var positionsRect = ExcludePerimeterPoints
                ? gridViewContext.Bounds().Expand(-1, -1)
                : gridViewContext.Bounds();

            // Fill each position with a random value
            uint squares = 0;

            foreach (var position in positionsRect.Positions())
            {
                gridViewContext[position] = RNG.PercentageCheck(FillProbability);
                squares++;
                if (FillsBetweenPauses != 0 && squares == FillsBetweenPauses)
                {
                    squares = 0;
                    yield return(null);
                }
            }

            // Pause one last time if we need
            if (FillsBetweenPauses != 0 && squares != 0)
            {
                yield return(null);
            }
        }
Example #4
0
        /// <inheritdoc />
        protected override IEnumerator <object?> OnPerform(GenerationContext context)
        {
            // Validate configuration
            if (MinRooms > MaxRooms)
            {
                throw new InvalidConfigurationException(this, nameof(MinRooms),
                                                        $"The value must be less than or equal to the value of {nameof(MaxRooms)}.");
            }

            if (RoomMinSize > RoomMaxSize)
            {
                throw new InvalidConfigurationException(this, nameof(RoomMinSize),
                                                        $"The value must be less than or equal to the value of ${nameof(RoomMaxSize)}.");
            }

            if (RoomSizeRatioX <= 0f)
            {
                throw new InvalidConfigurationException(this, nameof(RoomSizeRatioX),
                                                        "The value must be greater than 0.");
            }

            if (RoomSizeRatioY <= 0f)
            {
                throw new InvalidConfigurationException(this, nameof(RoomSizeRatioY),
                                                        "The value must be greater than 0.");
            }

            // Get or create/add a wall-floor context component
            var wallFloorContext = context.GetFirstOrNew <ISettableGridView <bool> >(
                () => new ArrayView <bool>(context.Width, context.Height),
                WallFloorComponentTag
                );

            // Determine how many rooms to generate
            var roomCounter = RNG.NextInt(MinRooms, MaxRooms + 1);

            // Get or create/add a rooms context component
            var roomsContext = context.GetFirstOrNew(
                () => new ItemList <Rectangle>(roomCounter),
                RoomsComponentTag
                );

            // Try to place all the rooms
            while (roomCounter != 0)
            {
                var tryCounterCreate = MaxCreationAttempts;
                var placed           = false;

                // Attempt to create the room until either we reach max attempts or we create and place a room in a valid location
                while (tryCounterCreate != 0)
                {
                    var roomSize = RNG.NextInt(RoomMinSize, RoomMaxSize + 1);
                    var width    =
                        (int)(roomSize * RoomSizeRatioX); // This helps with non square fonts. So rooms don't look odd
                    var height = (int)(roomSize * RoomSizeRatioY);

                    // When accounting for font ratios, these adjustments help prevent all rooms
                    // having the same looking square format
                    var adjustmentBase = roomSize / 4;

                    if (adjustmentBase != 0)
                    {
                        var adjustment       = RNG.NextInt(-adjustmentBase, adjustmentBase + 1);
                        var adjustmentChance = RNG.NextInt(0, 2);

                        if (adjustmentChance == 0)
                        {
                            width += (int)(adjustment * RoomSizeRatioX);
                        }
                        else if (adjustmentChance == 1)
                        {
                            height += (int)(adjustment * RoomSizeRatioY);
                        }
                    }

                    width  = Math.Max(RoomMinSize, width);
                    height = Math.Max(RoomMinSize, height);

                    // Keep room interior odd, helps with placement + tunnels around the outside.
                    if (width % 2 == 0)
                    {
                        width += 1;
                    }

                    if (height % 2 == 0)
                    {
                        height += 1;
                    }

                    var roomInnerRect = new Rectangle(0, 0, width, height);

                    var tryCounterPlace = MaxPlacementAttempts;

                    // Try to place the room we've created until either it doesn't intersect any other rooms, or we reach max retries (in which case, we will scrap the room entirely, create a new one, and try again)
                    while (tryCounterPlace != 0)
                    {
                        int xPos = 0, yPos = 0;

                        // Generate the rooms at odd positions, to make door/tunnel placement easier
                        while (xPos % 2 == 0)
                        {
                            xPos = RNG.NextInt(3, wallFloorContext.Width - roomInnerRect.Width - 3);
                        }
                        while (yPos % 2 == 0)
                        {
                            yPos = RNG.NextInt(3, wallFloorContext.Height - roomInnerRect.Height - 3);
                        }

                        // Record a rectangle for the inner and outer bounds of the room we've created
                        roomInnerRect = roomInnerRect.WithPosition(new Point(xPos, yPos));
                        var roomBounds = roomInnerRect.Expand(3, 3);

                        // Check if the room intersects with any floor tile on the map already.  We do it this way instead of checking against only the rooms list
                        // to ensure that if some other map generation step placed things before we did, we don't intersect those.
                        var intersected = false;
                        foreach (var point in roomBounds.Positions())
                        {
                            if (wallFloorContext[point])
                            {
                                intersected = true;
                                break;
                            }
                        }

                        // If we intersected floor tiles, try to place the room again
                        if (intersected)
                        {
                            tryCounterPlace--;
                            continue;
                        }

                        // Once we place it in a valid location, update the wall/floor context, and add the room to the list of rooms.
                        foreach (var point in roomInnerRect.Positions())
                        {
                            wallFloorContext[point] = true;
                        }

                        placed = true;
                        roomsContext.Add(roomInnerRect, Name);
                        break;
                    }

                    if (placed)
                    {
                        yield return(null);

                        break;
                    }

                    tryCounterCreate--;
                }

                roomCounter--;
            }
        }
Example #5
0
        /// <inheritdoc />
        protected override IEnumerator <object?> OnPerform(GenerationContext context)
        {
            // Validate configuration
            if (CrawlerChangeDirectionImprovement > 100)
            {
                throw new InvalidConfigurationException(this, nameof(CrawlerChangeDirectionImprovement),
                                                        "The value must be a valid percent (between 0 and 100).");
            }

            // Logic implemented from http://journal.stuffwithstuff.com/2014/12/21/rooms-and-mazes/

            // Get or create/add a wall-floor context component
            var wallFloorContext = context.GetFirstOrNew <ISettableGridView <bool> >(
                () => new ArrayView <bool>(context.Width, context.Height),
                WallFloorComponentTag
                );

            // Get or create/add a tunnel list context component
            var tunnelList = context.GetFirstOrNew(
                () => new ItemList <Area>(),
                TunnelsComponentTag
                );

            // Record spaces we've crawled to introduce changes.
            int spacesCrawled = 0;


            var crawlers = new List <Crawler>();
            var empty    = FindEmptySquare(wallFloorContext, RNG);

            while (empty != Point.None)
            {
                var crawler = new Crawler();
                crawlers.Add(crawler);
                crawler.MoveTo(empty);
                var    startedCrawler         = true;
                ushort percentChangeDirection = 0;

                while (crawler.Path.Count != 0)
                {
                    // Dig this position
                    wallFloorContext[crawler.CurrentPosition] = true;

                    // Get valid directions (basically is any position outside the map or not?
                    var points     = AdjacencyRule.Cardinals.NeighborsClockwise(crawler.CurrentPosition).ToArray();
                    var directions = AdjacencyRule.Cardinals.DirectionsOfNeighborsClockwise(Direction.None).ToList();

                    var validDirections = new bool[4];

                    // Rule out any valid directions based on their position. Only process cardinals, do not use diagonals
                    for (var i = 0; i < 4; i++)
                    {
                        validDirections[i] = IsPointWallsExceptSource(wallFloorContext, points[i], directions[i] + 4);
                    }

                    // If not a new crawler, exclude where we came from
                    if (!startedCrawler)
                    {
                        validDirections[directions.IndexOf(crawler.Facing + 4)] = false;
                    }

                    // Do we have any valid direction to go?
                    if (validDirections[0] || validDirections[1] || validDirections[2] || validDirections[3])
                    {
                        int index;

                        // Are we just starting this crawler? OR Is the current crawler facing
                        // direction invalid?
                        if (startedCrawler || validDirections[directions.IndexOf(crawler.Facing)] == false)
                        {
                            // Just get anything
                            index                  = GetDirectionIndex(validDirections, RNG);
                            crawler.Facing         = directions[index];
                            percentChangeDirection = 0;
                            startedCrawler         = false;
                        }
                        else
                        {
                            // Increase probability we change direction
                            percentChangeDirection += CrawlerChangeDirectionImprovement;

                            if (RNG.PercentageCheck(percentChangeDirection))
                            {
                                index                  = GetDirectionIndex(validDirections, RNG);
                                crawler.Facing         = directions[index];
                                percentChangeDirection = 0;
                            }
                            else
                            {
                                index = directions.IndexOf(crawler.Facing);
                            }
                        }

                        crawler.MoveTo(points[index]);
                        spacesCrawled++;
                    }
                    else
                    {
                        crawler.Backtrack();
                        spacesCrawled++;
                    }

                    if (spacesCrawled >= 10)
                    {
                        yield return(null);

                        spacesCrawled = 0;
                    }
                }

                if (spacesCrawled > 0)
                {
                    yield return(null);

                    spacesCrawled = 0;
                }

                empty = FindEmptySquare(wallFloorContext, RNG);
            }

            // Add appropriate items to the tunnels list
            tunnelList.AddRange(crawlers.Select(c => c.AllPositions).Where(a => a.Count != 0), Name);
        }