private static string GetImageSourcePath(string tmxPath, Tileset tileset, TilesetImage image)
        {
            // The image may be absolute, so only prepend the tmx path if the image is relative
            string sourcepath;

            if (FileManager.IsRelative(image.Source))
            {
                sourcepath = tmxPath + image.Source;
            }
            else
            {
                sourcepath = image.Source;
            }

            sourcepath = FileManager.RemoveDotDotSlash(sourcepath);

            if (tileset.Source != null)
            {
                if (tileset.SourceDirectory != "." && !tileset.SourceDirectory.Contains(":"))
                {
                    sourcepath = tmxPath + tileset.SourceDirectory.Replace("\\", "/") + "/" + image.sourceFileName;
                    sourcepath = FileManager.GetDirectory(sourcepath) + image.sourceFileName;
                }
                else if (tileset.SourceDirectory.Contains(":"))
                {
                    sourcepath = tileset.SourceDirectory + "/" + image.sourceFileName;
                }
                else
                {
                    sourcepath = tmxPath + image.sourceFileName;
                }
            }
            return sourcepath;
        }
Example #2
0
        private static void AddTileShapeCollections(LayeredTileMap layeredTileMap, TiledMapSave tms, bool separateOnTileType)
        {
            var allTilesets = tms.Tilesets;

            Dictionary <string, TileCollisions.TileShapeCollection> nameCollisionPairs = new Dictionary <string, TileCollisions.TileShapeCollection>();


            for (int i = 0; i < tms.Layers.Count; i++)
            {
                var layer = tms.Layers[i];
                // Currently we only support 1 tileset per layer, so we'll find the tileset for this layer
                var firstNonZero = layer.data[0].tiles.FirstOrDefault(item => item != 0);

                TMXGlueLib.Tileset tileset = null;

                if (firstNonZero != 0)
                {
                    tileset = tms.GetTilesetForGid(firstNonZero);
                }
                AddTileShapeCollectionForLayer(layer, nameCollisionPairs, tileset, tms.tilewidth, i, separateOnTileType);
            }
            foreach (var item in nameCollisionPairs.Values)
            {
                if (item.Rectangles.Count > 0 || item.Polygons.Count > 0)
                {
                    var sortOnY = layeredTileMap.Height > layeredTileMap.Width;
                    if (sortOnY)
                    {
                        item.SortAxis = Math.Axis.Y;
                    }
                    else
                    {
                        item.SortAxis = Math.Axis.X;
                    }

                    item.RefreshAllRepositionDirections();

                    layeredTileMap.Collisions.Add(item);
                }
            }
        }
Example #3
0
        private static void AddTileShapeCollectionForLayer(TMXGlueLib.MapLayer layer, Dictionary <string, TileCollisions.TileShapeCollection> collisionDictionary,
                                                           TMXGlueLib.Tileset tileset, float tileDimension, float z, bool separateOnTileType)
        {
            Math.Geometry.AxisAlignedRectangle rectangle = null;
            Math.Geometry.Polygon polygon = null;
            Circle circle;

            var tiles = layer.data[0].tiles;


            bool sortOnY = layer.height > layer.width;


            if (tileset != null)
            {
                foreach (var tilesetTile in tileset.Tiles.Where(item => item.Objects?.@object?.Length > 0))
                {
                    var tilesetTileGid = tilesetTile.id + tileset.Firstgid;
                    foreach (var tilesetObject in tilesetTile.Objects.@object)
                    {
                        var name = layer.Name;

                        TiledMapToShapeCollectionConverter.ConvertTiledObjectToFrbShape(tilesetObject, out polygon, out rectangle, out circle);
                        if (rectangle != null)
                        {
                            var collectionName = layer.Name;
                            if (tilesetObject.Type != null && separateOnTileType)
                            {
                                collectionName += "_" + tilesetObject.Type;
                            }
                            var collection = GetOrAddTileShapeCollection(collectionName, collisionDictionary);
                            collection.GridSize = tileDimension;
                            rectangle.Z         = z;
                            if (sortOnY)
                            {
                                for (int y = 0; y < layer.height; y++)
                                {
                                    for (int x = 0; x < layer.width; x++)
                                    {
                                        AddRectangleCloneAtXY(layer, tileDimension, rectangle, tiles, tilesetTileGid, x, y, collection);
                                    }
                                }
                            }
                            else
                            {
                                for (int x = 0; x < layer.width; x++)
                                {
                                    for (int y = 0; y < layer.height; y++)
                                    {
                                        AddRectangleCloneAtXY(layer, tileDimension, rectangle, tiles, tilesetTileGid, x, y, collection);
                                    }
                                }
                            }
                        }
                        else if (polygon != null)
                        {
                            var collectionName = layer.Name;
                            if (tilesetObject.Type != null && separateOnTileType)
                            {
                                collectionName += "_" + tilesetObject.Type;
                            }
                            var collection = GetOrAddTileShapeCollection(collectionName, collisionDictionary);
                            collection.GridSize = tileDimension;

                            // For tile polygons we want them to be centered on the tile.
                            // To do this, we shift all points by its position:
                            for (int i = 0; i < polygon.Points.Count; i++)
                            {
                                var point = polygon.Points[i];
                                point.X += polygon.Position.X - tileDimension / 2.0f;
                                point.Y += polygon.Position.Y + tileDimension / 2.0f;

                                polygon.SetPoint(i, point);
                            }

                            polygon.Z = z;

                            if (sortOnY)
                            {
                                for (int y = 0; y < layer.height; y++)
                                {
                                    for (int x = 0; x < layer.width; x++)
                                    {
                                        var i = y * layer.width + x;

                                        if ((tiles[i] & 0x0fffffff) == tilesetTileGid)
                                        {
                                            var cloned = AddPolygonCloneAtXY(layer, tileDimension, polygon, tiles, tilesetTileGid, i, collection);

                                            ApplyFlip(tiles[i], cloned);
                                        }
                                    }
                                }
                            }
                            else
                            {
                                for (int x = 0; x < layer.width; x++)
                                {
                                    for (int y = 0; y < layer.height; y++)
                                    {
                                        var i = y * layer.width + x;

                                        if ((tiles[i] & 0x0fffffff) == tilesetTileGid)
                                        {
                                            var cloned = AddPolygonCloneAtXY(layer, tileDimension, polygon, tiles, tilesetTileGid, i, collection);

                                            ApplyFlip(tiles[i], cloned);
                                        }
                                    }
                                }
                            }
                        }
                        else if (circle != null)
                        {
                            throw new NotImplementedException("Need to handle circles...");
                        }
                    }
                }
            }
        }
Example #4
0
        public static LayeredTileMap FromTiledMapSave(string tiledMapSaveFile, string contentManager, TiledMapSave tms)
        {
            // Ultimately properties are tied to tiles by the tile name.
            // If a tile has no name but it has properties, those properties
            // will be lost in the conversion. Therefore, we have to add name properties.
            tms.MoveTypeToProperties();

#if DEBUG
            CheckForDuplicateTilesets(tms);
#endif

            tms.NameUnnamedTilesetTiles();
            tms.NameUnnamedObjects();


            string directory = FlatRedBall.IO.FileManager.GetDirectory(tiledMapSaveFile);

            var rtmi = ReducedTileMapInfo.FromTiledMapSave(
                tms, 1, 0, directory, FileReferenceType.Absolute);

            var toReturn = FromReducedTileMapInfo(rtmi, contentManager, tiledMapSaveFile);

            AddShapeCollections(toReturn, tms);

            foreach (var layer in tms.MapLayers)
            {
                var matchingLayer = toReturn.MapLayers.FirstOrDefault(item => item.Name == layer.Name);


                if (matchingLayer != null)
                {
                    if (layer is MapLayer)
                    {
                        var mapLayer = layer as MapLayer;
                        foreach (var propertyValues in mapLayer.properties)
                        {
                            matchingLayer.Properties.Add(new NamedValue
                            {
                                Name  = propertyValues.StrippedName,
                                Value = propertyValues.value,
                                Type  = propertyValues.Type
                            });
                        }

                        matchingLayer.Visible = mapLayer.visible == 1;
                        matchingLayer.Alpha   = mapLayer.Opacity;
                    }
                    else if (layer is mapObjectgroup objectLayer)
                    {
                        matchingLayer.Visible = objectLayer.IsVisible;
                    }
                }
            }


            foreach (var tileset in tms.Tilesets)
            {
                foreach (var tile in tileset.TileDictionary.Values)
                {
                    int propertyCountFromTileset = 0;

                    if (tile.properties.Count != 0)
                    {
                        // this needs a name:
                        string name = tile.properties.FirstOrDefault(item => item.StrippedName.ToLowerInvariant() == "name")?.value;
                        // todo - eventually need to copy default values from the Tileset to the tile here
                        AddPropertiesToMap(tms, toReturn.TileProperties, tile.properties, null, name);
                    }
                }
            }

            foreach (var objectLayer in tms.objectgroup)
            {
                if (objectLayer.@object != null)
                {
                    foreach (var objectInstance in objectLayer.@object)
                    {
                        TMXGlueLib.Tileset tileset   = null;
                        int propertyCountFromTileset = 0;

                        var             objectProperties  = objectInstance.properties;
                        List <property> tilesetProperties = null;
                        if (objectInstance.gid != null)
                        {
                            var gidNoFlip = objectInstance.GidNoFlip;

                            tileset = tms.GetTilesetForGid(gidNoFlip.Value);
                            if (tileset.TileDictionary.ContainsKey(gidNoFlip.Value - tileset.Firstgid))
                            {
                                tilesetProperties        = tileset.TileDictionary[gidNoFlip.Value - tileset.Firstgid].properties;
                                propertyCountFromTileset = tilesetProperties.Count;
                            }
                        }


                        if (objectProperties.Count + propertyCountFromTileset != 0)
                        {
                            string name = objectInstance.Name;
                            // if name is null, check the properties:
                            if (string.IsNullOrEmpty(name))
                            {
                                name = objectProperties.FirstOrDefault(item => item.StrippedNameLower == "name")?.value;
                            }

                            var objectInstanceIsTile = objectInstance.gid != null;

                            if (objectInstanceIsTile)
                            {
                                AddPropertiesToMap(tms, toReturn.TileProperties, objectProperties, tilesetProperties, name);
                            }
                            else
                            {
                                AddPropertiesToMap(tms, toReturn.ShapeProperties, objectProperties, tilesetProperties, name);
                            }
                        }
                    }
                }
            }

            var tmxDirectory = FileManager.GetDirectory(tiledMapSaveFile);

            // add image layers
            foreach (var imageLayer in tms.ImageLayers)
            {
                var imageLayerFile = tmxDirectory + imageLayer.ImageObject.Source;
                var texture        = FlatRedBallServices.Load <Microsoft.Xna.Framework.Graphics.Texture2D>(imageLayerFile);

                var newSprite = new Sprite
                {
                    Texture = texture,
                    Width   = imageLayer.ImageObject.Width,
                    Height  = imageLayer.ImageObject.Height,
                    X       = imageLayer.ImageObject.Width / 2 + imageLayer.OffsetX,
                    Y       = -imageLayer.ImageObject.Height / 2 + imageLayer.OffsetY
                };

                var mdb = new MapDrawableBatch(1, texture);
                mdb.Alpha = imageLayer.Opacity;
                mdb.AttachTo(toReturn, false);
                mdb.Paste(newSprite);
                mdb.Visible = imageLayer.IsVisible;

                toReturn.mMapLists.Add(mdb);
            }

            var animationDictionary = new Dictionary <string, AnimationChain>();

            // add animations
            foreach (var tileset in tms.Tilesets)
            {
                string tilesetImageFile = tmxDirectory + tileset.Images[0].Source;

                if (tileset.SourceDirectory != ".")
                {
                    tilesetImageFile = tmxDirectory + tileset.SourceDirectory + tileset.Images[0].Source;
                }

                var texture = FlatRedBallServices.Load <Microsoft.Xna.Framework.Graphics.Texture2D>(tilesetImageFile, contentManager);

                foreach (var tile in tileset.Tiles.Where(item => item.Animation != null && item.Animation.Frames.Count != 0))
                {
                    var animation = tile.Animation;

                    var animationChain = new AnimationChain();
                    foreach (var frame in animation.Frames)
                    {
                        var animationFrame = new AnimationFrame();
                        animationFrame.FrameLength = frame.Duration / 1000.0f;
                        animationFrame.Texture     = texture;

                        int tileIdRelative = frame.TileId;
                        int globalTileId   = (int)(tileIdRelative + tileset.Firstgid);

                        int leftPixel;
                        int rightPixel;
                        int topPixel;
                        int bottomPixel;
                        TiledMapSave.GetPixelCoordinatesFromGid((uint)globalTileId, tileset, out leftPixel, out topPixel, out rightPixel, out bottomPixel);

                        animationFrame.LeftCoordinate  = MapDrawableBatch.CoordinateAdjustment + leftPixel / (float)texture.Width;
                        animationFrame.RightCoordinate = -MapDrawableBatch.CoordinateAdjustment + rightPixel / (float)texture.Width;

                        animationFrame.TopCoordinate    = MapDrawableBatch.CoordinateAdjustment + topPixel / (float)texture.Height;
                        animationFrame.BottomCoordinate = -MapDrawableBatch.CoordinateAdjustment + bottomPixel / (float)texture.Height;


                        animationChain.Add(animationFrame);
                    }

                    var property = tile.properties.FirstOrDefault(item => item.StrippedNameLower == "name");

                    if (property == null)
                    {
                        throw new InvalidOperationException(
                                  $"The tile with ID {tile.id} has an animation, but it doesn't have a Name property, which is required for animation.");
                    }
                    else
                    {
                        animationDictionary.Add(property.value, animationChain);
                    }
                }
            }

            toReturn.Animation = new LayeredTileMapAnimation(animationDictionary);

            AddTileShapeCollections(toReturn, tms, separateOnTileType: true);


            toReturn.MapProperties = tms.properties
                                     .Select(propertySave => new NamedValue
            {
                Name = propertySave.name, Value = propertySave.value, Type = propertySave.Type
            })
                                     .ToList();


            return(toReturn);
        }
        private static bool TryCopyTilesetImage(string tmxPath, string destinationPath, Tileset tileset, TilesetImage image)
        {
            string sourcepath = GetImageSourcePath(tmxPath, tileset, image);

            string whyCantCopy = null;

            if(!System.IO.File.Exists(sourcepath))
            {
                whyCantCopy = "Could not find the file\n" + sourcepath + "\nwhich is referenced by the tileset " + tileset.Name + " in the tmx\n" + tmxPath;
            }

            if (!string.IsNullOrEmpty(whyCantCopy))
            {
                System.Console.Error.WriteLine(whyCantCopy);
            }
            else
            {
                string destinationFullPath = destinationPath + image.sourceFileName;

                bool shouldCopy =
                    !sourcepath.Equals(destinationFullPath, StringComparison.InvariantCultureIgnoreCase) &&
                    !FileManager.GetDirectory(destinationFullPath).Equals(FileManager.GetDirectory(sourcepath));

                if(shouldCopy)
                {
                    if (File.Exists(destinationFullPath))
                    {
                        // Only copy if source is newer than destination
                        var sourceDate = File.GetLastWriteTimeUtc(sourcepath);
                        var destinationDate = File.GetLastWriteTimeUtc(destinationFullPath);
                        shouldCopy = sourceDate > destinationDate;
                    }
                }

                if (shouldCopy)
                {
                    System.Console.WriteLine("Copying \"{0}\" to \"{1}\".", sourcepath, destinationFullPath);

                    string fileWithoutDotDotSlash = FileManager.RemoveDotDotSlash(sourcepath);

                    const int maxFailures = 5;
                    int currentFailures = 0;

                    while (true)
                    {
                        try
                        {
                            // try it 3 times, in case someone else is copying it at the same time

                            File.Copy(fileWithoutDotDotSlash, destinationFullPath, true);

                            break;
                        }
                        catch (Exception e)
                        {
                            if (currentFailures < maxFailures)
                            {
                                System.Threading.Thread.Sleep(300);
                                // try again:
                                currentFailures++;
                            }
                            else
                            {
                                System.Console.Error.WriteLine("Could not copy \"{0}\" to \"{1}\" \n{2}.", sourcepath, destinationFullPath, e.ToString());
                                break;
                            }
                        }
                    }
                }
            }

            bool succeeded = string.IsNullOrEmpty(whyCantCopy);

            return succeeded;
        }
        private void HandleAddNewTilesetClick(object sender, EventArgs e)
        {
            string fileName;
            bool succeeded;
            GetTilesetFromFileOrOptions(canBeImageFile:true, fileName: out fileName, succeeded: out succeeded);

            if (fileName != null)
            {
                Tileset newTileset = new Tileset();
                // We're going to add this to the end, so we need to see what the last tileset is,
                // get its ID and count, and start this tileset at that number

                var lastTileset = AppState.Self.CurrentTiledMapSave.Tilesets.LastOrDefault();

                uint startingIndex = 1;

                if (lastTileset != null)
                {
                    startingIndex = lastTileset.Firstgid + (uint)lastTileset.GetTileCount();
                }

                newTileset.Firstgid = startingIndex;

                string oldRelative = FileManager.RelativeDirectory;
                FileManager.RelativeDirectory = AppState.Self.TmxFolder;

                bool isFileTsx = FileManager.GetExtension(fileName) == "tsx";

                bool shouldContinue = true;

                if (isFileTsx)
                {

                    newTileset.Source = FileManager.MakeRelative(fileName, AppState.Self.TmxFolder);
                }
                else
                {

                    TilesetWindow window = new TilesetWindow();
                    TilesetViewModel viewModel = new TilesetViewModel();

                    viewModel.Name = FileManager.RemovePath(FileManager.RemoveExtension(fileName));

                    window.DataContext = viewModel;

                    var result = window.ShowDialog();

                    if(result.HasValue && result.Value)
                    {
                        shouldContinue = true;

                        string sourceToSet = fileName;

                        if (viewModel.CopyFile && !FileManager.IsRelativeTo(fileName, AppState.Self.TmxFolder))
                        {
                            try
                            {
                                sourceToSet = AppState.Self.TmxFolder + FileManager.RemovePath(fileName);
                                System.IO.File.Copy(fileName, sourceToSet, overwrite: true);

                            }
                            catch (Exception copyException)
                            {
                                MessageBox.Show("Error copying file:\n" + copyException.Message);
                                shouldContinue = false;
                            }

                        }
                        if (shouldContinue)
                        {

                            sourceToSet = FileManager.MakeRelative(sourceToSet, AppState.Self.TmxFolder);

                            newTileset.Images = new TilesetImage[1];
                            newTileset.Name = viewModel.Name;
                            newTileset.Tilewidth = viewModel.TileWidth;
                            newTileset.Tileheight = viewModel.TileHeight;
                            newTileset.Margin = viewModel.Margin;
                            newTileset.Spacing = viewModel.Spacing;

                            var tilesetImage = new TilesetImage();
                            tilesetImage.Source = sourceToSet;

                            // We could use either the original file or the copied file.
                            // I'll use the original since it's in scope and shouldn't matter
                            var dimensions = ImageHeader.GetDimensions(fileName);

                            tilesetImage.width = dimensions.Width;
                            tilesetImage.height = dimensions.Height;

                            newTileset.Images[0] = tilesetImage;
                        }
                    }
                    else
                    {
                        shouldContinue = false;

                    }
                }
                FileManager.RelativeDirectory = oldRelative;

                if (shouldContinue)
                {
                    AppState.Self.CurrentTiledMapSave.Tilesets.Add(newTileset);

                    // refresh everything:
                    RefreshListBox(newTileset);
                    mTilesetsListBox.SelectedItem = newTileset;

                    UpdateXnaDisplayToTileset();
                    // and make sure the apps are notified about the change
                    if (AnyTileMapChange != null)
                    {
                        AnyTileMapChange(this, null);
                    }
                }
            }
        }
        private void RefreshListBox(Tileset tileset)
        {
            // Does this tileset already exist?
            bool alreadyExists = mTilesetsListBox.Items.Contains(tileset);

            if (alreadyExists)
            {
                var index = mTilesetsListBox.Items.IndexOf(tileset);
                // This should refresh the text, but keep things selected
                mTilesetsListBox.Items[index] = tileset;
            }
            else
            {
                mTilesetsListBox.Items.Add(tileset);
            }
        }
        private ExternalTileSet CloneAndCast(Tileset tileset)
        {
            string container;

            FileManager.XmlSerialize(tileset, out container);

            container = container.Replace("<mapTileset", "<tileset");
            container = container.Replace(@"</mapTileset", "</tileset");

            return FileManager.XmlDeserializeFromString<ExternalTileSet>(container);
        }
Example #9
0
        private static TileCollisions.TileShapeCollection GetTileShapeCollectionForLayer(TMXGlueLib.MapLayer layer, TMXGlueLib.Tileset tileset, float tileDimension, float z)
        {
            TileCollisions.TileShapeCollection toReturn = new TileCollisions.TileShapeCollection();

            toReturn.Name = layer.Name;

            Math.Geometry.AxisAlignedRectangle rectangle = null;
            Math.Geometry.Polygon polygon = null;
            Circle circle;

            var tiles = layer.data[0].tiles;


            bool sortOnY = layer.height > layer.width;


            foreach (var tilesetTile in tileset.Tiles.Where(item => item.Objects?.@object?.Length > 0))
            {
                var tilesetTileGid = tilesetTile.id + tileset.Firstgid;
                foreach (var tilesetObject in tilesetTile.Objects.@object)
                {
                    TiledMapToShapeCollectionConverter.ConvertTiledObjectToFrbShape(tilesetObject, out polygon, out rectangle, out circle);
                    if (rectangle != null)
                    {
                        rectangle.Z = z;
                        if (sortOnY)
                        {
                            for (int y = 0; y < layer.height; y++)
                            {
                                for (int x = 0; x < layer.width; x++)
                                {
                                    AddRectangleCloneAtXY(layer, tileDimension, toReturn, rectangle, tiles, tilesetTileGid, x, y);
                                }
                            }
                        }
                        else
                        {
                            for (int x = 0; x < layer.width; x++)
                            {
                                for (int y = 0; y < layer.height; y++)
                                {
                                    AddRectangleCloneAtXY(layer, tileDimension, toReturn, rectangle, tiles, tilesetTileGid, x, y);
                                }
                            }
                        }
                    }
                    else if (polygon != null)
                    {
                        // For tile polygons we want them to be centered on the tile.
                        // To do this, we shift all points by its position:
                        for (int i = 0; i < polygon.Points.Count; i++)
                        {
                            var point = polygon.Points[i];
                            point.X += polygon.Position.X - tileDimension / 2.0f;
                            point.Y += polygon.Position.Y + tileDimension / 2.0f;

                            polygon.SetPoint(i, point);
                        }

                        polygon.Z = z;

                        if (sortOnY)
                        {
                            for (int y = 0; y < layer.height; y++)
                            {
                                for (int x = 0; x < layer.width; x++)
                                {
                                    AddPolygonCloneAtXY(layer, tileDimension, toReturn, polygon, tiles, tilesetTileGid, x, y);
                                }
                            }
                        }
                        else
                        {
                            for (int x = 0; x < layer.width; x++)
                            {
                                for (int y = 0; y < layer.height; y++)
                                {
                                    AddPolygonCloneAtXY(layer, tileDimension, toReturn, polygon, tiles, tilesetTileGid, x, y);
                                }
                            }
                        }
                    }
                    else if (circle != null)
                    {
                        throw new NotImplementedException("Need to handle circles...");
                    }
                }
            }

            // The values are inserted sorted above for speed, now we set the XAxis - this will also sort but will be fast:
            if (sortOnY)
            {
                toReturn.SortAxis = Math.Axis.Y;
            }
            else
            {
                toReturn.SortAxis = Math.Axis.X;
            }

            toReturn.RefreshAllRepositionDirections();

            return(toReturn);
        }