public void FillTarget(Grid<Tile> target, Point2 offset)
        {
            Point2 patternSize = new Point2(this.pattern.Width, this.pattern.Height);

            // Adjust the offset to positive values only
            if (offset.X < 0) offset.X += (1 + (-offset.X / patternSize.X)) * patternSize.X;
            if (offset.Y < 0) offset.Y += (1 + (-offset.Y / patternSize.Y)) * patternSize.Y;

            // Apply the source tiles to the target area
            for (int y = 0; y < target.Height; y++)
            {
                for (int x = 0; x < target.Width; x++)
                {
                    Point2 wrapped = new Point2(
                        (x + offset.X) % patternSize.X,
                        (y + offset.Y) % patternSize.Y);

                    if (this.shape[wrapped.X, wrapped.Y])
                        target[x, y] = this.pattern[wrapped.X, wrapped.Y];
                    else
                        target[x, y] = new Tile();
                }
            }
        }
Beispiel #2
0
        void ISerializeExplicit.ReadData(IDataReader reader)
        {
            // Determine which Tile data version we're dealing with
            int version;
            try { reader.ReadValue("version", out version); }
            catch (Exception) { version = Serialize_Version_Unknown; }

            // Read the compressed data and unpack it
            if (version >= Serialize_MinVersion &&
                version <= Serialize_MaxVersion)
            {
                byte[] compressedData;
                reader.ReadValue("data", out compressedData);

                using (MemoryStream compressedStream = new MemoryStream(compressedData))
                using (GZipStream stream = new GZipStream(compressedStream, CompressionMode.Decompress))
                using (BinaryReader streamReader = new BinaryReader(stream))
                {
                    int width = streamReader.ReadInt32();
                    int height = streamReader.ReadInt32();

                    Tile[] rawData = new Tile[width * height];
                    switch (version)
                    {
                        case Serialize_Version_ZippedBytes: this.ReadBinVersionFirst(streamReader, rawData); break;
                        case Serialize_Version_DepthOffset: this.ReadBinVersionDepth(streamReader, rawData); break;
                        case Serialize_Version_AutoTile: this.ReadBinVersionAutoTile(streamReader, rawData); break;
                    }

                    this.tiles = new Grid<Tile>(width, height, rawData);
                }
            }
            else
            {
                throw new NotSupportedException(string.Format(
                    "Unknown TilemapData serialization version '{0}'. Can't load tilemap data.",
                    version));
            }
        }
Beispiel #3
0
 private void ReadBinVersionFirst(BinaryReader reader, Tile[] target)
 {
     for (int i = 0; i < target.Length; i++)
     {
         target[i].BaseIndex = reader.ReadInt32();
         target[i].Index = target[i].BaseIndex;
     }
 }
Beispiel #4
0
 private void ReadBinVersionAutoTile(BinaryReader reader, Tile[] target)
 {
     for (int i = 0; i < target.Length; i++)
     {
         target[i].BaseIndex = reader.ReadInt32();
         target[i].DepthOffset = reader.ReadInt16();
         target[i].AutoTileCon = (TileConnection)reader.ReadByte();
         target[i].Index = target[i].BaseIndex;
     }
 }
Beispiel #5
0
        /// <summary>
        /// Resolves the <see cref="Index"/> values of the specified <see cref="Tile"/> grid area, given the grid's raw data block.
        /// </summary>
        /// <param name="tileGridData"></param>
        /// <param name="beginX"></param>
        /// <param name="beginY"></param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <param name="stride"></param>
        /// <param name="tilesetRes"></param>
        public static void ResolveIndices(Tile[] tileGridData, int stride, int beginX, int beginY, int width, int height, ContentRef<Tileset> tileset)
        {
            if (tileset.Res == null) throw new ArgumentNullException("tileset");
            if (!tileset.Res.Compiled) throw new InvalidOperationException("The specified Tileset needs to be compiled first.");

            Tileset tilesetRes = tileset.Res;
            TileInfo[] tileData = tilesetRes.TileData.Data;
            int tileCount = tilesetRes.TileCount;
            for (int y = beginY; y < beginY + height; y++)
            {
                for (int x = beginX; x < beginX + width; x++)
                {
                    int i = y * stride + x;
                    int baseIndex = MathF.Clamp(tileGridData[i].BaseIndex, 0, tileCount - 1);
                    int autoTileIndex = tileData[baseIndex].AutoTileLayer - 1;
                    TilesetAutoTileInfo autoTile = autoTileIndex > -1 ? tilesetRes.AutoTileData[autoTileIndex] : null;

                    tileGridData[i].ResolveIndex(autoTile);
                }
            }
        }
Beispiel #6
0
        public override void Do()
        {
            if (this.oldData == null)
            {
                this.oldData = new Grid<Tile>[this.tilemaps.Length];
                for (int i = 0; i < this.tilemaps.Length; i++)
                {
                    this.oldData[i] = new Grid<Tile>(this.tilemaps[i].BeginUpdateTiles());
                    this.tilemaps[i].EndUpdateTiles(0, 0, 0, 0);
                }
            }

            for (int i = 0; i < this.tilemaps.Length; i++)
            {
                // Determine the tile which should fill the new area
                Tile fillTile = new Tile(TilemapsSetupUtility.GetDefaultTileIndex(
                    this.tilemaps[i].Tileset.Res,
                    true));

                // Determine the up to four regions that we will add
                int leftAdd = 0;
                int rightAdd = 0;
                int topAdd = 0;
                int bottomAdd = 0;
                {
                    if (this.origin == Alignment.Right ||
                        this.origin == Alignment.TopRight ||
                        this.origin == Alignment.BottomRight)
                        leftAdd = this.newSize.X - this.tilemaps[i].Size.X;
                    else if (
                        this.origin == Alignment.Center ||
                        this.origin == Alignment.Top ||
                        this.origin == Alignment.Bottom)
                        leftAdd = (this.newSize.X - this.tilemaps[i].Size.X) / 2;

                    if (this.origin == Alignment.Bottom ||
                        this.origin == Alignment.BottomLeft ||
                        this.origin == Alignment.BottomRight)
                        topAdd = this.newSize.Y - this.tilemaps[i].Size.Y;
                    else if (
                        this.origin == Alignment.Center ||
                        this.origin == Alignment.Left ||
                        this.origin == Alignment.Right)
                        topAdd = (this.newSize.Y - this.tilemaps[i].Size.Y) / 2;

                    rightAdd = (this.newSize.X - this.tilemaps[i].Size.X) - leftAdd;
                    bottomAdd = (this.newSize.Y - this.tilemaps[i].Size.Y) - topAdd;
                }

                // Resize the tilemap
                this.tilemaps[i].Resize(this.newSize.X, this.newSize.Y, this.origin);

                // If we have a non-default filling tile, use it
                if (!object.Equals(fillTile, default(Tile)))
                {
                    Grid<Tile> data = this.tilemaps[i].BeginUpdateTiles();
                    if (topAdd    > 0) data.Fill(fillTile, 0, 0, data.Width, topAdd);
                    if (leftAdd   > 0) data.Fill(fillTile, 0, 0, leftAdd, data.Height);
                    if (bottomAdd > 0) data.Fill(fillTile, 0, data.Height - bottomAdd, data.Width, bottomAdd);
                    if (rightAdd  > 0) data.Fill(fillTile, data.Width - rightAdd, 0, rightAdd, data.Height);
                    this.tilemaps[i].EndUpdateTiles(0, 0, 0, 0);
                }
            }

            this.OnNotifyPropertyChanged();
        }
Beispiel #7
0
 /// <summary>
 /// Resolves the <see cref="Index"/> values of the specified tile array segment.
 /// </summary>
 /// <param name="tiles"></param>
 /// <param name="index"></param>
 /// <param name="count"></param>
 /// <param name="tileset"></param>
 public static void ResolveIndices(Tile[] tiles, int index, int count, ContentRef<Tileset> tileset)
 {
     ResolveIndices(tiles, 0, index, 0, count, 1, tileset);
 }
Beispiel #8
0
 private static bool _FloodFill_TilesEqual(Tile baseTile, Tile otherTile)
 {
     return baseTile.BaseIndex == otherTile.BaseIndex;
 }
Beispiel #9
0
 private static bool _FloodFill_IsCandidate(Grid<bool> fillBuffer, Grid<Tile> tiles, Point2 pos, Tile baseTile)
 {
     return !fillBuffer[pos.X, pos.Y] && _FloodFill_TilesEqual(baseTile, tiles[pos.X, pos.Y]);
 }
Beispiel #10
0
 private static Point2 _FloodFillTiles_FindTopLeft(Grid<bool> fillBuffer, Grid<Tile> tiles, Point2 pos, Tile baseTile)
 {
     // Find the topleft-most connected matching tile
     while(true)
     {
         Point2 origin = pos;
         while (pos.Y != 0 && _FloodFill_IsCandidate(fillBuffer, tiles, new Point2(pos.X, pos.Y - 1), baseTile)) pos.Y--;
         while (pos.X != 0 && _FloodFill_IsCandidate(fillBuffer, tiles, new Point2(pos.X - 1, pos.Y), baseTile)) pos.X--;
         if (pos == origin) break;
     }
     return pos;
 }
Beispiel #11
0
        private static bool _FloodFillTiles(Grid<bool> fillBuffer, Grid<Tile> tiles, Point2 pos, Tile baseTile, ref int maxTileCount, ref Point2 fillAreaTopLeft, ref Point2 fillAreaSize)
        {
            // Adjust the fill area to the position we'll be starting to fill
            fillAreaSize.X += Math.Max(fillAreaTopLeft.X - pos.X, 0);
            fillAreaSize.Y += Math.Max(fillAreaTopLeft.Y - pos.Y, 0);
            fillAreaTopLeft.X = Math.Min(fillAreaTopLeft.X, pos.X);
            fillAreaTopLeft.Y = Math.Min(fillAreaTopLeft.Y, pos.Y);

            // Since the top and left of the current tile are blocking the fill operation, proceed down and right
            int lastRowLength = 0;
            do
            {
                Point2 rowPos = pos;
                int rowLength = 0;
                bool firstRow = (lastRowLength == 0);

                // Narrow scan line width on the left when necessary
                if (!firstRow && !_FloodFill_IsCandidate(fillBuffer, tiles, pos, baseTile))
                {
                    while (lastRowLength > 1)
                    {
                        pos.X++;
                        lastRowLength--;

                        if (_FloodFill_IsCandidate(fillBuffer, tiles, pos, baseTile))
                            break;
                    }

                    rowPos.X = pos.X;
                }
                // Expand scan line width to the left when necessary
                else
                {
                    for (; pos.X != 0 && _FloodFill_IsCandidate(fillBuffer, tiles, new Point2(pos.X - 1, pos.Y), baseTile); rowLength++, lastRowLength++)
                    {
                        pos.X--;
                        fillBuffer[pos.X, pos.Y] = true;

                        // Adjust the fill area
                        fillAreaSize.X += Math.Max(fillAreaTopLeft.X - pos.X, 0);
                        fillAreaTopLeft.X = Math.Min(fillAreaTopLeft.X, pos.X);

                        // If something above the current scan line is free, handle it recursively
                        if (pos.Y != 0 && _FloodFill_IsCandidate(fillBuffer, tiles, new Point2(pos.X, pos.Y - 1), baseTile))
                        {
                            // Find the topleft-most tile to start with
                            Point2 targetPos = new Point2(pos.X, pos.Y - 1);
                            targetPos = _FloodFillTiles_FindTopLeft(fillBuffer, tiles, targetPos, baseTile);
                            if (!_FloodFillTiles(fillBuffer, tiles, targetPos, baseTile, ref maxTileCount, ref fillAreaTopLeft, ref fillAreaSize))
                                return false;
                        }
                    }
                }

                // Fill the current row
                for (; rowPos.X < tiles.Width && _FloodFill_IsCandidate(fillBuffer, tiles, rowPos, baseTile); rowLength++, rowPos.X++)
                {
                    fillBuffer[rowPos.X, rowPos.Y] = true;
                }
                maxTileCount -= rowLength;
                if (maxTileCount < 0) return false;

                // Adjust the fill area
                fillAreaSize.X = Math.Max(fillAreaSize.X, rowPos.X - fillAreaTopLeft.X);

                // If the current row is shorter than the previous, see if there are
                // disconnected pixels below the (filled) previous row left to handle
                if (rowLength < lastRowLength)
                {
                    for (int end = pos.X + lastRowLength; ++rowPos.X < end; )
                    {
                        // Recursively handle the disconnected below-bottom pixels of the last row
                        if (_FloodFill_IsCandidate(fillBuffer, tiles, rowPos, baseTile))
                        {
                            if (!_FloodFillTiles(fillBuffer, tiles, rowPos, baseTile, ref maxTileCount, ref fillAreaTopLeft, ref fillAreaSize))
                                return false;
                        }
                    }
                }
                // If the current row is longer than the previous, see if there are
                // top pixels above this one that are disconnected from the last row
                else if (rowLength > lastRowLength && pos.Y != 0)
                {
                    for (int prevRowX = pos.X + lastRowLength; ++prevRowX < rowPos.X; )
                    {
                        // Recursively handle the disconnected pixels of the last row
                        if (_FloodFill_IsCandidate(fillBuffer, tiles, new Point2(prevRowX, pos.Y - 1), baseTile))
                        {
                            // Find the topleft-most tile to start with
                            Point2 targetPos = new Point2(prevRowX, pos.Y - 1);
                            targetPos = _FloodFillTiles_FindTopLeft(fillBuffer, tiles, targetPos, baseTile);
                            if (!_FloodFillTiles(fillBuffer, tiles, targetPos, baseTile, ref maxTileCount, ref fillAreaTopLeft, ref fillAreaSize))
                                return false;
                        }
                    }
                }

                lastRowLength = rowLength;
                pos.Y++;

                // Adjust the fill area
                fillAreaSize.Y = Math.Max(fillAreaSize.Y, pos.Y - fillAreaTopLeft.Y);
            }
            while (lastRowLength != 0 && pos.Y < tiles.Height);

            return true;
        }
Beispiel #12
0
 /// <summary>
 /// Sets a single tile at the specified position.
 /// </summary>
 /// <param name="x"></param>
 /// <param name="y"></param>
 /// <param name="tile"></param>
 public void SetTile(int x, int y, Tile tile)
 {
     this.tileData.Tiles[x, y] = tile;
     this.OnTilesChanged(x, y, 1, 1);
 }