2019-12-07 15:17:40 +01:00
using System ;
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-04-30 21:15:28 +02:00
using ColorExtensions = MLEM . Extensions . ColorExtensions ;
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 ) {
2020-04-30 21:15:28 +02:00
return ColorExtensions . FromHex ( properties . Get ( key ) ) ;
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 ) {
2019-12-27 23:10:57 +01:00
float . TryParse ( properties . Get ( key ) , NumberStyles . Number , NumberFormatInfo . InvariantInfo , 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 ) {
2019-12-27 23:10:57 +01:00
int . TryParse ( properties . Get ( key ) , NumberStyles . Number , NumberFormatInfo . InvariantInfo , 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 ) ;
2019-12-07 15:17:40 +01:00
var tilesetTile = tileset . Tiles . FirstOrDefault ( t = > t . LocalTileIdentifier = = localId ) ;
if ( tilesetTile = = null & & createStub ) {
var id = tile . GetLocalIdentifier ( tileset , map ) ;
if ( ! StubTilesetTiles . TryGetValue ( id , out tilesetTile ) ) {
tilesetTile = new TiledMapTilesetTile ( id ) ;
StubTilesetTiles . Add ( id , tilesetTile ) ;
}
}
return tilesetTile ;
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
}
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 ;
}
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 ) ;
}
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>
/// <returns>The area that the tile covers</returns>
2019-09-25 20:37:20 +02:00
public static RectangleF GetArea ( this TiledMapObject obj , TiledMap map , Vector2 ? position = null ) {
2019-09-19 20:23:18 +02:00
var tileSize = map . GetTileSize ( ) ;
2019-09-25 20:37:20 +02:00
var pos = position ? ? Vector2 . Zero ;
2019-12-01 22:55:17 +01:00
return new RectangleF ( obj . Position / tileSize + pos , obj . Size / tileSize ) ;
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
}
}