public bool CompareTile(EditorTile targetTile, FillTile replacementTile)
 {
     return targetTile.Tileset == replacementTile.Tileset && targetTile.SrcX == replacementTile.X && targetTile.SrcY == replacementTile.Y && targetTile.Terrain == replacementTile.Terrain;
 }
        /// <summary>
        /// Re-sizes the map based on the anchor and size.
        /// </summary>
        /// <param name="width">New width of the map.</param>
        /// <param name="height">New height of the map.</param>
        /// <param name="tilewidth">New width of the tiles.</param>
        /// <param name="tileheight">New height of the tiles.</param>
        /// <param name="anchor">Anchor point to resize around.</param>
        public void ResizeMap(int width, int height, int tilewidth, int tileheight, AnchorPoints anchor)
        {
            if (tilewidth != TileWidth) TileWidth = tilewidth;
            if (tileheight != TileHeight) TileHeight = tileheight;

            if (Width == width && Height == height) return;

            using (UndoRedoArea.Start("Map Size Changed"))
            {
                /*
                 * The actual re-sizing the array (when it's actually resized) is in the
                 * property changed events for the "Width" and "Height" properties.
                 *
                 * Here we'll simply move the tiles around based on the anchor point passed
                 * by the user.
                 *
                 * We cannot destroy objects, so we'll be copying over data between tiles
                 * directly in to the tile properties. This allows us to retain the undo/redo
                 * stack of each tile without moving the objects themselves.
                 *
                 * After that, setting the Width and Height properties will cause the array
                 * itself to resize (or not, depending) and pass through the changes
                 * to the ScrollMap controls accordingly.
                 *
                 * If the new width is less than the old one, then we'll move the tiles
                 * before changing the size.
                 *
                 * If the new width is more than the old one, then we'll change the size
                 * and then move the tiles.
                 *
                 */

                var oldWidth = Width;
                var oldHeight = Height;

                var difference = 0;

                _building = true;

                if (width < oldWidth)
                {
                    switch (anchor)
                    {
                        case AnchorPoints.Right:
                        case AnchorPoints.UpRight:
                        case AnchorPoints.DownRight:
                            difference = oldWidth - width;
                            foreach (var layer in Layers)
                            {
                                for (int x = 0; x < oldWidth; x++)
                                {
                                    for (int y = 0; y < oldHeight; y++)
                                    {
                                        if (x < oldWidth - difference)
                                        {
                                            var tile = layer.Tiles[x + difference, y];
                                            layer.Tiles[x, y].SrcX = tile.SrcX;
                                            layer.Tiles[x, y].SrcY = tile.SrcY;
                                            layer.Tiles[x, y].Terrain = tile.Terrain;
                                            layer.Tiles[x, y].Tileset = tile.Tileset;
                                        }
                                    }
                                }
                            }
                            break;
                        case AnchorPoints.Center: case AnchorPoints.Up: case AnchorPoints.Down:
                            difference = (oldWidth / 2) - (width / 2);
                            foreach (var layer in Layers)
                            {
                                for (int x = 0; x < oldWidth; x++)
                                {
                                    for (int y = 0; y < oldHeight; y++)
                                    {
                                        if (x < oldWidth - (oldWidth - width))
                                        {
                                            var tile = layer.Tiles[x + difference, y];
                                            layer.Tiles[x, y].SrcX = tile.SrcX;
                                            layer.Tiles[x, y].SrcY = tile.SrcY;
                                            layer.Tiles[x, y].Terrain = tile.Terrain;
                                            layer.Tiles[x, y].Tileset = tile.Tileset;
                                        }
                                    }
                                }
                            }
                            break;
                    }
                }

                if (height < oldHeight)
                {
                    switch (anchor)
                    {
                        case AnchorPoints.Down:
                        case AnchorPoints.DownRight:
                        case AnchorPoints.DownLeft:
                            difference = oldHeight - height;
                            foreach (var layer in Layers)
                            {
                                for (int x = 0; x < oldWidth; x++)
                                {
                                    for (int y = 0; y < oldHeight; y++)
                                    {
                                        if (y < oldHeight - difference)
                                        {
                                            var tile = layer.Tiles[x, y + difference];
                                            layer.Tiles[x, y].SrcX = tile.SrcX;
                                            layer.Tiles[x, y].SrcY = tile.SrcY;
                                            layer.Tiles[x, y].Terrain = tile.Terrain;
                                            layer.Tiles[x, y].Tileset = tile.Tileset;
                                        }
                                    }
                                }
                            }
                            break;
                        case AnchorPoints.Center:
                        case AnchorPoints.Left:
                        case AnchorPoints.Right:
                            difference = (oldHeight / 2) - (height / 2);
                            foreach (var layer in Layers)
                            {
                                for (int x = 0; x < oldWidth; x++)
                                {
                                    for (int y = 0; y < oldHeight; y++)
                                    {
                                        if (y < oldHeight - (oldHeight - height))
                                        {
                                            var tile = layer.Tiles[x, y + difference];
                                            layer.Tiles[x, y].SrcX = tile.SrcX;
                                            layer.Tiles[x, y].SrcY = tile.SrcY;
                                            layer.Tiles[x, y].Terrain = tile.Terrain;
                                            layer.Tiles[x, y].Tileset = tile.Tileset;
                                        }
                                    }
                                }
                            }
                            break;
                    }
                }

                Width = width;
                Height = height;

                // resize

                foreach (var layer in Layers)
                {
                    if (width > layer.Tiles.GetLength(0) || height > layer.Tiles.GetLength(1))
                    {
                        EditorTile[,] tmpArray = layer.Tiles;

                        var newwidth = Math.Max(width, layer.Tiles.GetLength(0));
                        var newheight = Math.Max(height, layer.Tiles.GetLength(1));

                        layer.Tiles = new EditorTile[newwidth, newheight];

                        layer.Sprites = new Sprite[width, height];
                        layer.TerrainCache = new TerrainCache[width, height];

                        for (int x = 0; x < newwidth; x++)
                        {
                            for (int y = 0; y < newheight; y++)
                            {
                                if (x >= tmpArray.GetLength(0) || y >= tmpArray.GetLength(1))
                                {
                                    var tile = new EditorTile(x, y, 0, 0, 0, 0, layer);
                                    layer.Tiles[x, y] = tile;
                                    layer.Tiles[x, y].PropertyChanged += Tile_PropertyChanged;
                                }
                                else
                                {
                                    layer.Tiles[x, y] = tmpArray[x, y];
                                    layer.Tiles[x, y].X = x;
                                    layer.Tiles[x, y].Y = y;
                                }
                            }
                        }
                    }

                    if (width < layer.Tiles.GetLength(0) || height < layer.Tiles.GetLength(1))
                    {
                        var arrwidth = layer.Tiles.GetLength(0);
                        var arrheight = layer.Tiles.GetLength(1);
                        var newwidth = Math.Min(width, arrwidth);
                        var newheight = Math.Min(height, arrheight);

                        for (int x = 0; x < arrwidth; x++)
                        {
                            for (int y = 0; y < arrheight; y++)
                            {
                                if (x > newwidth || y > newheight)
                                {
                                    layer.Tiles[x, y].SrcX = 0;
                                    layer.Tiles[x, y].SrcY = 0;
                                    layer.Tiles[x, y].Terrain = 0;
                                    layer.Tiles[x, y].Tileset = 0;
                                }
                            }
                        }
                    }
                }

                // end resize

                if (width > oldWidth)
                {
                    switch (anchor)
                    {
                        case AnchorPoints.Right:
                        case AnchorPoints.UpRight:
                        case AnchorPoints.DownRight:
                            difference = width - oldWidth;
                            foreach (var layer in Layers)
                            {
                                for (int x = width - 1; x > 0; x--)
                                {
                                    for (int y = 0; y < Height; y++)
                                    {
                                        if (x - difference >= 0)
                                        {
                                            var tile = layer.Tiles[x - difference, y];
                                            layer.Tiles[x, y].SrcX = tile.SrcX;
                                            layer.Tiles[x, y].SrcY = tile.SrcY;
                                            layer.Tiles[x, y].Terrain = tile.Terrain;
                                            layer.Tiles[x, y].Tileset = tile.Tileset;
                                        }
                                    }
                                }
                                for (int x = 0; x < difference; x++)
                                {
                                    for (int y = 0; y < Height; y++)
                                    {
                                        layer.Tiles[x, y].SrcX = 0;
                                        layer.Tiles[x, y].SrcY = 0;
                                        layer.Tiles[x, y].Terrain = 0;
                                        layer.Tiles[x, y].Tileset = 0;
                                    }
                                }
                            }
                            break;
                        case AnchorPoints.Center:
                        case AnchorPoints.Up:
                        case AnchorPoints.Down:
                            difference = (width / 2) - (oldWidth / 2);
                            foreach (var layer in Layers)
                            {
                                for (int x = width - 1; x > 0; x--)
                                {
                                    for (int y = 0; y < Height; y++)
                                    {
                                        if (x - difference >= 0)
                                        {
                                            var tile = layer.Tiles[x - difference, y];
                                            layer.Tiles[x, y].SrcX = tile.SrcX;
                                            layer.Tiles[x, y].SrcY = tile.SrcY;
                                            layer.Tiles[x, y].Terrain = tile.Terrain;
                                            layer.Tiles[x, y].Tileset = tile.Tileset;
                                        }
                                    }
                                }
                                for (int x = 0; x < difference; x++)
                                {
                                    for (int y = 0; y < Height; y++)
                                    {
                                        layer.Tiles[x, y].SrcX = 0;
                                        layer.Tiles[x, y].SrcY = 0;
                                        layer.Tiles[x, y].Terrain = 0;
                                        layer.Tiles[x, y].Tileset = 0;
                                    }
                                }
                            }
                            break;
                    }
                }

                if (height > oldHeight)
                {
                    switch (anchor)
                    {
                        case AnchorPoints.Down:
                        case AnchorPoints.DownRight:
                        case AnchorPoints.DownLeft:
                            difference = height - oldHeight;
                            foreach (var layer in Layers)
                            {
                                for (int x = 0; x < Width; x++)
                                {
                                    for (int y = height - 1; y > 0; y--)
                                    {
                                        if (y - difference >= 0)
                                        {
                                            var tile = layer.Tiles[x, y - difference];
                                            layer.Tiles[x, y].SrcX = tile.SrcX;
                                            layer.Tiles[x, y].SrcY = tile.SrcY;
                                            layer.Tiles[x, y].Terrain = tile.Terrain;
                                            layer.Tiles[x, y].Tileset = tile.Tileset;
                                        }
                                    }
                                }
                                for (int x = 0; x < Width; x++)
                                {
                                    for (int y = 0; y < difference; y++)
                                    {
                                        layer.Tiles[x, y].SrcX = 0;
                                        layer.Tiles[x, y].SrcY = 0;
                                        layer.Tiles[x, y].Terrain = 0;
                                        layer.Tiles[x, y].Tileset = 0;
                                    }
                                }
                            }
                            break;
                        case AnchorPoints.Center:
                        case AnchorPoints.Left:
                        case AnchorPoints.Right:
                            difference = (height / 2) - (oldHeight / 2);
                            foreach (var layer in Layers)
                            {
                                for (int x = 0; x < Width; x++)
                                {
                                    for (int y = height - 1; y > 0; y--)
                                    {
                                        if (y - difference >= 0)
                                        {
                                            var tile = layer.Tiles[x, y - difference];
                                            layer.Tiles[x, y].SrcX = tile.SrcX;
                                            layer.Tiles[x, y].SrcY = tile.SrcY;
                                            layer.Tiles[x, y].Terrain = tile.Terrain;
                                            layer.Tiles[x, y].Tileset = tile.Tileset;
                                        }
                                    }
                                }
                                for (int x = 0; x < Width; x++)
                                {
                                    for (int y = 0; y < difference; y++)
                                    {
                                        layer.Tiles[x, y].SrcX = 0;
                                        layer.Tiles[x, y].SrcY = 0;
                                        layer.Tiles[x, y].Terrain = 0;
                                        layer.Tiles[x, y].Tileset = 0;
                                    }
                                }
                            }
                            break;
                    }
                }

                UnsavedChanges = true;

                _building = false;

                CacheAllTiles();
                SizeChanged.Invoke(this, new EventArgs());

                UndoRedoArea.Commit();
            };
        }
        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>
        /// Event handler for the LayerAdded event of the <see cref="EditorTileMap"/> object.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="LayerAddedEventArgs"/> instance containing the event data.</param>
        private void EditorMap_LayerAdded(object sender, LayerAddedEventArgs e)
        {
            e.Layer.Tiles = new EditorTile[Width, Height];
            e.Layer.Sprites = new Sprite[Width, Height];
            e.Layer.TerrainCache = new TerrainCache[Width, Height];

            for (int x = 0; x < Width; x++)
            {
                for (int y = 0; y < Height; y++)
                {
                    var tile = new EditorTile(x, y, 0, 0, 0, 0, e.Layer);
                    e.Layer.Tiles[x, y] = tile;
                    e.Layer.Tiles[x, y].PropertyChanged += Tile_PropertyChanged;
                }
            }

            e.Layer.PropertyChanged += Layer_PropertyChanged;
        }
        /// <summary>
        /// Caches an individual tile's sprite.
        /// </summary>
        /// <param name="layer">Layer of the tile.</param>
        /// <param name="tile">Tile to cache.</param>
        private void CacheTile(EditorTileLayer layer, EditorTile tile)
        {
            layer.TerrainCache[tile.X, tile.Y] = null;

            var tileset = TilesetManager.Instance.GetTileset(tile.Tileset);
            if (tileset == null)
            {
                layer.Sprites[tile.X, tile.Y] = null;
                return;
            }

            layer.Sprites[tile.X, tile.Y] = new Sprite
            {
                Texture = ResourceManager.Instance.LoadTexture(tileset.Image),
                Position = new Vector2f(tile.X * tileset.TileWidth, tile.Y * tileset.TileHeight),
                TextureRect = new IntRect(tile.SrcX * tileset.TileWidth, tile.SrcY * tileset.TileHeight, tileset.TileWidth, tileset.TileHeight)
            };
        }
        /// <summary>
        /// Caches an individual terrain tile's sprite.
        /// </summary>
        /// <param name="layer">Layer of the tile.</param>
        /// <param name="tile">Tile to cache.</param>
        private void CacheTerrainTile(EditorTileLayer layer, EditorTile tile)
        {
            layer.Sprites[tile.X, tile.Y] = null;

            var terrain = TerrainManager.Instance.GetTerrain(tile.Terrain);
            if (terrain == null)
            {
                layer.TerrainCache[tile.X, tile.Y] = null;
                return;
            }

            layer.TerrainCache[tile.X, tile.Y] = new TerrainCache();

            EditorTile NW; EditorTile N; EditorTile NE; EditorTile E; EditorTile SE; EditorTile S; EditorTile SW; EditorTile W;

            EditorTile forcedTile;
            if (!UndoRedoManager.IsCommandStarted && !UndoRedoArea.IsCommandStarted)
            {
                using (UndoRedoManager.Start("Creating fake tile"))
                {
                    forcedTile = new EditorTile(0, 0, 0, 0, 0, tile.Terrain);
                    UndoRedoManager.Commit();
                }
            }
            else
            {
                forcedTile = new EditorTile(0, 0, 0, 0, 0, tile.Terrain);
            }

            if (!(tile.X - 1 < 0 || tile.Y - 1 < 0))
                NW = layer.Tiles[tile.X - 1, tile.Y - 1];
            else
                NW = forcedTile;
            if (!(tile.Y - 1 < 0))
                N = layer.Tiles[tile.X, tile.Y - 1];
            else
                N = forcedTile;
            if (!(tile.X + 1 > Width - 1 || tile.Y - 1 < 0))
                NE = layer.Tiles[tile.X + 1, tile.Y - 1];
            else
                NE = forcedTile;
            if (!(tile.X + 1 > Width - 1))
                E = layer.Tiles[tile.X + 1, tile.Y];
            else
                E = forcedTile;
            if (!(tile.X + 1 > Width - 1 || tile.Y + 1 > Height - 1))
                SE = layer.Tiles[tile.X + 1, tile.Y + 1];
            else
                SE = forcedTile;
            if (!(tile.Y + 1 > Height - 1))
                S = layer.Tiles[tile.X, tile.Y + 1];
            else
                S = forcedTile;
            if (!(tile.X - 1 < 0 || tile.Y + 1 > Height - 1))
                SW = layer.Tiles[tile.X - 1, tile.Y + 1];
            else
                SW = forcedTile;
            if (!(tile.X - 1 < 0))
                W = layer.Tiles[tile.X - 1, tile.Y];
            else
                W = forcedTile;

            layer.TerrainCache[tile.X, tile.Y].Cache(tile.X, tile.Y, tile.Terrain, NW.Terrain, N.Terrain, NE.Terrain, E.Terrain, SE.Terrain, S.Terrain, SW.Terrain, W.Terrain);
        }
        private void CacheSurroundingTerrain(EditorTileLayer layer, EditorTile tile)
        {
            var tiles = new List<EditorTile>();

            if (!(tile.X - 1 < 0 || tile.Y - 1 < 0))
                tiles.Add(layer.Tiles[tile.X - 1, tile.Y - 1]);
            if (!(tile.Y - 1 < 0))
                tiles.Add(layer.Tiles[tile.X, tile.Y - 1]);
            if (!(tile.X + 1 > Width - 1 || tile.Y - 1 < 0))
                tiles.Add(layer.Tiles[tile.X + 1, tile.Y - 1]);
            if (!(tile.X + 1 > Width - 1))
                tiles.Add(layer.Tiles[tile.X + 1, tile.Y]);
            if (!(tile.X + 1 > Width - 1 || tile.Y + 1 > Height - 1))
                tiles.Add(layer.Tiles[tile.X + 1, tile.Y + 1]);
            if (!(tile.Y + 1 > Height - 1))
                tiles.Add(layer.Tiles[tile.X, tile.Y + 1]);
            if (!(tile.X - 1 < 0 || tile.Y + 1 > Height - 1))
                tiles.Add(layer.Tiles[tile.X - 1, tile.Y + 1]);
            if (!(tile.X - 1 < 0))
                tiles.Add(layer.Tiles[tile.X - 1, tile.Y]);

            foreach (var item in tiles)
            {
                if (item.Terrain > 0)
                    CacheTerrainTile(layer, item);
            }
        }