public void Fill(Tileset tileset, PointSelection selection, int x, int y)
        {
            if (x < 0 || x > Width - 1 || y < 0 || y > Height - 1) return;

            using (UndoRedoArea.Start("Fill Tool"))
            {
                _tileChecked = new bool[Width, Height];
                _tileMap = SelectedLayer.Tiles;
                _tilesToCache = new List<Point>();

                var tile = selection.GetTopLeftMostPoint();
                var replacementTile = new FillTile(tileset.ID, tile.X, tile.Y, 0);
                var currTile = GetFillTile(x, y, replacementTile);
                var originalFillTile = new FillTile(currTile.Tileset, currTile.SrcX, currTile.SrcY, currTile.Terrain);

                FillTiles(currTile, originalFillTile, replacementTile);

                UndoRedoArea.Commit();
            }
        }
        /// <summary>
        /// Runs a flood-fill algorithm with the passed co-ordinates of the root.
        /// Returns an enumerable list of points based on the results.
        /// </summary>
        /// <param name="tileset">Tileset to replace with.</param>
        /// <param name="selection">Tile selection to replace with.</param>
        /// <param name="x">X co-ordinate of the root tile.</param>
        /// <param name="y">Y co-ordinate of the root tile.</param>
        /// <returns>An enumerable list of points.</returns>
        public List<Point> FillCache(Tileset tileset, PointSelection selection, int x, int y)
        {
            if (x < 0 || x > Width - 1 || y < 0 || y > Height - 1) return null;

            _tileCache = new List<Point>();

            _cacheMap = new FillTile[Width, Height];
            for (var x2 = 0; x2 < Width; x2++)
            {
                for (var y2 = 0; y2 < Height; y2++)
                {
                    var tile = SelectedLayer.Tiles[x2, y2];
                    _cacheMap[x2, y2] = new FillTile(tile.Tileset, tile.SrcX, tile.SrcY, tile.Terrain);
                }
            }

            _tileChecked = new bool[Width, Height];

            var point = selection.GetTopLeftMostPoint();
            var replacementTile = new FillTile(tileset.ID, point.X, point.Y, 0);
            var currTile = GetFillCacheTile(x, y, replacementTile);
            var originalFillTile = new FillTile(currTile.Tileset, currTile.X, currTile.Y, currTile.Terrain);

            FillCacheTiles(originalFillTile, replacementTile, x, y);

            return _tileCache;
        }
 /// <summary>
 /// Compares two FillTile objects.
 /// </summary>
 /// <param name="targetTile">Object to compare against.</param>
 /// <param name="replacementTile">Object to compare to.</param>
 /// <returns>True if tiles match, false if not.</returns>
 public bool CompareCacheTile(FillTile targetTile, FillTile replacementTile)
 {
     return targetTile.Tileset == replacementTile.Tileset && targetTile.X == replacementTile.X && targetTile.Y == replacementTile.Y && targetTile.Terrain == replacementTile.Terrain;
 }
 public bool CompareTile(EditorTile targetTile, FillTile replacementTile)
 {
     return targetTile.Tileset == replacementTile.Tileset && targetTile.SrcX == replacementTile.X && targetTile.SrcY == replacementTile.Y && targetTile.Terrain == replacementTile.Terrain;
 }
 private EditorTile GetFillTile(int x, int y, FillTile fillTile)
 {
     var tile = _tileMap[x, y] ?? new EditorTile(fillTile.Tileset, x, y, fillTile.X, fillTile.Y, 0);
     return tile;
 }
 /// <summary>
 /// If a cache map tile doesn't exist, it'll create a new fill tile.
 /// </summary>
 /// <param name="x">X co-ordinate of the tile.</param>
 /// <param name="y">Y co-ordinate of the tile.</param>
 /// <param name="fillTile">Tile to replace with.</param>
 /// <returns>Returns a generated FillTile object.</returns>
 private FillTile GetFillCacheTile(int x, int y, FillTile fillTile)
 {
     var tile = _cacheMap[x, y] ?? new FillTile(fillTile.Tileset, x, y, 0);
     return tile;
 }
        private void FillTiles(EditorTile tile, FillTile originalTile, FillTile replacementTile)
        {
            var open = new Queue<Point>();
            open.Enqueue(new Point(tile.X, tile.Y));

            while (open.Count > 0)
            {
                var current = open.Dequeue();
                var x = current.X;
                var y = current.Y;

                CheckFillTile(open, x, y, originalTile, replacementTile);

                if (x > 0)
                    CheckFillTile(open, x - 1, y, originalTile, replacementTile);
                if (x < Width - 1)
                    CheckFillTile(open, x + 1, y, originalTile, replacementTile);
                if (y > 0)
                    CheckFillTile(open, x, y - 1, originalTile, replacementTile);
                if (y < Height - 1)
                    CheckFillTile(open, x, y + 1, originalTile, replacementTile);
            }

            SelectedLayer.Tiles = _tileMap;

            foreach (var tmp in SelectedLayer.Tiles)
            {
                tmp.PropertyChanged += Tile_PropertyChanged;
            }

            foreach (var point in _tilesToCache)
            {
                CacheTile(SelectedLayer, SelectedLayer.Tiles[point.X, point.Y]);
            }
        }
        /// <summary>
        /// Begins the actual flood-fill algorithm. Should only be called from the <see cref="FillCache"/> method.
        /// </summary>
        /// <param name="originalTile">Root tile to check.</param>
        /// <param name="replacementTile">Tile to replace all tiles with.</param>
        /// <param name="destx">Root X co-ordinate.</param>
        /// <param name="desty">Root Y co-ordinate.</param>
        private void FillCacheTiles(FillTile originalTile, FillTile replacementTile, int destx, int desty)
        {
            var open = new Queue<Point>();
            open.Enqueue(new Point(destx, desty));

            while (open.Count > 0)
            {
                var current = open.Dequeue();
                var x = current.X;
                var y = current.Y;

                CheckCacheFillTile(open, x, y, originalTile, replacementTile);

                if (x > 0)
                    CheckCacheFillTile(open, x - 1, y, originalTile, replacementTile);
                if (x < Width - 1)
                    CheckCacheFillTile(open, x + 1, y, originalTile, replacementTile);
                if (y > 0)
                    CheckCacheFillTile(open, x, y - 1, originalTile, replacementTile);
                if (y < Height - 1)
                    CheckCacheFillTile(open, x, y + 1, originalTile, replacementTile);
            }
        }
        private void CheckFillTile(Queue<Point> open, int x, int y, FillTile originalTile, FillTile replacementTile)
        {
            if (_tileChecked[x, y]) return;
            var currTile = GetFillTile(x, y, replacementTile);
            if (CompareTile(currTile, originalTile))
            {
                currTile.Tileset = replacementTile.Tileset;
                currTile.SrcX = replacementTile.X;
                currTile.SrcY = replacementTile.Y;
                currTile.X = x;
                currTile.Y = y;
                currTile.Terrain = replacementTile.Terrain;

                _tilesToCache.Add(new Point(x, y));

                _tileChecked[x, y] = true;
                open.Enqueue(new Point(x, y));
            }
        }