using System; using System.Collections.Generic; using System.Reflection; using System.Xml; using System.Xml.Serialization; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using MonoGame.Extended.Tiled.Serialization; namespace MonoGame.Extended.Tiled { public class TMXLoader { private readonly string _fileName; public delegate Texture2D TextureLoader(string source); public delegate XmlReader XmlLoader(string source); private TextureLoader _textureLoader; private XmlLoader _xmlLoader; private TMXLoader(string path, TextureLoader textureLoader, XmlLoader xmlLoader) { _fileName = path; _textureLoader = textureLoader; _xmlLoader = xmlLoader; Console.WriteLine($"TMX Loader: {path}"); } private TiledMapObjectLayer ConvertTiledMapObjectLayer(TiledMap tiledMap , TiledMapObjectLayerContent lc) { List objects = new List(); for (int i = 0; i < lc.Objects.Count; i++) { TiledMapObjectContent c = lc.Objects[i]; if (c.Ellipse != null) { // Not supported } else if (c.Polygon != null) { // TODO // List points = new List(); // var ob = new TiledMapPolygonObject(c.Identifier, c.Name, null, new Size2(c.Width, c.Height), new Vector2(c.X, c.Y), c.Rotation, 1.0f, c.Visible, c.Type ); // objects.Add(ob); } else if (c.Polyline != null) { // TODO } else { TiledMapTileset tileSet = tiledMap.GetTilesetByTileGlobalIdentifier((int) c.GlobalIdentifier); if (tileSet != null) { var fgi = tiledMap.GetTilesetFirstGlobalIdentifier(tileSet); int localIdentifier = (int) (c.GlobalIdentifier - fgi); TiledMapTilesetTile tile = null; foreach (var t in tileSet.Tiles) { if (t.LocalTileIdentifier == localIdentifier) { tile = t; break; } } // Copy properties var tmo = new TiledMapTileObject(c.Identifier, c.Name, tileSet, tile, new Vector2(c.Width, c.Height), new Vector2(c.X, c.Y), c.Rotation , 1.0f, c.Visible, c.Type); foreach (var prop in c.Properties) { tmo.Properties.Add(prop.Name, prop.Value); } objects.Add(tmo); } else { Console.WriteLine($"Warning: no tileset for object gid {c.GlobalIdentifier}"); } } } TiledMapObjectLayer layer = new TiledMapObjectLayer(lc.Name, objects.ToArray(), ColorHelper.FromHex(lc.Color), (TiledMapObjectDrawOrder) lc.DrawOrder, new Vector2(lc.OffsetX, lc.OffsetY), lc.Opacity, lc.Visible); return layer; } private TiledMapTileLayer ConvertTiledMapTileLayer(TiledMap tiledMap, TiledMapTileLayerContent lc) { TiledMapTileLayer layer = new TiledMapTileLayer(lc.Name, lc.Width, lc.Height, tiledMap.TileWidth, tiledMap.TileHeight, null, lc.Opacity, lc.Visible); // Copy properties foreach (var prop in lc.Properties) { layer.Properties.Add(prop.Name, prop.Value); } // Copy tiles if (lc.Data.Encoding == "csv") { string[] tileIds = lc.Data.Value.Split(","); int offs = 0; for (ushort y = 0; y < layer.Height; y++) { for (ushort x = 0; x < layer.Width; x++) { layer.Tiles[offs] = new TiledMapTile(uint.Parse(tileIds[offs]), x, y); offs++; } } } return layer; } private TiledMapTileset ConvertTiledMapTileset(TiledMapTilesetContent tsc) { var texture = _textureLoader(tsc.Image.Source); var ts = new TiledMapTileset(texture, tsc.TileWidth, tsc.TileHeight, tsc.TileCount, tsc.Spacing, tsc.Margin, tsc.Columns); foreach (var t in tsc.Tiles) { // TODO: Objects? TiledMapTilesetTile tile; if (t.Frames.Count == 0) { tile = new TiledMapTilesetTile(t.LocalIdentifier, t.Type, null); } else { // Animation frames TiledMapTilesetTileAnimationFrame[] frames = new TiledMapTilesetTileAnimationFrame[t.Frames.Count]; for (int i = 0; i < t.Frames.Count; i++) { var frameContent = t.Frames[i]; BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Instance; object[] parameters = {ts, frameContent.TileIdentifier, frameContent.Duration}; object instantiatedType = Activator.CreateInstance(typeof(TiledMapTilesetTileAnimationFrame), bindingFlags, null, parameters, null); frames[i] = (TiledMapTilesetTileAnimationFrame) instantiatedType; } tile = new TiledMapTilesetAnimatedTile(t.LocalIdentifier, frames, t.Type, null); } // Copy properties foreach (var prop in t.Properties) { tile.Properties[prop.Name] = prop.Value; } ts.Tiles.Add(tile); } foreach (var prop in tsc.Properties) { ts.Properties[prop.Name] = prop.Value; } return ts; } private TiledMap ConvertTiledMap() { var xmlSerializer = new XmlSerializer(typeof(TiledMapContent)); var xmlReader = _xmlLoader(_fileName); TiledMapContent tiledMapContent = (TiledMapContent) xmlSerializer.Deserialize(xmlReader); TiledMap tiledMap = new TiledMap(tiledMapContent.Name, tiledMapContent.Width, tiledMapContent.Height, tiledMapContent.TileWidth, tiledMapContent.TileHeight, (TiledMapTileDrawOrder) tiledMapContent.RenderOrder, TiledMapOrientation.Orthogonal, ColorHelper.FromHex(tiledMapContent.BackgroundColor)); foreach (var prop in tiledMapContent.Properties) { tiledMap.Properties[prop.Name] = prop.Value; } foreach (var tileSetContent in tiledMapContent.Tilesets) { TiledMapTilesetContent content = tileSetContent; if (tileSetContent.Source != null) { // load content from file content = (TiledMapTilesetContent) new XmlSerializer(typeof(TiledMapTilesetContent)).Deserialize( _xmlLoader(tileSetContent.Source)); } TiledMapTileset tileSet = ConvertTiledMapTileset(content); tiledMap.AddTileset(tileSet, tileSetContent.FirstGlobalIdentifier); } foreach (var layerContent in tiledMapContent.Layers) { switch (layerContent.Type) { case TiledMapLayerType.TileLayer: TiledMapTileLayer layer = ConvertTiledMapTileLayer(tiledMap, (TiledMapTileLayerContent) layerContent); tiledMap.AddLayer(layer); break; case TiledMapLayerType.ObjectLayer: var objectLayer = ConvertTiledMapObjectLayer(tiledMap, (TiledMapObjectLayerContent) layerContent); tiledMap.AddLayer(objectLayer); break; } } return tiledMap; } public static TiledMap LoadTiledMap(string path, TextureLoader textureLoader, XmlLoader xmlLoader) { var loader = new TMXLoader(path, textureLoader, xmlLoader); return loader.ConvertTiledMap(); } } }