Beispiel #1
0
        protected override void _runAlgorithm(IAlgorithmContext context)
        {
            DungeonTiles workingTiles = context.D.Tiles;

            bool[,] algMask = context.Mask;
            _ctx            = context;

            if (this.WallStrategy == WallFormation.Tiles)
            {
                throw new NotImplementedException();
            }

            bool[,] existingDataMask = new bool[workingTiles.Height, workingTiles.Width];

            for (int y = 0; y < workingTiles.Height; ++y)
            {
                for (int x = 0; x < workingTiles.Width; ++x)
                {
                    existingDataMask[y, x] = algMask[y, x] && (workingTiles[y, x].Physics == Tile.MoveType.Wall);
                }
            }

            // Prime the dungeon tiles by opening them all up (as appropriate).
            // "Ignore" shouldn't wipe existing data, but...
            switch (BuildStrategy)
            {
            case ExistingDataHandling.Ignore:
            case ExistingDataHandling.Avoid:
                workingTiles.SetAllToo(Tile.MoveType.Open_HORIZ, existingDataMask);
                break;

            case ExistingDataHandling.Overwrite:
                workingTiles.SetAllToo(Tile.MoveType.Open_HORIZ, algMask);
                break;

            default:
                throw new NotImplementedException();
            }

            // Run algorithm with the appropriate mask
            // ... "Ignore" SHOULD build walls over existing data
            Rectangle startRegion = new Rectangle(0, 0, workingTiles.Width, workingTiles.Height);

            switch (BuildStrategy)
            {
            case ExistingDataHandling.Avoid:
                this.Divide(workingTiles, startRegion, existingDataMask, algMask, context.R);
                break;

            case ExistingDataHandling.Ignore:
            case ExistingDataHandling.Overwrite:
                this.Divide(workingTiles, startRegion, algMask, algMask, context.R);
                break;

            default:
                throw new NotImplementedException();
            }

            _ctx = null;
        }
Beispiel #2
0
        protected override void _runAlgorithm(IAlgorithmContext context)
        {
            DungeonTiles workingTiles = context.D.Tiles;

            // Implemented via http://weblog.jamisbuck.org/2015/1/15/better-recursive-division-algorithm

            if (this.WallStrategy != WallFormation.Boundaries)
            {
                throw new NotImplementedException();
            }

            // CLOBBER!
            workingTiles.SetAllToo(Tile.MoveType.Open_HORIZ, context.Mask);

            Stack <Subregion> subregions = new Stack <Subregion>();

            // 1. Collect all the cells in the maze into a single region.
            Subregion topRegion = new Subregion(workingTiles.GetTilesIn(context.Mask));

            subregions.Push(topRegion);

            while (subregions.Count > 0)
            {
                Subregion parentRegion = subregions.Pop();

                int roomSize = this.RoomSize - (this.RoomSizeVariability / 2) + context.R.Next(this.RoomSizeVariability);
                roomSize = roomSize < 1 ? 1 : roomSize;

                if (parentRegion.Tiles.Count <= roomSize)
                {
                    if (roomSize > 1)
                    {
                        if (this.GroupRooms)
                        {
                            workingTiles.Parent.CreateGroup(parentRegion.Tiles.ToHashSet(), TileCategory.Room);
                        }
                    }
                    continue;
                }

                // 2. Split the region into two, using the following process:
                List <Tile> S = new List <Tile>();
                Dictionary <Tile, Subregion_Split> tileSubregions = new Dictionary <Tile, Subregion_Split>();
                foreach (Tile t in parentRegion.Tiles)
                {
                    tileSubregions[t] = Subregion_Split.NONE;
                }

                //   2.1  Choose two cells from the region at random as “seeds”.
                //        Identify one as subregion A and one as subregion B.
                //        Then put them into a set S.
                Tile randomSeed_A = parentRegion.Tiles.PickRandomly(context.R);
                Tile randomSeed_B = randomSeed_A; // Shouldn't be equal when we're done
                while (randomSeed_A == randomSeed_B)
                {
                    randomSeed_B = parentRegion.Tiles.PickRandomly(context.R);
                }
                tileSubregions[randomSeed_A] = Subregion_Split.A;
                tileSubregions[randomSeed_B] = Subregion_Split.B;
                S.Add(randomSeed_A);
                S.Add(randomSeed_B);

                while (S.Count > 0)
                {
                    //   2.2  Choose a cell at random from S. Remove it from the set.
                    Tile currentTile = S.PullRandomly(context.R);

                    //   2.3  For each of that cell’s neighbors, if the neighbor
                    //        is not already associated with a subregion, add it to S,
                    //        and associate it with the same subregion as the cell itself.
                    IList <Tile> nextAdjacents = currentTile
                                                 .GetAdjacents()
                                                 .Where(t => parentRegion.Tiles.Contains(t))
                                                 .ToList();
                    foreach (Tile t in nextAdjacents)
                    {
                        if (tileSubregions[t] == Subregion_Split.NONE)
                        {
                            S.Add(t);
                            tileSubregions[t] = tileSubregions[currentTile];
                        }
                    }
                } //   2.4  Repeat 2.2 and 2.3 until the entire region has been split into two.

                // 3. Construct a wall between the two regions by identifying cells
                //    in one region that have neighbors in the other region. Leave a
                //    gap by omitting the wall from one such cell pair.
                List <Tuple <Tile, Tile> > wallBoundaries = new List <Tuple <Tile, Tile> >();

                wallBoundaries.AddRange(
                    tileSubregions.Keys
                    // Safe to filter with hard-coded A/B due to associative property
                    .Where(k => tileSubregions[k] == Subregion_Split.A)
                    .SelectMany <Tile, Tuple <Tile, Tile> >(
                        t => t.GetAdjacents()
                        .Where(adj => parentRegion.Tiles.Contains(adj))
                        .Where(sr => tileSubregions[sr] == Subregion_Split.B)
                        .Select(t2 => new Tuple <Tile, Tile>(t, t2))));

                // Leave as many gaps opened as requested
                int maxGaps = Math.Min(
                    this.GapCount,
                    (int)Math.Ceiling(wallBoundaries.Count * MaxGapProportion));
                for (int i = 0; i < maxGaps; ++i)
                {
                    wallBoundaries.PullRandomly(context.R);
                }

                foreach (Tuple <Tile, Tile> pair in wallBoundaries)
                {
                    Tile.MoveType touchingBoundary = workingTiles.GetCardinality(pair.Item1, pair.Item2);
                    pair.Item1.Physics = pair.Item1.Physics.CloseOff(touchingBoundary);
                }

                ISet <Tile> subregion_A = parentRegion.Tiles.Where(t => tileSubregions[t] == Subregion_Split.A).ToHashSet();
                ISet <Tile> subregion_B = parentRegion.Tiles.Where(t => tileSubregions[t] == Subregion_Split.B).ToHashSet();

                if (this.GroupForDebug)
                {
                    workingTiles.Parent.CreateGroup(subregion_A);
                    workingTiles.Parent.CreateGroup(subregion_B);
                }

                this.RunCallbacks(context);

                // Push new subregions to the stack
                subregions.Push(new Subregion(subregion_A));
                subregions.Push(new Subregion(subregion_B));
            } // 4. Repeat 2 and 3 for each subregion
        }
Beispiel #3
0
        protected override void _runAlgorithm(IAlgorithmContext context)
        {
            DungeonTiles workingTiles = context.D.Tiles;

            bool[,] algMask = context.Mask;

            if (this.ClearArea)
            {
                workingTiles.SetAllToo(Tile.MoveType.Wall, algMask);
            }

            bool[,] isExplored = (bool[, ])algMask.Clone();
            for (int y = 0; y < workingTiles.Height; ++y)
            {
                for (int x = 0; x < workingTiles.Width; ++x)
                {
                    isExplored[y, x] = !algMask[y, x];
                }
            }

            // If appropriate, mask out already-opened tiles
            if (!this.ClearArea && this.AvoidOpen)
            {
                for (int y = 0; y < workingTiles.Height; ++y)
                {
                    for (int x = 0; x < workingTiles.Width; ++x)
                    {
                        if (algMask[y, x] && workingTiles[y, x].Physics != Tile.MoveType.Wall)
                        {
                            isExplored[y, x] = true;
                        }
                    }
                }
            }

            // Create a pool of origins
            List <Point> unmaskedPoints = new List <Point>();

            for (int y = 0; y < isExplored.GetLength(0); ++y)
            {
                for (int x = 0; x < isExplored.GetLength(1); ++x)
                {
                    // If it's not masked out, add it to the pool of potential
                    // starting points
                    if (!isExplored[y, x])
                    {
                        unmaskedPoints.Add(new Point(x, y));
                    }
                }
            }

            // Only odd coordinates are valid starts due to limitations of algorithm below
            Predicate <Point> oddPoints          = p => p.X % 2 != 0 && p.Y % 2 != 0;
            Predicate <Point> paddedWithinBorder = p => p.X > BorderPadding && p.Y > BorderPadding && p.X < isExplored.GetLength(1) - BorderPadding && p.Y < isExplored.GetLength(0) - BorderPadding;
            List <Point>      originPool         = unmaskedPoints.FindAll(oddPoints).FindAll(paddedWithinBorder);

            if (originPool.Count == 0)
            {
                return;
            }

            int currentRooms   = 0;
            int currentAttempt = 0;

            while (++currentAttempt <= this.Attempts && currentRooms < this.TargetRoomCount)
            {
                int   originIdx  = context.R.Next(originPool.Count);
                Point nextOrigin = originPool[originIdx];

                int y = nextOrigin.Y;
                int x = nextOrigin.X;
                int w = context.R.Next(this.RoomWidthMin, this.RoomWidthMax) - 1 | 0x1;
                int h = context.R.Next(this.RoomHeightMin, this.RoomHeightMax) - 1 | 0x1;

                if (!workingTiles.TileIsValid(x, y) || !workingTiles.TileIsValid(x, y + h) || !workingTiles.TileIsValid(x + w, y) || !workingTiles.TileIsValid(x + w, y + h))
                {
                    continue;
                }

                bool overlapsOrAdjacent = false;
                for (int nuY = y - 1; nuY < y + h + 1; ++nuY)
                {
                    for (int nuX = x - 1; nuX < x + w + 1; ++nuX)
                    {
                        if (!workingTiles.TileIsValid(nuX, nuY) || isExplored[nuY, nuX])
                        {
                            overlapsOrAdjacent = true;
                            break;
                        }
                    }
                    if (overlapsOrAdjacent)
                    {
                        break;
                    }
                }
                if (overlapsOrAdjacent)
                {
                    continue;             // Failed attempt
                }
                ISet <Tile> newRoom = new HashSet <Tile>();
                for (int nuY = y; nuY < y + h; ++nuY)
                {
                    for (int nuX = x; nuX < x + w; ++nuX)
                    {
                        isExplored[nuY, nuX]           = true;
                        workingTiles[nuY, nuX].Physics = workingTiles[nuY, nuX].Physics.OpenUp(Tile.MoveType.Open_HORIZ);
                        originPool.Remove(workingTiles[nuY, nuX].Location);
                        newRoom.Add(workingTiles[nuY, nuX]);
                    }
                }
                // Close off boundaries if appropriate
                if (this.WallStrategy == WallFormation.Boundaries)
                {
                    for (int nuY = y; nuY < y + h; ++nuY)
                    {
                        for (int nuX = x; nuX < x + w; ++nuX)
                        {
                            if (nuY == y)
                            {
                                workingTiles[nuY, nuX].Physics = workingTiles[nuY, nuX].Physics.CloseOff(Tile.MoveType.Open_NORTH);
                            }
                            if (nuX == x)
                            {
                                workingTiles[nuY, nuX].Physics = workingTiles[nuY, nuX].Physics.CloseOff(Tile.MoveType.Open_WEST);
                            }
                            if (nuY == y + h - 1)
                            {
                                workingTiles[nuY, nuX].Physics = workingTiles[nuY, nuX].Physics.CloseOff(Tile.MoveType.Open_SOUTH);
                            }
                            if (nuX == x + w - 1)
                            {
                                workingTiles[nuY, nuX].Physics = workingTiles[nuY, nuX].Physics.CloseOff(Tile.MoveType.Open_EAST);
                            }
                        }
                    }
                }

                // Rooms should not be orphaned!
                workingTiles.Parent.CreateGroup(newRoom, TileCategory.Room);

                this.RunCallbacks(context);

                ++currentRooms;
            }
        }