コード例 #1
0
        public static int SidesOpened(this Tile.MoveType t)
        {
            if (0 != (t & Tile.MoveType.Open_HEX))
            {
                throw new NotImplementedException("don't support hex yet");
            }
            if (t == Tile.MoveType.Open_HORIZ)
            {
                return(4);
            }

            int sidesOpened = 0;

            if (0 != (t & Tile.MoveType.Open_NORTH))
            {
                ++sidesOpened;
            }
            if (0 != (t & Tile.MoveType.Open_EAST))
            {
                ++sidesOpened;
            }
            if (0 != (t & Tile.MoveType.Open_SOUTH))
            {
                ++sidesOpened;
            }
            if (0 != (t & Tile.MoveType.Open_WEST))
            {
                ++sidesOpened;
            }
            return(sidesOpened);
        }
コード例 #2
0
        /// <summary>
        /// Gets the opposite MoveType to the one specified. Can do most
        /// hex directionality, but will get confused with hex horiz vs
        /// non-hex horiz. Will have to create a new func later to handle
        /// that case (maybe with specified "preferHex" bool?)
        /// </summary>
        /// <param name="t">The MoveType for which to find the opposite</param>
        /// <returns>The opposite MoveType</returns>
        public static Tile.MoveType GetOpposite(this Tile.MoveType t)
        {
            switch (t)
            {
            case Tile.MoveType.Wall:
                return(Tile.MoveType.Open_ALL);

            case Tile.MoveType.Open_NORTH:
                return(Tile.MoveType.Open_SOUTH);

            case Tile.MoveType.Open_EAST:
                return(Tile.MoveType.Open_WEST);

            case Tile.MoveType.Open_SOUTH:
                return(Tile.MoveType.Open_NORTH);

            case Tile.MoveType.Open_WEST:
                return(Tile.MoveType.Open_EAST);

            case Tile.MoveType.Open_HORIZ:
                return(Tile.MoveType.Open_VERT);

            case Tile.MoveType.Open_UP:
                return(Tile.MoveType.Open_DOWN);

            case Tile.MoveType.Open_DOWN:
                return(Tile.MoveType.Open_UP);

            case Tile.MoveType.Open_VERT:
                return(Tile.MoveType.Open_HORIZ);

            case Tile.MoveType.Open_ALL:
                return(Tile.MoveType.Wall);

            case Tile.MoveType.Open_HEX_NW:
                return(Tile.MoveType.Open_HEX_SE);

            case Tile.MoveType.Open_HEX_NE:
                return(Tile.MoveType.Open_HEX_SW);

            case Tile.MoveType.Open_HEX_SE:
                return(Tile.MoveType.Open_HEX_NW);

            case Tile.MoveType.Open_HEX_SW:
                return(Tile.MoveType.Open_HEX_NE);

            case Tile.MoveType.Open_HEX_HORIZ:
                throw new NotSupportedException("Can't do that, sorry");

            default:
                throw new ArgumentException("Unknown Tile.MoveType");
            }
        }
コード例 #3
0
 public void SetAllToo(Tile.MoveType physics, bool[,] mask = null)
 {
     for (int y = 0; y < this.Height; ++y)
     {
         for (int x = 0; x < this.Width; ++x)
         {
             if (null == mask || mask[y, x])
             {
                 this[y, x].Physics = physics;
             }
         }
     }
 }
コード例 #4
0
 public Brush GetFillBrushFor(Tile.MoveType physics)
 {
     if (physics == Tile.MoveType.Wall)
     {
         return(this.WallTile_Brush);
     }
     if ((physics & Tile.MoveType.Open_ALL) != 0)
     {
         // Open the tile up, and assume the border drawing will handle
         // open-ness
         return(this.OpenTile_Brush);
     }
     return(new SolidBrush(this.Background_Color));
 }
コード例 #5
0
        /// <summary>
        /// Checks if a wall exists anywhere that prevents movement from the specified tile
        /// to the adjacent tile in the specified direction (including walls in that tile
        /// which would prevent movement). If the movement direction is not possible due to
        /// being at the edge of a map, returns true. If the specified tile is invalid,
        /// returns true.
        /// </summary>
        public bool WallExists(int x, int y, Tile.MoveType direction)
        {
            if (!TileIsValid(x, y))
            {
                return(true);
            }
            if (0 == (this[y, x].Physics & direction))
            {
                return(true);
            }
            switch (direction)
            {
            case Tile.MoveType.Wall:
                throw new ArgumentException();

            case Tile.MoveType.Open_NORTH:
                return(!TileIsValid(x, y - 1) || (this[y - 1, x].Physics & direction.GetOpposite()) == 0);

            case Tile.MoveType.Open_EAST:
                return(!TileIsValid(x + 1, y) || (this[y, x + 1].Physics & direction.GetOpposite()) == 0);

            case Tile.MoveType.Open_SOUTH:
                return(!TileIsValid(x, y + 1) || (this[y + 1, x].Physics & direction.GetOpposite()) == 0);

            case Tile.MoveType.Open_WEST:
                return(!TileIsValid(x - 1, y) || (this[y, x - 1].Physics & direction.GetOpposite()) == 0);

            case Tile.MoveType.Open_HORIZ:
                return(WallExists(x, y, Tile.MoveType.Open_NORTH) &&
                       WallExists(x, y, Tile.MoveType.Open_EAST) &&
                       WallExists(x, y, Tile.MoveType.Open_SOUTH) &&
                       WallExists(x, y, Tile.MoveType.Open_WEST));

            case Tile.MoveType.Open_UP:
            case Tile.MoveType.Open_DOWN:
            case Tile.MoveType.Open_VERT:
            case Tile.MoveType.Open_ALL:
            case Tile.MoveType.Open_HEX_NW:
            case Tile.MoveType.Open_HEX_NE:
            case Tile.MoveType.Open_HEX_SE:
            case Tile.MoveType.Open_HEX_SW:
            case Tile.MoveType.Open_HEX:
            case Tile.MoveType.Open_HEX_HORIZ:
            default:
                throw new NotImplementedException();
            }
        }
コード例 #6
0
 public void ResetTiles(int width, int height, Tile.MoveType startingPhyics = Tile.MoveType.Wall)
 {
     Tile[,] tiles = new Tile[height, width];
     for (int y = 0; y < height; ++y)
     {
         for (int x = 0; x < width; ++x)
         {
             tiles[y, x] = new Tile()
             {
                 Parent  = this,
                 Physics = startingPhyics
             };
         }
     }
     this.Tiles = tiles;
     this.IsHex = false; // TODO
 }
コード例 #7
0
 public DungeonTiles(int width, int height, Tile.MoveType startingPhyics = Tile.MoveType.Wall)
 {
     ResetTiles(width, height, startingPhyics);
 }
コード例 #8
0
        private void Divide(DungeonTiles d, Rectangle topRegion, bool[,] totalMask, bool[,] algMask, Random r)
        {
            if (null == d || null == topRegion || null == totalMask)
            {
                return;
            }
            Stack <Rectangle> subregions = new Stack <Rectangle>();

            subregions.Push(topRegion);
            if (null == r)
            {
                r = new Random();
            }

            while (subregions.Count > 0)
            {
                Rectangle currentRegion = subregions.Pop();

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

                if (currentRegion.Width == 1 || currentRegion.Height == 1)
                {
                    continue;
                }
                if (currentRegion.Width * currentRegion.Height <= roomSize)
                {
                    d.Parent.CreateGroup(currentRegion, TileCategory.Room);
                    continue;
                }

                bool doHoriz = DoUseHorizontalOrientation(currentRegion.Width, currentRegion.Height, r);

                // Origin of new wall - TODO is there more efficient math for this?
                int wx_offset_base = (int)((1.0 - Variability) / 2.0 * currentRegion.Width);
                int wy_offset_base = (int)((1.0 - Variability) / 2.0 * currentRegion.Height);
                int wx_offset_rand = ((int)Math.Floor(Variability * r.NextDouble() * (currentRegion.Width - 1)));
                int wy_offset_rand = ((int)Math.Floor(Variability * r.NextDouble() * (currentRegion.Height - 1)));
                int wx_offset      = wx_offset_base + wx_offset_rand;
                int wy_offset      = wy_offset_base + wy_offset_rand;
                int wx             = currentRegion.X + (doHoriz ? 0 : wx_offset);
                int wy             = currentRegion.Y + (doHoriz ? wy_offset : 0);

                // Directionality
                int dx = doHoriz ? 1 : 0;
                int dy = doHoriz ? 0 : 1;

                int len = doHoriz ? currentRegion.Width : currentRegion.Height;

                // Break new wall into as many sub-walls as are appropriate (based on mask)
                HashSet <List <Tile> > subWalls       = new HashSet <List <Tile> >();
                List <Tile>            currentSubWall = new List <Tile>();
                for (int i = 0; i < len; ++i, wx += dx, wy += dy)
                {
                    if (algMask[wy, wx] && (totalMask[wy, wx] || this.BuildStrategy == ExistingDataHandling.Ignore))
                    {
                        currentSubWall.Add(d[wy, wx]);
                    }
                    else
                    {
                        if (currentSubWall.Count > 0)
                        {
                            subWalls.Add(currentSubWall);
                            currentSubWall = new List <Tile>();
                        }
                        continue;
                    }
                }
                subWalls.Add(currentSubWall);

                Tile.MoveType newWalls = DetermineClosureMethod(doHoriz);

                // Build division wall, honoring mask & leaving a passage
                foreach (List <Tile> wall in subWalls)
                {
                    int passageIdx = r.Next(0, wall.Count);
                    for (int i = 0; i < wall.Count; ++i)
                    {
                        if (i == passageIdx)
                        {
                            continue;
                        }
                        wall[i].Physics = wall[i].Physics.CloseOff(newWalls);
                    }
                }

                // Now subdivide the two new regions
                Rectangle subregion1 = new Rectangle(
                    currentRegion.X,
                    currentRegion.Y,
                    doHoriz ? currentRegion.Width : wx - currentRegion.X + 1,
                    doHoriz ? wy - currentRegion.Y + 1 : currentRegion.Height);
                Rectangle subregion2 = new Rectangle(
                    doHoriz ? currentRegion.X : wx + 1,
                    doHoriz ? wy + 1 : currentRegion.Y,
                    doHoriz ? currentRegion.Width : currentRegion.X + currentRegion.Width - wx - 1,
                    doHoriz ? currentRegion.Y + currentRegion.Height - wy - 1 : currentRegion.Height);

                // Push the new subregions ont othe stack
                subregions.Push(subregion1);
                subregions.Push(subregion2);

                if (this.GroupForDebug)
                {
                    d.Parent.CreateGroup(subregion1);
                    d.Parent.CreateGroup(subregion2);
                }

                this.RunCallbacks(_ctx);
            }
        }
コード例 #9
0
 public static Tile.MoveType OpenUp(this Tile.MoveType t, int direction)
 {
     return(t.OpenUp((Tile.MoveType)direction));
 }
コード例 #10
0
 public static Tile.MoveType OpenUp(this Tile.MoveType t, Tile.MoveType direction)
 {
     return(t | direction);
 }
コード例 #11
0
 public static Tile.MoveType CloseOff(this Tile.MoveType t, int direction)
 {
     return(t.CloseOff((Tile.MoveType)direction));
 }
コード例 #12
0
 public static Tile.MoveType CloseOff(this Tile.MoveType t, Tile.MoveType direction)
 {
     return(t & ~direction);
 }
コード例 #13
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
        }
コード例 #14
0
        /// <summary>
        /// Draws the border for tile (x, y) in the specified DungeonTiles,
        /// to the specified graphics and rectangle coordinates.
        /// </summary>
        /// <param name="tiles">The DungeonTiles for which this border is being drawn</param>
        /// <param name="x">The x-location of the tile whose border is being drawn</param>
        /// <param name="y">The x-location of the tile whose border is being drawn</param>
        /// <param name="moveDir">The move direction. Currently supports a single horizontal square direction, or all horizontal square directions, but no other combinations</param>
        /// <param name="g">The graphics on which to draw</param>
        /// <param name="x1">x1 of the tile's square in graphics.</param>
        /// <param name="y1">y1 of the tile's square in graphics.</param>
        /// <param name="x2">x2 of the tile's square in graphics.</param>
        /// <param name="y2">y2 of the tile's square in graphics.</param>
        private void DrawBorderFor(DungeonTiles tiles, int x, int y, Tile.MoveType moveDir, Graphics g, int x1, int y1, int x2, int y2)
        {
            if (tiles == null || g == null)
            {
                return;                       // no op
            }
            if (!tiles.TileIsValid(x, y))
            {
                return;
            }

            int adjacentTile_x = 0,
                adjacentTile_y = 0;

            // Determine the correct adjacent tile to check physics
            switch (moveDir)
            {
            case Tile.MoveType.Wall:
                return; // no op

            case Tile.MoveType.Open_NORTH:
                adjacentTile_x = x;
                adjacentTile_y = y - 1;
                break;

            case Tile.MoveType.Open_EAST:
                adjacentTile_x = x + 1;
                adjacentTile_y = y;
                break;

            case Tile.MoveType.Open_SOUTH:
                adjacentTile_x = x;
                adjacentTile_y = y + 1;
                break;

            case Tile.MoveType.Open_WEST:
                adjacentTile_x = x - 1;
                adjacentTile_y = y;
                break;

            case Tile.MoveType.Open_HORIZ:
                DrawBorderFor(tiles, x, y, Tile.MoveType.Open_NORTH, g, x1, y1, x2, y2);
                DrawBorderFor(tiles, x, y, Tile.MoveType.Open_EAST, g, x1, y1, x2, y2);
                DrawBorderFor(tiles, x, y, Tile.MoveType.Open_SOUTH, g, x1, y1, x2, y2);
                DrawBorderFor(tiles, x, y, Tile.MoveType.Open_WEST, g, x1, y1, x2, y2);
                return;

            default:
                throw new NotImplementedException("Unsupported Tile.MoveType specified");
            }

            // 1. Check if THIS cell is even open
            bool useWall = ((tiles[y, x].Physics & moveDir) == 0);

            // 2. Check if adjacent cell exists; if not, use a wall border
            useWall = (useWall || !tiles.TileIsValid(adjacentTile_x, adjacentTile_y));
            // 3. If adjacent cell exists, check if its corresponding wall is opened too; if not, use a wall border
            useWall = (useWall || (tiles[adjacentTile_y, adjacentTile_x].Physics & moveDir.GetOpposite()) == 0);

            if (this.WallBorder_Pen == null || this.OpenBorder_Pen == null)
            {
                throw new Exception("DungeonTileRenderer is in an invalid state: set WallBorder_Pen and OpenBorder_Pen");
            }

            Pen borderPen = useWall ? this.WallBorder_Pen : this.OpenBorder_Pen;

            // All this work, just to draw a single line.
            switch (moveDir)
            {
            case Tile.MoveType.Open_NORTH:
                g.DrawLine(borderPen, x1, y1, x2, y1);
                break;

            case Tile.MoveType.Open_EAST:
                g.DrawLine(borderPen, x2, y1, x2, y2);
                break;

            case Tile.MoveType.Open_SOUTH:
                g.DrawLine(borderPen, x1, y2, x2, y2);
                break;

            case Tile.MoveType.Open_WEST:
                g.DrawLine(borderPen, x1, y1, x1, y2);
                break;

            default:
                throw new ArgumentException("Unsupported moveDir to draw border");
            }
        }