Ejemplo n.º 1
0
        /// <summary>
        /// Helper function to do the work needed to create a single new extrance/exit based on the current tile being walked,
        /// and the Tile at the start of the sequence.
        /// </summary>
        /// <param name="cluster">The cluster we are evaluating. Hitting a Tile outside this cluster will end that wall.</param>
        /// <param name="tile">The current tile to evaluate.</param>
        /// <param name="sequeceStart">The tile which started this sequence. Use null to start.</param>
        /// <param name="dirWalk">The direction to walk along the wall.</param>
        /// <param name="dirCheck">The direction of the neighbouring Cluster to check. It isn't enough to check outselves; the neighbour may be blocking travel.</param>
        /// <param name="dirNeighbourCluster">Same as dirCheck but using the enum that Clusters understand.</param>
        /// <param name="removeSelf">True if the tile that triggered this call should not be included in the sequence. Useful if this sequence ended because you hit a wall.</param>
        private void CreateEntrance(Cluster cluster, Level.Tile tile, ref Level.Tile sequenceStart, Level.Tile.AdjacentTileDir dirWalk, Level.Tile.AdjacentTileDir dirCheck, Cluster.AdjacentClusterDirections dirNeighbourCluster, Boolean removeSelf)
        {
            // Find the center point between the tile at the start of the sequence of enpty tiles
            // and the current tile.
            Vector2 sequenceVector = tile.mCollisionRect.pCenterPoint - sequenceStart.mCollisionRect.pCenterPoint;

            // If we enter this block by hitting a wall, we need to remove that a Tile length from our
            // calculations since that wall is not part of the entrance/exit.
            if (removeSelf)
            {
                sequenceVector -= Vector2.Normalize(sequenceVector) * new Vector2(cluster.pTileDimensions.X, cluster.pTileDimensions.Y);
            }

            // If the sequence is long enough, instead of putting a GraphNode in the center, create 2 GraphNode objects,
            // and place them at opposite ends of the Sequence. This is recommended by the original HPA* white paper.
            if (sequenceVector.LengthSquared() >= (mClusterSize * mGetMapInfoMsg.mInfo_Out.mMapWidth * 0.5f))
            {
                // Add the length of the Sequence to the starting point to get our ending position.
                Vector2 end = (sequenceVector) + sequenceStart.mCollisionRect.pCenterPoint;

                // We need to find the tile at that position because our GraphNode depends on that data.
                mGetTileAtPositionMsg.mPosition_In = end;
                WorldManager.pInstance.pCurrentLevel.OnMessage(mGetTileAtPositionMsg);

                System.Diagnostics.Debug.Assert(null != mGetTileAtPositionMsg.mTile_Out, "Unable to find tile.");

                CreateEntranceNodes(
                    cluster,
                    cluster.pNeighbouringClusters[(Int32)dirNeighbourCluster],
                    mGetTileAtPositionMsg.mTile_Out,
                    mGetTileAtPositionMsg.mTile_Out.mAdjecentTiles[(Int32)dirCheck]);

                CreateEntranceNodes(
                    cluster,
                    cluster.pNeighbouringClusters[(Int32)dirNeighbourCluster],
                    sequenceStart,
                    sequenceStart.mAdjecentTiles[(Int32)dirCheck]);
            }
            else
            {
                // Add half the length in order to put us in the middle of the sequence.
                Vector2 middle = (sequenceVector * 0.5f) + sequenceStart.mCollisionRect.pCenterPoint;

                // We need to find the tile at that position because our GraphNode depends on that data.
                mGetTileAtPositionMsg.mPosition_In = middle;
                WorldManager.pInstance.pCurrentLevel.OnMessage(mGetTileAtPositionMsg);

                System.Diagnostics.Debug.Assert(null != mGetTileAtPositionMsg.mTile_Out, "Unable to find tile.");

                CreateEntranceNodes(
                    cluster,
                    cluster.pNeighbouringClusters[(Int32)dirNeighbourCluster],
                    mGetTileAtPositionMsg.mTile_Out,
                    mGetTileAtPositionMsg.mTile_Out.mAdjecentTiles[(Int32)dirCheck]);
            }

            // Start a new sequence.
            sequenceStart = null;
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Recursive algorithm for finding all entraces/exits in a cluster, and creating GraphNodes at those points
        /// in neighbouring Clusters and linking them together. Has logic to only create on entrance/exit per concurent
        /// set of empty tiles.
        /// </summary>
        /// <param name="cluster">The cluster we are evaluating. Hitting a Tile outside this cluster will end that wall.</param>
        /// <param name="tile">The current tile to evaluate.</param>
        /// <param name="sequeceStart">The tile which started this sequence. Use null to start.</param>
        /// <param name="dirWalk">The direction to walk along the wall.</param>
        /// <param name="dirCheck">The direction of the neighbouring Cluster to check. It isn't enough to check outselves; the neighbour may be blocking travel.</param>
        /// <param name="dirNeighbourCluster">Same as dirCheck but using the enum that Clusters understand.</param>
        /// <returns>The last tile visited on this wall. Useful for walking an entire perimeter.</returns>
        private Level.Tile WalkWall(Cluster cluster, Level.Tile tile, Level.Tile sequeceStart, Level.Tile.AdjacentTileDir dirWalk, Level.Tile.AdjacentTileDir dirCheck, Cluster.AdjacentClusterDirections dirNeighbourCluster)
        {
            // Get the Tile in the neighbouring Cluster. It being solid creates a wall just the same as
            // if the tile in this Cluster is solid.
            Level.Tile adj = tile.mAdjecentTiles[(Int32)dirCheck];

            Boolean entraceMade = false;

            if (null != adj)
            {
                // If we don't yet have a sequence start point, and this tile is an entrace,
                // it becomes the new sequence start.
                if (null == sequeceStart && tile.mType == Level.Tile.TileTypes.Empty && adj.mType == Level.Tile.TileTypes.Empty)
                {
                    sequeceStart = tile;
                }
                // The sequence has started already and we just hit a wall. Time to create an entrance in the
                // center of this sequence.
                else if (null != sequeceStart && (tile.mType != Level.Tile.TileTypes.Empty || adj.mType != Level.Tile.TileTypes.Empty))
                {
                    CreateEntrance(cluster, tile, ref sequeceStart, dirWalk, dirCheck, dirNeighbourCluster, true);

                    entraceMade = true;
                }
            }

            // Walk to the next Tile.
            adj = tile.mAdjecentTiles[(Int32)dirWalk];

            // Are we still in the Cluster/Level?
            if (null != adj && cluster.IsInBounds(adj))
            {
                // Recursivly visit the next Tile.
                return(WalkWall(cluster, adj, sequeceStart, dirWalk, dirCheck, dirNeighbourCluster));
            }
            else
            {
                // We have left either the map or the Cluster. Either way that is considered an end to
                // the current sequence, should one be in progress.
                if (null != sequeceStart)
                {
                    System.Diagnostics.Debug.Assert(!entraceMade, "Entrance made twice.");

                    CreateEntrance(cluster, tile, ref sequeceStart, dirWalk, dirCheck, dirNeighbourCluster, false);
                }

                return(tile);
            }
        }