Ejemplo n.º 1
0
 /// <summary>Construct a new FovCone instance.</summary>
 internal FovCone(int range, IntVector2D top, IntVector2D bottom, RiseRun riseRun) : this()
 {
     this.Range        = range;
     this.RiseRun      = riseRun;
     this.VectorTop    = top;
     this.VectorBottom = bottom;
 }
 static IntVector2D LogAndEnqueue(Action <FovCone> enqueue, int range, IntVector2D top,
                                  IntVector2D bottom, RiseRun riseRun, int code
                                  )
 {
     if (top.GT(bottom))
     {
         var cone = new FovCone(range + 1, top, bottom, riseRun);
         FieldOfViewTrace(false, "  EQ: ({0}) code: {1}", cone, code);
         enqueue(cone);
         return(bottom);
     }
     else
     {
         return(top);
     }
 }
        /// <summary>Processes the supplied FovCone and returns the next FovCone to process.</summary>
        /// <param name="heightObserver"></param>
        /// <param name="isOnboard"></param>
        /// <param name="heightTarget"></param>
        /// <param name="heightTerrain"></param>
        /// <param name="setFieldOfView"></param>
        /// <param name="queue"></param>
        /// <param name="cone"></param>
        /// <returns></returns>
        /// <remarks>
        /// This method:
        /// (1) marks points inside the cone-arc that are within the radius of the field
        ///     of view; and
        /// (2) computes which portions of the following column are in the field of view,
        ///     queueing them for later processing.
        ///
        /// This algorithm is "center-to-center"; a more sophisticated algorithm
        /// would say that a cell is visible if neighbour is *any* straight line segment that
        /// passes through *any* portion of the origin cell and any portion of the target
        /// cell, passing through only transparent cells along the way. This is the
        /// "Permissive Field Of View" algorithm, and it is much harder to implement.
        ///
        /// Search for transitions from opaque to transparent or transparent to opaque and
        /// use those to determine what portions of the *next* column are visible from the
        /// origin.
        /// </remarks>
        private static FovCone ComputeFoVForRange(
            int heightObserver,
            Func <HexCoords, bool> isOnboard,
            Func <HexCoords, int> heightTarget,
            Func <HexCoords, Hexside, int> heightTerrain,
            Action <HexCoords> setFieldOfView,
            FovConeQueue queue,
            FovCone cone
            )
        {
            Action <FovCone> enqueue = queue.Enqueue;

            var range        = cone.Range;
            var topVector    = cone.VectorTop;
            var topRiseRun   = cone.RiseRun;
            var bottomVector = cone.VectorBottom;

            // track the overlap-cone between adjacent hexes as we move down.
            var overlapVector = cone.VectorTop;
            var hexX          = XFromVector(range, topVector);

            FieldOfViewTrace(false, "DQ:   ({0}) from {1}", cone, hexX);

            do
            {
                while (overlapVector.GT(bottomVector))
                {
                    var coordsCurrent   = HexCoords.NewCanonCoords(hexX, range);
                    var hexVectorBottom = VectorHexBottom(coordsCurrent);
                    if (isOnboard(coordsCurrent))
                    {
                        #region Set current hex parameters
                        var hexVectorTop = VectorHexTop(coordsCurrent);
                        var hexElevation = heightTarget(coordsCurrent);
                        var hexHeight    = heightTerrain(coordsCurrent, Hexside.North);
                        var hexRiseRun   = new RiseRun(hexHeight - heightObserver, range);
                        #endregion

                        #region Check visibility of current hex
                        var riseRun = new RiseRun(hexElevation - heightObserver, coordsCurrent.RangeFromOrigin);
                        if (riseRun >= cone.RiseRun &&
                            bottomVector.LE(coordsCurrent.Canon) && coordsCurrent.Canon.LE(topVector)
                            )
                        {
                            setFieldOfView(coordsCurrent);
                            FieldOfViewTrace(false, "    Set visible: {0} / {1}; {2} >= {3}",
                                             _mapCoordsDodecant(coordsCurrent), coordsCurrent.ToString(), riseRun, cone.RiseRun);
                        }
                        #endregion

                        #region Check hex transition
                        if (hexRiseRun > topRiseRun)
                        {
                            topVector  = LogAndEnqueue(enqueue, range, topVector, hexVectorTop, topRiseRun, 0);
                            topRiseRun = hexRiseRun;
                        }
                        else if (hexRiseRun > cone.RiseRun)
                        {
                            topVector  = LogAndEnqueue(enqueue, range, topVector, overlapVector, topRiseRun, 1);
                            topRiseRun = hexRiseRun;
                        }
                        else if (hexRiseRun < cone.RiseRun)
                        {
                            topVector  = LogAndEnqueue(enqueue, range, topVector, overlapVector, topRiseRun, 2);
                            topRiseRun = cone.RiseRun;
                        }
                        #endregion
                    }

                    overlapVector = VectorMax(hexVectorBottom, bottomVector);
                    if (hexVectorBottom.GT(bottomVector))
                    {
                        --hexX;
                    }
                }

                #region Dequeue next cone
                if (queue.Count == 0)
                {
                    topVector = LogAndEnqueue(enqueue, range, topVector, bottomVector, topRiseRun, 3);
                    cone      = queue.Dequeue();
                    break;
                }
                else
                {
                    cone = queue.Dequeue();
                    if (cone.Range != range)
                    {
                        topVector = LogAndEnqueue(enqueue, range, topVector, bottomVector, topRiseRun, 4);
                        break;
                    }
                    FieldOfViewTrace(false, "DQ:   ({0}) from {1}", cone, hexX);
                }
                #endregion

                #region Check cone transition
                if (cone.RiseRun > topRiseRun)
                {
                    topVector  = LogAndEnqueue(enqueue, range, topVector, cone.VectorTop, topRiseRun, 5);
                    topRiseRun = cone.RiseRun;
                }
                else if (cone.RiseRun < topRiseRun)
                {
                    topVector  = LogAndEnqueue(enqueue, range, topVector, overlapVector, topRiseRun, 6);
                    topRiseRun = cone.RiseRun;
                }
                #endregion

                overlapVector = topVector;
                bottomVector  = cone.VectorBottom;
            } while(true);

            // Pick-up any cone portion at bottom of range still unprocessed
            if (topVector.GT(bottomVector))
            {
                LogAndEnqueue(enqueue, range, topVector, bottomVector, cone.RiseRun, 7);
            }

            return(cone);
        }
Ejemplo n.º 4
0
 /// <inheritdoc/>
 public override int GetHashCode()
 {
     return(VectorTop.GetHashCode() ^ Range.GetHashCode()
            ^ RiseRun.GetHashCode() ^ VectorBottom.GetHashCode());
 }
 /// <inheritdoc/>
 public override int GetHashCode()
 => VectorTop.GetHashCode() ^ Range.GetHashCode()
 ^ RiseRun.GetHashCode() ^ VectorBottom.GetHashCode();