using System.Collections.Generic; using System.Globalization; using System.Linq; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using MonoGame.Extended; using MonoGame.Extended.Tiled; using ColorHelper = MLEM.Extensions.ColorHelper; namespace MLEM.Extended.Tiled { /// /// A set of extensions for dealing with MonoGame.Extended tiled maps /// public static class TiledExtensions { private static readonly Dictionary StubTilesetTiles = new Dictionary(); /// /// Gets the property with the given key, or null if there is none. /// /// The set of properties /// The key by which to get a property /// The property, or null if there is none public static string Get(this TiledMapProperties properties, string key) { properties.TryGetValue(key, out var val); return val; } /// /// Gets a boolean property with the given key, or null if there is none. /// /// The set of properties /// The key by which to get a property /// The boolean property, or false if there is none public static bool GetBool(this TiledMapProperties properties, string key) { bool.TryParse(properties.Get(key), out var val); return val; } /// /// Gets a Color property with the given key, or null if there is none. /// /// The set of properties /// The key by which to get a property /// The color property public static Color GetColor(this TiledMapProperties properties, string key) { ColorHelper.TryFromHexString(properties.Get(key), out var val); return val; } /// /// Gets a float property with the given key, or null if there is none. /// /// The set of properties /// The key by which to get a property /// The float property, or 0 if there is none public static float GetFloat(this TiledMapProperties properties, string key) { float.TryParse(properties.Get(key), NumberStyles.Number, CultureInfo.InvariantCulture, out var val); return val; } /// /// Gets an int property with the given key, or null if there is none. /// /// The set of properties /// The key by which to get a property /// The int property, or 0 if there is none public static int GetInt(this TiledMapProperties properties, string key) { int.TryParse(properties.Get(key), NumberStyles.Number, CultureInfo.InvariantCulture, out var val); return val; } /// /// Gets the tileset for the given map tile on the given map. /// /// The tile /// The map the tile is on /// The tileset that the tile came from public static TiledMapTileset GetTileset(this TiledMapTile tile, TiledMap map) { return map.GetTilesetByTileGlobalIdentifier(tile.GlobalIdentifier); } /// /// Gets the local tile identifier for the given tiled map tile. /// The local tile identifier is the identifier within the tile's tileset. /// /// The tile whose identifier to get /// The tileset the tile is from /// The map the tile is on /// The local identifier public static int GetLocalIdentifier(this TiledMapTile tile, TiledMapTileset tileset, TiledMap map) { return tile.GlobalIdentifier - map.GetTilesetFirstGlobalIdentifier(tileset); } /// /// Gets the global tile identifier for the given tiled map tileset tile. /// The global tile identifier is the identifier within all of the tile sets that the map has. /// /// The tile whose global identifier to get /// The tileset the tile is from /// The map the tile is on /// The global identifier public static int GetGlobalIdentifier(this TiledMapTilesetTile tile, TiledMapTileset tileset, TiledMap map) { return map.GetTilesetFirstGlobalIdentifier(tileset) + tile.LocalTileIdentifier; } /// /// Gets the tileset tile on the given tileset for the given tile. /// /// The tileset /// The tile /// The map the tile is on /// If a tileset tile has no special properties, there is no pre-made object for it. If this boolean is true, a stub object with no extra data will be created instead of returning null. /// null if the tile is blank or the tileset tile if there is one or createStub is true public static TiledMapTilesetTile GetTilesetTile(this TiledMapTileset tileset, TiledMapTile tile, TiledMap map, bool createStub = true) { if (tile.IsBlank) return null; var localId = tile.GetLocalIdentifier(tileset, map); return tileset.GetTilesetTile(localId, createStub); } /// /// Gets the tileset tile on the given tileset for the given tile. /// If the tileset is already known, you should use instead for performance. /// /// The tile /// The map the tile is on /// If a tileset tile has no special properties, there is no pre-made object for it. If this boolean is true, a stub object with no extra data will be created instead of returning null. /// null if the tile is blank or the tileset tile if there is one or createStub is true public static TiledMapTilesetTile GetTilesetTile(this TiledMapTile tile, TiledMap map, bool createStub = true) { if (tile.IsBlank) return null; var tileset = tile.GetTileset(map); return tileset.GetTilesetTile(tile, map, createStub); } /// /// Gets the tileset tile on the given tileset for the given local id. /// /// The tileset /// The tile's local id /// If a tileset tile has no special properties, there is no pre-made object for it. If this boolean is true, a stub object with no extra data will be created instead of returning null. /// null if the tile is blank or the tileset tile if there is one or createStub is true public static TiledMapTilesetTile GetTilesetTile(this TiledMapTileset tileset, int localId, bool createStub = true) { var tilesetTile = tileset.Tiles.FirstOrDefault(t => t.LocalTileIdentifier == localId); if (tilesetTile == null && createStub) { if (!TiledExtensions.StubTilesetTiles.TryGetValue(localId, out tilesetTile)) { tilesetTile = new TiledMapTilesetTile(localId); TiledExtensions.StubTilesetTiles.Add(localId, tilesetTile); } } return tilesetTile; } /// /// Gets the layer index of the layer with the given name in the array. /// /// The map /// The name of the layer /// The resulting index public static int GetTileLayerIndex(this TiledMap map, string layerName) { var layer = map.GetLayer(layerName); return map.TileLayers.IndexOf(layer); } /// /// Returns the tiled map tile at the given location on the layer with the given name. /// /// The map /// The name of the layer the tile is on /// The x coordinate of the tile /// The y coordinate of the tile /// The tile at the given location, or default if the layer does not exist public static TiledMapTile GetTile(this TiledMap map, string layerName, int x, int y) { var layer = map.GetLayer(layerName); return layer != null ? layer.GetTile(x, y) : default; } /// /// Returns the tiled map tile at the given location on the layer with the given name. /// /// The map /// The layer position to get the tile at /// The tile at the given location, or default if the layer does not exist public static TiledMapTile GetTile(this TiledMap map, LayerPosition pos) { return map.GetTile(pos.Layer, pos.X, pos.Y); } /// /// Sets the tiled map tile at the given location to the given global tile identifier. /// /// The map /// The name of the layer /// The x coordinate /// The y coordinate /// The tile's global identifier to set public static void SetTile(this TiledMap map, string layerName, int x, int y, int globalTile) { var layer = map.GetLayer(layerName); if (layer != null) layer.SetTile((ushort) x, (ushort) y, (uint) globalTile); } /// /// Sets the tiled map tile at the given location to the given tile from the given tileset. /// If the passed or is null, the tile at the location is removed instead. /// /// The map /// The name of the layer /// The x coordinate /// The y coordinate /// The tileset to use, or null to remove the tile /// The tile to place, from the given tileset, or null to remove the tile public static void SetTile(this TiledMap map, string layerName, int x, int y, TiledMapTileset tileset, TiledMapTilesetTile tile) { map.SetTile(layerName, x, y, tileset != null && tile != null ? tile.GetGlobalIdentifier(tileset, map) : 0); } /// /// Sets the tiled map tile at the given location to the given global tile identifier. /// /// The map /// The layer position /// The tile's global identifier to set public static void SetTile(this TiledMap map, LayerPosition pos, int globalTile) { map.SetTile(pos.Layer, pos.X, pos.Y, globalTile); } /// /// Sets the tiled map tile at the given location to the given tile from the given tileset. /// If the passed or is null, the tile at the location is removed instead. /// /// The map /// The layer position /// The tileset to use, or null to remove the tile /// The tile to place, from the given tileset, or null to remove the tile public static void SetTile(this TiledMap map, LayerPosition pos, TiledMapTileset tileset, TiledMapTilesetTile tile) { map.SetTile(pos.Layer, pos.X, pos.Y, tileset, tile); } /// /// For an x and y coordinate, returns an enumerable of all of the tiles on each of the map's . /// /// The map /// The x coordinate /// The y coordinate /// All of the tiles on the map at the given location public static IEnumerable GetTiles(this TiledMap map, int x, int y) { foreach (var layer in map.TileLayers) { var tile = layer.GetTile(x, y); if (!tile.IsBlank) yield return tile; } } /// /// Returns the tiled map at the given location on the given layer /// /// The layer to get the tile from /// The tile's x coordinate /// The tile's y coordinate /// The tiled map tile at the location, or default if the location is out of bounds public static TiledMapTile GetTile(this TiledMapTileLayer layer, int x, int y) { return !layer.IsInBounds(x, y) ? default : layer.GetTile((ushort) x, (ushort) y); } /// /// Returns the area that a tiled map object covers. /// The area returned is in percent, meaning that an area that covers a full tile has a size of 1,1. /// /// The object whose area to get /// The map /// The position to add to the object's position /// The flipping of the tile that this object belongs to. If set, the returned area will be "flipped" in the tile's space so that it matches the flip flags. /// The area that the tile covers public static RectangleF GetArea(this TiledMapObject obj, TiledMap map, Vector2? position = null, TiledMapTileFlipFlags flipFlags = TiledMapTileFlipFlags.None) { var tileSize = map.GetTileSize(); var area = new RectangleF(obj.Position / tileSize, obj.Size / tileSize); if (flipFlags.HasFlag(TiledMapTileFlipFlags.FlipHorizontally)) area.X = 1 - area.X - area.Width; if (flipFlags.HasFlag(TiledMapTileFlipFlags.FlipVertically)) area.Y = 1 - area.Y - area.Height; if (position != null) area.Offset(position.Value); return area; } /// /// Returns the width and height of a tile on the given map, as a vector. /// /// The map /// The width and height of a tile public static Vector2 GetTileSize(this TiledMap map) { return new Vector2(map.TileWidth, map.TileHeight); } /// /// Returns whether the given position is in the bounds of the layer (that is, if each coordinate is >= 0 and if they are both smaller than the layer's width and height). /// /// The layer /// The x coordinate /// The y coordinate /// Whether the position is in bounds of the layer public static bool IsInBounds(this TiledMapTileLayer layer, int x, int y) { return x >= 0 && y >= 0 && x < layer.Width && y < layer.Height; } /// /// Returns all of the objects by the given name, or by the given type, in an object layer. /// /// The layer whose objects to search /// The name or type name of the objects to find /// Whether to search object names /// Whether to search object types /// An enumerable of tiled map objects that match the search public static IEnumerable GetObjects(this TiledMapObjectLayer layer, string id, bool searchName = true, bool searchType = false) { foreach (var obj in layer.Objects) { if (searchName && obj.Name == id || searchType && obj.Type == id) yield return obj; } } /// /// Returns all of the objects by the given name, or by the given type, on the given map. /// /// The layer whose objects to search /// The name or type name of the objects to find /// Whether to search object names /// Whether to search object types /// An enumerable of tiled map objects that match the search public static IEnumerable GetObjects(this TiledMap map, string id, bool searchName = true, bool searchType = false) { foreach (var layer in map.ObjectLayers) { foreach (var obj in layer.GetObjects(id, searchName, searchType)) yield return obj; } } /// /// Returns the texture region, as a rectangle, that the given tile uses for rendering. /// /// The tileset the tile is on /// The tile /// The tile's texture region, in pixels. public static Rectangle GetTextureRegion(this TiledMapTileset tileset, TiledMapTilesetTile tile) { var id = tile.LocalTileIdentifier; if (tile is TiledMapTilesetAnimatedTile animated) id = animated.CurrentAnimationFrame.LocalTileIdentifier; return tileset.GetTileRegion(id); } /// /// Converts a tile's flip settings into . /// /// The tile whose flip settings to convert /// The tile's flip settings as sprite effects public static SpriteEffects GetSpriteEffects(this TiledMapTile tile) { var flipping = SpriteEffects.None; if (tile.IsFlippedHorizontally) flipping |= SpriteEffects.FlipHorizontally; if (tile.IsFlippedVertically) flipping |= SpriteEffects.FlipVertically; return flipping; } } }