예제 #1
0
        /// <summary>
        /// Sets the given isometric tile as the parent of this cell.
        /// </summary>
        /// <param name="tile">The isometric tile to set as the parent of this cell.</param>
        /// <param name="isoIndices">The indices of this cell inside the parent isometric tile.</param>
        public void SetIsoTile(IsoTile tile, RCIntVector isoIndices)
        {
            if (tile == null)
            {
                throw new ArgumentNullException("tile");
            }
            if (isoIndices == RCIntVector.Undefined)
            {
                throw new ArgumentNullException("isoIndices");
            }
            if (tile.ParentMap != this.parentMap)
            {
                throw new InvalidOperationException("cells and their parent isometric tile must have the same parent map!");
            }
            if (this.parentMap.Status != MapStructure.MapStatus.Initializing)
            {
                throw new InvalidOperationException(string.Format("Invalid operation! Map status: {0}", this.parentMap.Status));
            }
            if (this.parentIsoTile != null)
            {
                throw new InvalidOperationException("Parent isometric tile already set!");
            }

            this.parentIsoTile = tile;
            this.isoIndices    = isoIndices;
        }
예제 #2
0
        /// <summary>
        /// Validates whether the neighbour of this isometric tile in the given direction satisfies the
        /// constraints of the tileset.
        /// </summary>
        /// <param name="dir">The direction of the neighbour to validate.</param>
        private void ValidateNeighbour(MapDirection dir)
        {
            IsoTile neighbour = this.neighbours[(int)dir];

            if (neighbour != null)
            {
                /// To simplify the algorithm, we use terrain combinations rotated to MapDirection.NorthEast.
                TerrainCombination thisCombRot  = MapHelper.RotateTerrainCombination(this.type.Combination, dir, MapDirection.NorthEast);
                TerrainCombination neighCombRot = MapHelper.RotateTerrainCombination(neighbour.type.Combination, dir, MapDirection.NorthEast);

                /// Generate the terrain-NESW array for this tile and the neighbour.
                ITerrainType[] thisNESWRot  = MapHelper.GetTerrainNESW(this.type.TerrainA, this.type.TerrainB, thisCombRot);
                ITerrainType[] neighNESWRot = MapHelper.GetTerrainNESW(neighbour.type.TerrainA, neighbour.type.TerrainB, neighCombRot);

                /// Check the generated terrain-NESW arrays.
                if (thisNESWRot[0] != neighNESWRot[3] || thisNESWRot[1] != neighNESWRot[2])
                {
                    throw new MapException(string.Format("Invalid neighbours at {0} and {1}!", this.mapCoords, neighbour.mapCoords));
                }

                /// Check whether the given direction satisfies the transition-length constraint.
                if (this.type.TerrainB != null && this.type.TerrainB.TransitionLength > 0 &&
                    thisNESWRot[0] == this.type.TerrainA && thisNESWRot[1] == this.type.TerrainA)
                {
                    int     remaining = this.type.TerrainB.TransitionLength;
                    IsoTile currTile  = neighbour;
                    while (currTile != null && remaining > 0)
                    {
                        /// Generate the terrain-NESW array of the currently checked tile.
                        TerrainCombination currCombRot = MapHelper.RotateTerrainCombination(currTile.type.Combination, dir, MapDirection.NorthEast);
                        ITerrainType[]     currNESWRot = MapHelper.GetTerrainNESW(currTile.type.TerrainA, currTile.type.TerrainB, currCombRot);

                        /// Check if the currently checked tile is part of the transition.
                        if (currNESWRot[0] != this.type.TerrainA || currNESWRot[1] != this.type.TerrainA ||
                            currNESWRot[2] != this.type.TerrainA || currNESWRot[3] != this.type.TerrainA)
                        {
                            /// No, it's not part of the transition. We have to check whether the upcoming terrain type
                            /// is another child of TerrainA or not.
                            if (currNESWRot[2] == this.type.TerrainA && currNESWRot[3] == this.type.TerrainA &&
                                (currNESWRot[0] == this.type.TerrainA || currNESWRot[0].Parent == this.type.TerrainA) &&
                                (currNESWRot[1] == this.type.TerrainA || currNESWRot[1].Parent == this.type.TerrainA))
                            {
                                /// It's another child of TerrainA, no contradiction with the tileset -> OK.
                                break;
                            }
                            else
                            {
                                /// It's not a child of TerrainA -> Error.
                                throw new MapException(string.Format("Invalid transition from {0} in direction {1}! Length must be at least {2}!", this.mapCoords, dir, this.type.TerrainB.TransitionLength));
                            }
                        }

                        /// Yes, it's part of the transition. We can switch to the next tile in the same direction.
                        currTile = currTile.neighbours[(int)dir];
                        remaining--;
                    }
                }
            }
        }
예제 #3
0
        /// <summary>
        /// Using this method the isometric tiles notify the MapStructure object if they type has been changed during a
        /// tile exchanging operation.
        /// </summary>
        /// <param name="tile">The isometric tile whose type has been changed.</param>
        public void OnIsoTileExchanged(IsoTile tile)
        {
            if (this.status != MapStatus.ExchangingTiles)
            {
                throw new InvalidOperationException(string.Format("Invalid operation! Map status: {0}", this.status));
            }
            if (tile == null)
            {
                throw new ArgumentNullException("tile");
            }

            this.tmpReplacedTiles.Add(tile);
        }
예제 #4
0
        /// <summary>
        /// Detaches the appropriate neighbours and cells of this isometric tile depending on the size of the map.
        /// </summary>
        public void DetachAtSize()
        {
            if (this.parentMap.Status != MapStructure.MapStatus.Opening)
            {
                throw new InvalidOperationException(string.Format("Invalid operation! Map status: {0}", this.parentMap.Status));
            }

            RCNumVector edges = new RCNumVector((RCNumber)(this.parentMap.Size.X * 2 - 1) / 2,
                                                (RCNumber)(this.parentMap.Size.Y * 2 - 1) / 2);

            /// Detach the appropriate neighbours.
            for (int dir = 0; dir < this.neighbours.Length; dir++)
            {
                if (this.neighbours[dir] != null)
                {
                    IsoTile     neighbour  = this.neighbours[dir];
                    RCNumVector quadCoords = MapStructure.QuadIsoTransform.TransformBA(neighbour.mapCoords);
                    if (quadCoords.X > edges.X || quadCoords.Y > edges.Y)
                    {
                        this.detachedNeighbours.Add(new Tuple <IsoTile, MapDirection>(this.neighbours[dir], (MapDirection)dir));
                        this.neighbours[dir] = null;
                    }
                }
            }

            /// Detach the appropriate cells.
            for (int col = 0; col < MapStructure.QUAD_PER_ISO_VERT * MapStructure.NAVCELL_PER_QUAD; col++)
            {
                for (int row = 0; row < MapStructure.QUAD_PER_ISO_HORZ * MapStructure.NAVCELL_PER_QUAD; row++)
                {
                    if (this.cells[col, row] != null &&
                        (this.cells[col, row].MapCoords.X >= this.parentMap.Size.X * MapStructure.NAVCELL_PER_QUAD ||
                         this.cells[col, row].MapCoords.Y >= this.parentMap.Size.Y * MapStructure.NAVCELL_PER_QUAD))
                    {
                        this.detachedCells.Add(this.cells[col, row]);
                        this.cells[col, row] = null;
                    }
                }
            }

            /// Detach the appropriate cutting quadratic tiles.
            foreach (QuadTile cuttingQuadTile in this.cuttingQuadTiles)
            {
                if (cuttingQuadTile.MapCoords.X >= this.parentMap.Size.X || cuttingQuadTile.MapCoords.Y >= this.parentMap.Size.Y)
                {
                    this.detachedCuttingQuadTiles.Add(cuttingQuadTile);
                }
            }
        }
예제 #5
0
        /// <summary>
        /// Builds up the relationships between the cells and isometric tiles.
        /// </summary>
        private void SetNavCellIsoIndices()
        {
            foreach (Cell cell in this.cells)
            {
                RCNumVector isoCoords    = MapStructure.NavCellIsoTransform.TransformAB(cell.MapCoords);
                RCIntVector isoCoordsInt = isoCoords.Round();
                IsoTile     isoTile      = this.isometricTiles[isoCoordsInt];

                RCNumVector isoCoordsRel  = isoCoords - isoTile.MapCoords;
                RCNumVector isoIndices    = MapStructure.NavCellIsoTransform.TransformBA(isoCoordsRel);
                RCIntVector isoIndicesInt = isoIndices.Round();

                cell.SetIsoTile(isoTile, isoIndicesInt);
                isoTile.SetCell(cell, isoIndicesInt);
            }
        }
예제 #6
0
        /// <summary>
        /// Constructs a quadratic tile.
        /// </summary>
        /// <param name="map">The map that this quadratic tile belongs to.</param>
        /// <param name="primaryIsoTile">Primary isometric tile.</param>
        /// <param name="secondaryIsoTile">Secondary isometric tile or null if doesn't exist.</param>
        /// <param name="mapCoords">The map coordinates of this quadratic tile.</param>
        public QuadTile(MapStructure map, IsoTile primaryIsoTile, IsoTile secondaryIsoTile, RCIntVector mapCoords)
        {
            if (map == null)
            {
                throw new ArgumentNullException("map");
            }
            if (primaryIsoTile == null)
            {
                throw new ArgumentNullException("isoTile");
            }
            if (mapCoords == RCIntVector.Undefined)
            {
                throw new ArgumentNullException("mapCoords");
            }
            if (mapCoords.X < 0 || mapCoords.X >= MapStructure.MAX_MAPSIZE || mapCoords.Y < 0 || mapCoords.Y >= MapStructure.MAX_MAPSIZE)
            {
                throw new ArgumentOutOfRangeException("mapCoords");
            }

            this.parentMap          = map;
            this.primaryIsoTile     = primaryIsoTile;
            this.secondaryIsoTile   = secondaryIsoTile;
            this.terrainObject      = null;
            this.mapCoords          = mapCoords;
            this.neighbours         = new QuadTile[8];
            this.detachedNeighbours = new List <Tuple <QuadTile, MapDirection> >();
            this.isBuildableCache   = new CachedValue <bool>(this.CalculateBuildabilityFlag);
            this.groundLevelCache   = new CachedValue <int>(this.CalculateGroundLevel);

            this.cells = new Cell[MapStructure.NAVCELL_PER_QUAD, MapStructure.NAVCELL_PER_QUAD];
            for (int col = 0; col < MapStructure.NAVCELL_PER_QUAD; col++)
            {
                for (int row = 0; row < MapStructure.NAVCELL_PER_QUAD; row++)
                {
                    this.cells[col, row] = new Cell(this.parentMap, this, new RCIntVector(col, row));
                }
            }

            this.primaryIsoTile.SetCuttingQuadTile(this);
            if (this.secondaryIsoTile != null)
            {
                this.secondaryIsoTile.SetCuttingQuadTile(this);
            }
        }
예제 #7
0
        /// <summary>
        /// Sets the given isometric tile as a neighbour of this isometric tile in the given direction.
        /// </summary>
        /// <param name="neighbour">The isometric tile to be set as a neighbour of this.</param>
        /// <param name="direction">The direction of the new neighbour.</param>
        public void SetNeighbour(IsoTile neighbour, MapDirection direction)
        {
            if (neighbour == null)
            {
                throw new ArgumentNullException("neighbour");
            }
            if (neighbour.parentMap != this.parentMap)
            {
                throw new InvalidOperationException("Neighbour isometric tiles must have the same parent map!");
            }
            if (this.parentMap.Status != MapStructure.MapStatus.Initializing)
            {
                throw new InvalidOperationException(string.Format("Invalid operation! Map status: {0}", this.parentMap.Status));
            }
            if (this.neighbours[(int)direction] != null)
            {
                throw new InvalidOperationException(string.Format("Neighbour in direction {0} already set!", direction));
            }

            this.neighbours[(int)direction] = neighbour;
        }
예제 #8
0
        /// <summary>
        /// Attaches the part of the map structure that is out of its size.
        /// </summary>
        private void AttachAtSize()
        {
            /// Attach the cells along the edge of the map.
            for (int row = 0; row < this.size.Y * NAVCELL_PER_QUAD; row++)
            {
                this.cells[this.size.X * NAVCELL_PER_QUAD - 1, row].AttachNeighboursAtSize();
            }
            for (int col = 0; col < this.size.X * NAVCELL_PER_QUAD; col++)
            {
                this.cells[col, this.size.Y * NAVCELL_PER_QUAD - 1].AttachNeighboursAtSize();
            }

            /// Attach the quadratic tiles along the edge of the map.
            for (int row = 0; row < this.size.Y; row++)
            {
                this.quadTiles[this.size.X - 1, row].AttachNeighboursAtSize();
            }
            for (int col = 0; col < this.size.X; col++)
            {
                this.quadTiles[col, this.size.Y - 1].AttachNeighboursAtSize();
            }

            RCNumVector outerEdges = new RCNumVector((RCNumber)(this.size.X * 2 - 1) / 2, (RCNumber)(this.size.Y * 2 - 1) / 2);
            RCNumVector innerEdges = new RCNumVector(((RCNumber)(this.size.X * 2 - 1) / 2) - MapStructure.QUAD_PER_ISO_VERT_HALF,
                                                     ((RCNumber)(this.size.Y * 2 - 1) / 2) - MapStructure.QUAD_PER_ISO_HORZ_HALF);

            /// Attach isometric tiles along the inner vertical edge.
            for (RCNumber innerVertical = MapStructure.QUAD_PER_ISO_HORZ_HALF - (RCNumber)1 / (RCNumber)2;
                 innerVertical <= innerEdges.Y;
                 innerVertical += MapStructure.QUAD_PER_ISO_HORZ)
            {
                RCNumVector quadCoords = new RCNumVector(innerEdges.X, innerVertical);
                RCIntVector isoCoords  = MapStructure.QuadIsoTransform.TransformAB(quadCoords).Round();
                IsoTile     tile       = this.isometricTiles[isoCoords];
                tile.AttachAtSize();
            }

            /// Attach isometric tiles along the outer vertical edge.
            for (RCNumber outerVertical = -((RCNumber)1 / (RCNumber)2);
                 outerVertical <= outerEdges.Y;
                 outerVertical += MapStructure.QUAD_PER_ISO_HORZ)
            {
                RCNumVector quadCoords = new RCNumVector(outerEdges.X, outerVertical);
                RCIntVector isoCoords  = MapStructure.QuadIsoTransform.TransformAB(quadCoords).Round();
                IsoTile     tile       = this.isometricTiles[isoCoords];
                tile.AttachAtSize();
            }

            /// Attach isometric tiles along the inner horizontal edge.
            for (RCNumber innerHorizontal = MapStructure.QUAD_PER_ISO_VERT_HALF - ((RCNumber)1 / (RCNumber)2);
                 innerHorizontal <= innerEdges.X;
                 innerHorizontal += MapStructure.QUAD_PER_ISO_VERT)
            {
                RCNumVector quadCoords = new RCNumVector(innerHorizontal, innerEdges.Y);
                RCIntVector isoCoords  = MapStructure.QuadIsoTransform.TransformAB(quadCoords).Round();
                IsoTile     tile       = this.isometricTiles[isoCoords];
                tile.AttachAtSize();
            }

            /// Attach isometric tiles along the outer horizontal edge.
            for (RCNumber outerHorizontal = -((RCNumber)1 / (RCNumber)2);
                 outerHorizontal <= outerEdges.X;
                 outerHorizontal += MapStructure.QUAD_PER_ISO_VERT)
            {
                RCNumVector quadCoords = new RCNumVector(outerHorizontal, outerEdges.Y);
                RCIntVector isoCoords  = MapStructure.QuadIsoTransform.TransformAB(quadCoords).Round();
                IsoTile     tile       = this.isometricTiles[isoCoords];
                tile.AttachAtSize();
            }
        }
예제 #9
0
        /// <summary>
        /// Initializes the structure of this map.
        /// </summary>
        public void Initialize()
        {
            if (this.status != MapStatus.Initializing)
            {
                throw new InvalidOperationException(string.Format("Invalid operation! Map status: {0}", this.status));
            }

            /// Create the quadratic and isometric tiles.
            RCIntVector navCellPerQuadVect = new RCIntVector(NAVCELL_PER_QUAD, NAVCELL_PER_QUAD);

            for (int col = 0; col < MAX_MAPSIZE; col++)
            {
                for (int row = 0; row < MAX_MAPSIZE; row++)
                {
                    /// Calculate the coordinates of the isometric tiles that contain the corner cells of the current quadratic tile.
                    /// Order of the cornerIsoCoords array: NorthWest, NorthEast, SouthEast, SouthWest.
                    RCIntVector   quadTileCoords  = new RCIntVector(col, row);
                    RCIntVector[] cornerIsoCoords = new RCIntVector[4]
                    {
                        MapStructure.NavCellIsoTransform.TransformAB(quadTileCoords * navCellPerQuadVect + new RCIntVector(0, 0)).Round(),
                        MapStructure.NavCellIsoTransform.TransformAB(quadTileCoords * navCellPerQuadVect + new RCIntVector(NAVCELL_PER_QUAD - 1, 0)).Round(),
                        MapStructure.NavCellIsoTransform.TransformAB(quadTileCoords * navCellPerQuadVect + new RCIntVector(NAVCELL_PER_QUAD - 1, NAVCELL_PER_QUAD - 1)).Round(),
                        MapStructure.NavCellIsoTransform.TransformAB(quadTileCoords * navCellPerQuadVect + new RCIntVector(0, NAVCELL_PER_QUAD - 1)).Round()
                    };

                    /// Create the isometric tiles.
                    int     isoTileACount = 0, isoTileBCount = 0;
                    IsoTile isoTileA = null, isoTileB = null;
                    for (int cornerIdx = 0; cornerIdx < 4; cornerIdx++)
                    {
                        if (isoTileA == null)
                        {
                            isoTileA = this.isometricTiles.ContainsKey(cornerIsoCoords[cornerIdx])
                                     ? this.isometricTiles[cornerIsoCoords[cornerIdx]]
                                     : new IsoTile(this, cornerIsoCoords[cornerIdx]);
                            this.isometricTiles[cornerIsoCoords[cornerIdx]] = isoTileA;
                            isoTileACount++;
                        }
                        else if (cornerIsoCoords[cornerIdx] == isoTileA.MapCoords)
                        {
                            isoTileACount++;
                        }
                        else if (isoTileB == null)
                        {
                            isoTileB = this.isometricTiles.ContainsKey(cornerIsoCoords[cornerIdx])
                                     ? this.isometricTiles[cornerIsoCoords[cornerIdx]]
                                     : new IsoTile(this, cornerIsoCoords[cornerIdx]);
                            this.isometricTiles[cornerIsoCoords[cornerIdx]] = isoTileB;
                            isoTileBCount++;
                        }
                        else if (cornerIsoCoords[cornerIdx] == isoTileB.MapCoords)
                        {
                            isoTileBCount++;
                        }
                        else
                        {
                            throw new InvalidOperationException("Unexpected case!");
                        }
                    }

                    QuadTile quadTile = new QuadTile(this,
                                                     isoTileACount >= isoTileBCount ? isoTileA : isoTileB,
                                                     isoTileACount < isoTileBCount ? isoTileA : isoTileB,
                                                     quadTileCoords);
                    this.quadTiles[quadTileCoords.X, quadTileCoords.Y] = quadTile;
                }
            }

            /// Buildup the structure of the map.
            this.SetIsoNeighbours();
            this.SetQuadNeighbours();
            this.SetNavCellNeighbours();
            this.SetNavCellIsoIndices();
            this.status = MapStatus.Closed;
        }