2019-12-07 15:17:40 +01:00
using System.Collections.Generic ;
2019-12-27 23:10:57 +01:00
using System.Globalization ;
2019-09-18 14:09:15 +02:00
using System.Linq ;
using Microsoft.Xna.Framework ;
2020-04-29 22:14:33 +02:00
using Microsoft.Xna.Framework.Graphics ;
2019-09-18 14:09:15 +02:00
using MonoGame.Extended ;
using MonoGame.Extended.Tiled ;
2020-11-04 23:44:41 +01:00
using ColorHelper = MLEM . Extensions . ColorHelper ;
2019-09-18 14:09:15 +02:00
namespace MLEM.Extended.Tiled {
2020-05-22 20:32:38 +02:00
/// <summary>
/// A set of extensions for dealing with MonoGame.Extended tiled maps
/// </summary>
2019-09-18 14:09:15 +02:00
public static class TiledExtensions {
2019-12-07 15:17:40 +01:00
private static readonly Dictionary < int , TiledMapTilesetTile > StubTilesetTiles = new Dictionary < int , TiledMapTilesetTile > ( ) ;
2020-05-22 20:32:38 +02:00
/// <summary>
/// Gets the property with the given key, or null if there is none.
/// </summary>
/// <param name="properties">The set of properties</param>
/// <param name="key">The key by which to get a property</param>
/// <returns>The property, or null if there is none</returns>
2019-09-18 14:09:15 +02:00
public static string Get ( this TiledMapProperties properties , string key ) {
properties . TryGetValue ( key , out var val ) ;
return val ;
}
2020-05-22 20:32:38 +02:00
/// <summary>
/// Gets a boolean property with the given key, or null if there is none.
/// </summary>
/// <param name="properties">The set of properties</param>
/// <param name="key">The key by which to get a property</param>
/// <returns>The boolean property, or false if there is none</returns>
2019-09-18 14:09:15 +02:00
public static bool GetBool ( this TiledMapProperties properties , string key ) {
bool . TryParse ( properties . Get ( key ) , out var val ) ;
return val ;
}
2020-05-22 20:32:38 +02:00
/// <summary>
/// Gets a Color property with the given key, or null if there is none.
/// </summary>
/// <param name="properties">The set of properties</param>
/// <param name="key">The key by which to get a property</param>
/// <returns>The color property</returns>
2019-09-18 14:09:15 +02:00
public static Color GetColor ( this TiledMapProperties properties , string key ) {
2023-09-30 22:50:18 +02:00
ColorHelper . TryFromHexString ( properties . Get ( key ) , out var val ) ;
return val ;
2019-09-18 14:09:15 +02:00
}
2020-05-22 20:32:38 +02:00
/// <summary>
/// Gets a float property with the given key, or null if there is none.
/// </summary>
/// <param name="properties">The set of properties</param>
/// <param name="key">The key by which to get a property</param>
/// <returns>The float property, or 0 if there is none</returns>
2019-09-18 14:09:15 +02:00
public static float GetFloat ( this TiledMapProperties properties , string key ) {
2021-03-28 06:20:27 +02:00
float . TryParse ( properties . Get ( key ) , NumberStyles . Number , CultureInfo . InvariantCulture , out var val ) ;
2019-09-18 14:09:15 +02:00
return val ;
}
2020-05-22 20:32:38 +02:00
/// <summary>
/// Gets an int property with the given key, or null if there is none.
/// </summary>
/// <param name="properties">The set of properties</param>
/// <param name="key">The key by which to get a property</param>
/// <returns>The int property, or 0 if there is none</returns>
2019-09-18 14:09:15 +02:00
public static int GetInt ( this TiledMapProperties properties , string key ) {
2021-03-28 06:20:27 +02:00
int . TryParse ( properties . Get ( key ) , NumberStyles . Number , CultureInfo . InvariantCulture , out var val ) ;
2019-09-18 14:09:15 +02:00
return val ;
}
2020-05-22 20:32:38 +02:00
/// <summary>
/// Gets the tileset for the given map tile on the given map.
/// </summary>
/// <param name="tile">The tile</param>
/// <param name="map">The map the tile is on</param>
/// <returns>The tileset that the tile came from</returns>
2019-09-18 14:09:15 +02:00
public static TiledMapTileset GetTileset ( this TiledMapTile tile , TiledMap map ) {
return map . GetTilesetByTileGlobalIdentifier ( tile . GlobalIdentifier ) ;
}
2020-05-22 20:32:38 +02:00
/// <summary>
/// Gets the local tile identifier for the given tiled map tile.
/// The local tile identifier is the identifier within the tile's tileset.
/// </summary>
/// <param name="tile">The tile whose identifier to get</param>
/// <param name="tileset">The tileset the tile is from</param>
/// <param name="map">The map the tile is on</param>
/// <returns>The local identifier</returns>
2019-09-18 14:09:15 +02:00
public static int GetLocalIdentifier ( this TiledMapTile tile , TiledMapTileset tileset , TiledMap map ) {
return tile . GlobalIdentifier - map . GetTilesetFirstGlobalIdentifier ( tileset ) ;
}
2020-05-22 20:32:38 +02:00
/// <summary>
/// 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.
/// </summary>
/// <param name="tile">The tile whose global identifier to get</param>
/// <param name="tileset">The tileset the tile is from</param>
/// <param name="map">The map the tile is on</param>
/// <returns>The global identifier</returns>
2020-04-29 22:14:33 +02:00
public static int GetGlobalIdentifier ( this TiledMapTilesetTile tile , TiledMapTileset tileset , TiledMap map ) {
return map . GetTilesetFirstGlobalIdentifier ( tileset ) + tile . LocalTileIdentifier ;
}
2020-05-22 20:32:38 +02:00
/// <summary>
/// Gets the tileset tile on the given tileset for the given tile.
/// </summary>
/// <param name="tileset">The tileset</param>
/// <param name="tile">The tile</param>
/// <param name="map">The map the tile is on</param>
/// <param name="createStub">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.</param>
/// <returns>null if the tile is blank or the tileset tile if there is one or createStub is true</returns>
2019-12-07 15:17:40 +01:00
public static TiledMapTilesetTile GetTilesetTile ( this TiledMapTileset tileset , TiledMapTile tile , TiledMap map , bool createStub = true ) {
2019-09-24 12:21:12 +02:00
if ( tile . IsBlank )
return null ;
2019-09-18 14:09:15 +02:00
var localId = tile . GetLocalIdentifier ( tileset , map ) ;
2021-03-09 18:56:55 +01:00
return tileset . GetTilesetTile ( localId , createStub ) ;
2019-09-18 14:09:15 +02:00
}
2020-05-22 20:32:38 +02:00
/// <summary>
/// Gets the tileset tile on the given tileset for the given tile.
/// If the tileset is already known, you should use <see cref="GetTilesetTile(MonoGame.Extended.Tiled.TiledMapTileset,MonoGame.Extended.Tiled.TiledMapTile,MonoGame.Extended.Tiled.TiledMap,bool)"/> instead for performance.
/// </summary>
/// <param name="tile">The tile</param>
/// <param name="map">The map the tile is on</param>
/// <param name="createStub">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.</param>
/// <returns>null if the tile is blank or the tileset tile if there is one or createStub is true</returns>
2019-12-07 15:17:40 +01:00
public static TiledMapTilesetTile GetTilesetTile ( this TiledMapTile tile , TiledMap map , bool createStub = true ) {
2019-09-24 12:21:12 +02:00
if ( tile . IsBlank )
return null ;
2019-09-18 14:09:15 +02:00
var tileset = tile . GetTileset ( map ) ;
2019-12-07 15:17:40 +01:00
return tileset . GetTilesetTile ( tile , map , createStub ) ;
2019-09-18 14:09:15 +02:00
}
2021-03-09 18:56:55 +01:00
/// <summary>
/// Gets the tileset tile on the given tileset for the given local id.
/// </summary>
/// <param name="tileset">The tileset</param>
/// <param name="localId">The tile's local id</param>
/// <param name="createStub">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.</param>
/// <returns>null if the tile is blank or the tileset tile if there is one or createStub is true</returns>
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 ) {
2022-06-15 11:38:11 +02:00
if ( ! TiledExtensions . StubTilesetTiles . TryGetValue ( localId , out tilesetTile ) ) {
2021-03-09 18:56:55 +01:00
tilesetTile = new TiledMapTilesetTile ( localId ) ;
2022-06-15 11:38:11 +02:00
TiledExtensions . StubTilesetTiles . Add ( localId , tilesetTile ) ;
2021-03-09 18:56:55 +01:00
}
}
return tilesetTile ;
}
2020-05-22 20:32:38 +02:00
/// <summary>
/// Gets the layer index of the layer with the given name in the <see cref="TiledMap.Layers"/> array.
/// </summary>
/// <param name="map">The map</param>
/// <param name="layerName">The name of the layer</param>
/// <returns>The resulting index</returns>
2019-12-07 12:56:01 +01:00
public static int GetTileLayerIndex ( this TiledMap map , string layerName ) {
var layer = map . GetLayer < TiledMapTileLayer > ( layerName ) ;
return map . TileLayers . IndexOf ( layer ) ;
}
2020-05-22 20:32:38 +02:00
/// <summary>
/// Returns the tiled map tile at the given location on the layer with the given name.
/// </summary>
/// <param name="map">The map</param>
/// <param name="layerName">The name of the layer the tile is on</param>
/// <param name="x">The x coordinate of the tile</param>
/// <param name="y">The y coordinate of the tile</param>
/// <returns>The tile at the given location, or default if the layer does not exist</returns>
2019-09-18 14:09:15 +02:00
public static TiledMapTile GetTile ( this TiledMap map , string layerName , int x , int y ) {
var layer = map . GetLayer < TiledMapTileLayer > ( layerName ) ;
2019-09-24 12:26:38 +02:00
return layer ! = null ? layer . GetTile ( x , y ) : default ;
}
2021-03-10 02:13:07 +01:00
/// <summary>
/// Returns the tiled map tile at the given location on the layer with the given name.
/// </summary>
/// <param name="map">The map</param>
/// <param name="pos">The layer position to get the tile at</param>
/// <returns>The tile at the given location, or default if the layer does not exist</returns>
public static TiledMapTile GetTile ( this TiledMap map , LayerPosition pos ) {
return map . GetTile ( pos . Layer , pos . X , pos . Y ) ;
}
2020-05-22 20:32:38 +02:00
/// <summary>
/// Sets the tiled map tile at the given location to the given global tile identifier.
/// </summary>
/// <param name="map">The map</param>
/// <param name="layerName">The name of the layer</param>
/// <param name="x">The x coordinate</param>
/// <param name="y">The y coordinate</param>
/// <param name="globalTile">The tile's global identifier to set</param>
2020-04-29 22:14:33 +02:00
public static void SetTile ( this TiledMap map , string layerName , int x , int y , int globalTile ) {
var layer = map . GetLayer < TiledMapTileLayer > ( layerName ) ;
if ( layer ! = null )
layer . SetTile ( ( ushort ) x , ( ushort ) y , ( uint ) globalTile ) ;
}
2021-03-09 18:56:55 +01:00
/// <summary>
2021-03-09 19:06:06 +01:00
/// Sets the tiled map tile at the given location to the given tile from the given tileset.
/// If the passed <paramref name="tileset"/> or <paramref name="tile"/> is null, the tile at the location is removed instead.
2021-03-09 18:56:55 +01:00
/// </summary>
/// <param name="map">The map</param>
/// <param name="layerName">The name of the layer</param>
/// <param name="x">The x coordinate</param>
/// <param name="y">The y coordinate</param>
2021-03-09 19:06:06 +01:00
/// <param name="tileset">The tileset to use, or null to remove the tile</param>
/// <param name="tile">The tile to place, from the given tileset, or null to remove the tile</param>
2021-03-09 18:56:55 +01:00
public static void SetTile ( this TiledMap map , string layerName , int x , int y , TiledMapTileset tileset , TiledMapTilesetTile tile ) {
2021-03-09 19:06:06 +01:00
map . SetTile ( layerName , x , y , tileset ! = null & & tile ! = null ? tile . GetGlobalIdentifier ( tileset , map ) : 0 ) ;
2021-03-09 18:56:55 +01:00
}
2021-03-10 02:13:07 +01:00
/// <summary>
/// Sets the tiled map tile at the given location to the given global tile identifier.
/// </summary>
/// <param name="map">The map</param>
/// <param name="pos">The layer position</param>
/// <param name="globalTile">The tile's global identifier to set</param>
public static void SetTile ( this TiledMap map , LayerPosition pos , int globalTile ) {
map . SetTile ( pos . Layer , pos . X , pos . Y , globalTile ) ;
}
/// <summary>
/// Sets the tiled map tile at the given location to the given tile from the given tileset.
/// If the passed <paramref name="tileset"/> or <paramref name="tile"/> is null, the tile at the location is removed instead.
/// </summary>
/// <param name="map">The map</param>
/// <param name="pos">The layer position</param>
/// <param name="tileset">The tileset to use, or null to remove the tile</param>
/// <param name="tile">The tile to place, from the given tileset, or null to remove the tile</param>
public static void SetTile ( this TiledMap map , LayerPosition pos , TiledMapTileset tileset , TiledMapTilesetTile tile ) {
map . SetTile ( pos . Layer , pos . X , pos . Y , tileset , tile ) ;
}
2020-05-22 20:32:38 +02:00
/// <summary>
/// For an x and y coordinate, returns an enumerable of all of the tiles on each of the map's <see cref="TiledMap.TileLayers"/>.
/// </summary>
/// <param name="map">The map</param>
/// <param name="x">The x coordinate</param>
/// <param name="y">The y coordinate</param>
/// <returns>All of the tiles on the map at the given location</returns>
2019-12-17 21:02:09 +01:00
public static IEnumerable < TiledMapTile > GetTiles ( this TiledMap map , int x , int y ) {
2020-01-14 14:46:06 +01:00
foreach ( var layer in map . TileLayers ) {
var tile = layer . GetTile ( x , y ) ;
if ( ! tile . IsBlank )
yield return tile ;
}
2019-12-17 21:02:09 +01:00
}
2020-05-22 20:32:38 +02:00
/// <summary>
/// Returns the tiled map at the given location on the given layer
/// </summary>
/// <param name="layer">The layer to get the tile from</param>
/// <param name="x">The tile's x coordinate</param>
/// <param name="y">The tile's y coordinate</param>
/// <returns>The tiled map tile at the location, or default if the location is out of bounds</returns>
2019-09-24 12:26:38 +02:00
public static TiledMapTile GetTile ( this TiledMapTileLayer layer , int x , int y ) {
2019-12-06 16:12:57 +01:00
return ! layer . IsInBounds ( x , y ) ? default : layer . GetTile ( ( ushort ) x , ( ushort ) y ) ;
2019-09-18 14:09:15 +02:00
}
2020-05-22 20:32:38 +02:00
/// <summary>
/// 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.
/// </summary>
/// <param name="obj">The object whose area to get</param>
/// <param name="map">The map</param>
/// <param name="position">The position to add to the object's position</param>
2021-03-12 20:22:36 +01:00
/// <param name="flipFlags">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.</param>
2020-05-22 20:32:38 +02:00
/// <returns>The area that the tile covers</returns>
2022-06-15 11:38:11 +02:00
public static RectangleF GetArea ( this TiledMapObject obj , TiledMap map , Vector2 ? position = null , TiledMapTileFlipFlags flipFlags = TiledMapTileFlipFlags . None ) {
2019-09-19 20:23:18 +02:00
var tileSize = map . GetTileSize ( ) ;
2021-03-12 20:22:36 +01:00
var area = new RectangleF ( obj . Position / tileSize , obj . Size / tileSize ) ;
2022-06-15 11:38:11 +02:00
if ( flipFlags . HasFlag ( TiledMapTileFlipFlags . FlipHorizontally ) )
2021-03-12 20:22:36 +01:00
area . X = 1 - area . X - area . Width ;
2022-06-15 11:38:11 +02:00
if ( flipFlags . HasFlag ( TiledMapTileFlipFlags . FlipVertically ) )
2021-03-12 20:22:36 +01:00
area . Y = 1 - area . Y - area . Height ;
if ( position ! = null )
area . Offset ( position . Value ) ;
return area ;
2019-09-18 14:09:15 +02:00
}
2020-05-22 20:32:38 +02:00
/// <summary>
/// Returns the width and height of a tile on the given map, as a vector.
/// </summary>
/// <param name="map">The map</param>
/// <returns>The width and height of a tile</returns>
2019-09-19 20:23:18 +02:00
public static Vector2 GetTileSize ( this TiledMap map ) {
return new Vector2 ( map . TileWidth , map . TileHeight ) ;
}
2020-05-22 20:32:38 +02:00
/// <summary>
/// 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).
/// </summary>
/// <param name="layer">The layer</param>
/// <param name="x">The x coordinate</param>
/// <param name="y">The y coordinate</param>
/// <returns>Whether the position is in bounds of the layer</returns>
2019-12-06 16:12:57 +01:00
public static bool IsInBounds ( this TiledMapTileLayer layer , int x , int y ) {
return x > = 0 & & y > = 0 & & x < layer . Width & & y < layer . Height ;
}
2020-05-22 20:32:38 +02:00
/// <summary>
/// Returns all of the objects by the given name, or by the given type, in an object layer.
/// </summary>
/// <param name="layer">The layer whose objects to search</param>
/// <param name="id">The name or type name of the objects to find</param>
/// <param name="searchName">Whether to search object names</param>
/// <param name="searchType">Whether to search object types</param>
/// <returns>An enumerable of tiled map objects that match the search</returns>
2019-12-19 00:27:56 +01:00
public static IEnumerable < TiledMapObject > 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 ;
}
}
2020-05-22 20:32:38 +02:00
/// <summary>
/// Returns all of the objects by the given name, or by the given type, on the given map.
/// </summary>
/// <param name="map">The layer whose objects to search</param>
/// <param name="id">The name or type name of the objects to find</param>
/// <param name="searchName">Whether to search object names</param>
/// <param name="searchType">Whether to search object types</param>
/// <returns>An enumerable of tiled map objects that match the search</returns>
public static IEnumerable < TiledMapObject > GetObjects ( this TiledMap map , string id , bool searchName = true , bool searchType = false ) {
2019-12-19 00:27:56 +01:00
foreach ( var layer in map . ObjectLayers ) {
2020-05-22 20:32:38 +02:00
foreach ( var obj in layer . GetObjects ( id , searchName , searchType ) )
2019-12-19 00:27:56 +01:00
yield return obj ;
}
}
2020-05-22 20:32:38 +02:00
/// <summary>
/// Returns the texture region, as a rectangle, that the given tile uses for rendering.
/// </summary>
/// <param name="tileset">The tileset the tile is on</param>
/// <param name="tile">The tile</param>
/// <returns>The tile's texture region, in pixels.</returns>
2020-04-29 22:14:33 +02:00
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 ) ;
}
2020-05-22 20:32:38 +02:00
/// <summary>
/// Converts a tile's flip settings into <see cref="SpriteEffects"/>.
/// </summary>
/// <param name="tile">The tile whose flip settings to convert</param>
/// <returns>The tile's flip settings as sprite effects</returns>
2020-04-29 22:14:33 +02:00
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 ;
}
2019-09-18 14:09:15 +02:00
}
2022-06-17 18:23:47 +02:00
}