public static List <Vector3> GetDynamicPathToDestination(List <PointWithCost> startPointList, Vector3 goal, float movementBudget, AbstractActor unit, bool shouldSprint, List <AbstractActor> lanceUnits, PathNodeGrid pathGrid, float targetRadius, bool actorAware = false)
        {
            HexGrid hexGrid = unit.Combat.HexGrid;

            if (shouldSprint && unit.CanSprint)
            {
                unit.Pathing.SetSprinting();
            }
            else
            {
                unit.Pathing.SetWalking();
            }

            HexPoint3 goalPoint = hexGrid.GetClosestHexPoint3OnGrid(goal);

            List <PointWithCost> pathLatticePoints = FindPath(startPointList, goalPoint, unit, shouldSprint ? MoveType.Sprinting : MoveType.Walking, targetRadius, actorAware);

            if ((pathLatticePoints == null) || (pathLatticePoints.Count == 0))
            {
                // can't find a path(!)
                return(null);
            }

            // Dig, if you will:
            // A - Current unit position
            // B - a point on the edge between short range and long range pathfinding
            // C - the goal point
            // FindPath returned a path from B to C
            // pathLatticePoints[0] is B
            // walking backward through parents gets us the path from A to B
            List <PathNode> pathNodes = new List <PathNode>();
            PathNode        walkNode  = pathLatticePoints[0].pathNode;

            while (walkNode != null)
            {
                pathNodes.Insert(0, walkNode);
                walkNode = walkNode.Parent;
            }

            List <Vector3> longRangePathWorldPoints = pathLatticePoints.ConvertAll(x => hexGrid.HexPoint3ToCartesianWorld(x.point));
            List <Vector3> pathWorldPoints          = pathNodes.ConvertAll(x => x.Position);

            pathWorldPoints.AddRange(longRangePathWorldPoints);

            // we have now spliced the A->B path together with the B->C path to have a A->C path

            if (longRangePathWorldPoints.Count == 1)
            {
                // if the B->C path is just one node, it's just B, so just return the A->C path,
                // which is just the A->B path, which we can do all in one go.

                return(pathWorldPoints);
            }

            // Debug Draw Path
            float scale = 1.0f;

            for (int pathIndex = 0; pathIndex < pathWorldPoints.Count - 1; ++pathIndex)
            {
                int     nextIndex = pathIndex + 1;
                Vector3 p0        = pathWorldPoints[pathIndex];
                Vector3 p1        = pathWorldPoints[nextIndex];

                scale = (p1 - p0).magnitude;

                for (int dx = -1; dx <= 1; ++dx)
                {
                    for (int dz = -1; dz <= 1; ++dz)
                    {
                        Vector3 offset = new Vector3(dx * scale * 0.1f, 0, dz * scale * 0.1f);
                        Debug.DrawLine(p0 + offset, p1 + offset, Color.red, 30.0f);
                    }
                }
            }

            drawCircle(unit.CurrentPosition, scale * 0.4f, Color.cyan, 30.0f);
            drawCircle(goal, scale * 0.4f, Color.magenta, 30.0f);
            drawCircle(pathWorldPoints[0], scale * 0.3f, Color.red, 30.0f);
            drawCircle(pathWorldPoints[0], scale * 0.35f, Color.white, 30.0f);
            drawCircle(pathWorldPoints[0], scale * 0.4f, Color.blue, 30.0f);

            // TODO/dlecompte push this up to filter our selection of path nodes, above.
            float spread = unit.BehaviorTree.GetBehaviorVariableValue(
                unit.Combat.TurnDirector.IsInterleaved ?
                BehaviorVariableName.Float_InterleavedLanceSpreadDistance :
                BehaviorVariableName.Float_NonInterleavedLanceSpreadDistance).FloatVal;

            drawCircle(unit.CurrentPosition, spread, Color.green, 30.0f);

            Debug.Assert(pathWorldPoints.Count >= 2);             // already tested this, above.
            float accumDistance = 0.0f;

            Vector3 clipPoint = goal;

            // Now we walk along the pathWorldPoints, snapping them to grid points.
            // We want to take the point furthest along the path that doesn't alias to an earlier point.
            // Also, nodes must be "safe" from artillery
            // Also, we want to make sure that it's the furthest point within our movement budget and within our lance spread.
            // MUST BE : within movement budget, not an alias to an earlier point
            // IF any points exist inside lance spread, pick last point inside lance spread, else last point.

            List <Vector3> dedupedSnappedPointsList = new List <Vector3>();
            List <Vector3> snappedPointsInOrder     = new List <Vector3>();
            List <Vector3> nextPointsInOrder        = new List <Vector3>();
            List <bool>    pointsInSpreadRangeList  = new List <bool>();
            List <bool>    isNewGroundList          = new List <bool>();

            float ROUNDING_RADIUS = 1.0f;

            bool wasEverInside = false;

            for (int pointIndex = 0; (pointIndex < pathWorldPoints.Count) && (accumDistance <= movementBudget); ++pointIndex)
            {
                Vector3 thisPoint = pathWorldPoints[pointIndex];
                Vector3 nextPoint = goal;

                if (pointIndex + 1 < pathWorldPoints.Count)
                {
                    nextPoint = pathWorldPoints[pointIndex + 1];
                }

                Vector3 thisSnappedPoint = unit.Combat.HexGrid.GetClosestPointOnGrid(thisPoint);
                if (!IsLocationSafe(unit.Combat, thisSnappedPoint))
                {
                    continue;
                }
                snappedPointsInOrder.Add(thisSnappedPoint);
                nextPointsInOrder.Add(nextPoint);

                bool pointIsInsideSpread = AIUtil.IsPositionWithinLanceSpread(unit, lanceUnits, thisSnappedPoint);
                wasEverInside |= pointIsInsideSpread;
                pointsInSpreadRangeList.Add(pointIsInsideSpread);

                bool alreadyVisited = isPointInList(thisSnappedPoint, dedupedSnappedPointsList, ROUNDING_RADIUS);

                isNewGroundList.Add(!alreadyVisited);

                if (!alreadyVisited)
                {
                    dedupedSnappedPointsList.Add(thisSnappedPoint);
                }

                if (pointIndex + 1 < pathWorldPoints.Count)
                {
                    accumDistance += (nextPoint - thisPoint).magnitude;
                }
            }

            if (wasEverInside)
            {
                // find the last point of our list that is "new ground" and inside
                for (int i = snappedPointsInOrder.Count - 1; i >= 0; --i)
                {
                    if (isNewGroundList[i] && pointsInSpreadRangeList[i])
                    {
                        clipPoint = snappedPointsInOrder[i];
                        break;
                    }
                }
            }

            if ((!wasEverInside) || ((clipPoint - unit.CurrentPosition).magnitude < 1.0f))
            {
                // find the last point of our list that is "new ground"
                for (int i = snappedPointsInOrder.Count - 1; i >= 0; --i)
                {
                    if (isNewGroundList[i])
                    {
                        clipPoint = snappedPointsInOrder[i];
                        break;
                    }
                }
            }

            for (int i = snappedPointsInOrder.Count - 1; i >= 0; --i)
            {
                drawCircle(snappedPointsInOrder[i], scale * 0.2f, new Color(0.5f, 0.5f, 0.0f), 30.0f);
                if (pointsInSpreadRangeList[i])
                {
                    drawCircle(snappedPointsInOrder[i], scale * 0.25f, new Color(0.0f, 1.0f, 0.0f), 30.0f);
                }
            }

            //Vector3 resultPos = clipPoint;
            drawCircle(clipPoint, scale * 0.4f, new Color(1.0f, 0.5f, 0.0f), 30.0f);
            return(snappedPointsInOrder);
        }
Beispiel #2
0
        public Vector3 GetDestination(List <PointWithDistance> startPointList, Vector3 goal, float movementBudget, float maxSlope, AbstractActor unit, bool shouldSprint, List <AbstractActor> lanceUnits, PathNodeGrid pathGrid, out Vector3 lookAtPoint)
        {
            if (shouldSprint && unit.CanSprint)
            {
                unit.Pathing.SetSprinting();
            }
            else
            {
                unit.Pathing.SetWalking();
            }

            InclineIndexPoint goalPoint = WorldPointToInclineIndices(goal);

            List <PointWithDistance> pathLatticePoints = FindPath(startPointList, goalPoint, maxSlope, maxSlope);

            if ((pathLatticePoints == null) || (pathLatticePoints.Count == 0))
            {
                // can't find a path(!)

                // set the lookAtPoint (out) variable to a point further out in the direction of start->goal
                lookAtPoint = goal * 2.0f - unit.CurrentPosition;
                return(goal);
            }


            List <PathNode> pathNodes = new List <PathNode>();
            PathNode        walkNode  = pathLatticePoints[0].pathNode;

            while (walkNode != null)
            {
                pathNodes.Insert(0, walkNode);
                walkNode = walkNode.Parent;
            }

            List <Vector3> longRangePathWorldPoints = pathLatticePoints.ConvertAll(x => InclineIndicesToWorldPoint(x.point));
            List <Vector3> pathWorldPoints          = pathNodes.ConvertAll(x => x.Position);

            pathWorldPoints.AddRange(longRangePathWorldPoints);

            Vector3 destination;

            if (longRangePathWorldPoints.Count == 1)
            {
                destination = longRangePathWorldPoints[0];
                if ((goal - destination).magnitude < 1.0f)
                {
                    lookAtPoint = destination * 2.0f - unit.CurrentPosition;
                }
                else
                {
                    lookAtPoint = goal;
                }

                return(destination);
            }

            // Debug Draw Path
            float scale = 1.0f;

            for (int pathIndex = 0; pathIndex < pathWorldPoints.Count - 1; ++pathIndex)
            {
                int     nextIndex = pathIndex + 1;
                Vector3 p0        = pathWorldPoints[pathIndex];
                Vector3 p1        = pathWorldPoints[nextIndex];

                scale = (p1 - p0).magnitude;

                for (int dx = -1; dx <= 1; ++dx)
                {
                    for (int dz = -1; dz <= 1; ++dz)
                    {
                        Vector3 offset = new Vector3(dx * scale * 0.1f, 0, dz * scale * 0.1f);
                        Debug.DrawLine(p0 + offset, p1 + offset, Color.red, 30.0f);
                    }
                }
            }

            drawCircle(unit.CurrentPosition, scale * 0.4f, Color.cyan, 30.0f);
            drawCircle(goal, scale * 0.4f, Color.magenta, 30.0f);
            drawCircle(pathWorldPoints[0], scale * 0.3f, Color.red, 30.0f);
            drawCircle(pathWorldPoints[0], scale * 0.35f, Color.white, 30.0f);
            drawCircle(pathWorldPoints[0], scale * 0.4f, Color.blue, 30.0f);

            // TODO/dlecompte push this up to filter our selection of path nodes, above.
            float spread = unit.BehaviorTree.GetBehaviorVariableValue(
                unit.Combat.TurnDirector.IsInterleaved ?
                BehaviorVariableName.Float_InterleavedLanceSpreadDistance :
                BehaviorVariableName.Float_NonInterleavedLanceSpreadDistance).FloatVal;

            drawCircle(unit.CurrentPosition, spread, Color.green, 30.0f);

            Debug.Assert(pathWorldPoints.Count >= 2);             // already tested this, above.
            float accumDistance = 0.0f;

            // if we can get to the goal, look at a point further away in the direction of start -> goal
            lookAtPoint = 2 * goal - unit.CurrentPosition;
            Vector3 clipPoint = goal;

            // Now we walk along the pathWorldPoints, snapping them to grid points.
            // We want to take the point furthest along the path that doesn't alias to an earlier point.
            // Also, we want to make sure that it's the furthest point within our movement budget and within our lance spread.
            // MUST BE : within movement budget, not an alias to an earlier point
            // IF any points exist inside lance spread, pick last point inside lance spread, else last point.

            List <Vector3> dedupedSnappedPointsList = new List <Vector3>();
            List <Vector3> snappedPointsInOrder     = new List <Vector3>();
            List <Vector3> nextPointsInOrder        = new List <Vector3>();
            List <bool>    pointsInSpreadRangeList  = new List <bool>();
            List <bool>    isNewGroundList          = new List <bool>();

            float ROUNDING_RADIUS = 1.0f;

            bool wasEverInside = false;

            for (int pointIndex = 0; (pointIndex < pathWorldPoints.Count) && (accumDistance <= movementBudget); ++pointIndex)
            {
                Vector3 thisPoint = pathWorldPoints[pointIndex];
                Vector3 nextPoint = goal;
                if (pointIndex + 1 < pathWorldPoints.Count)
                {
                    nextPoint = pathWorldPoints[pointIndex + 1];
                }

                Vector3 thisSnappedPoint = unit.Combat.HexGrid.GetClosestPointOnGrid(thisPoint);
                snappedPointsInOrder.Add(thisSnappedPoint);
                nextPointsInOrder.Add(nextPoint);

                bool pointIsInsideSpread = AIUtil.IsPositionWithinLanceSpread(unit, lanceUnits, thisSnappedPoint);
                wasEverInside |= pointIsInsideSpread;
                pointsInSpreadRangeList.Add(pointIsInsideSpread);

                bool alreadyVisited = isPointInList(thisSnappedPoint, dedupedSnappedPointsList, ROUNDING_RADIUS);

                isNewGroundList.Add(!alreadyVisited);

                if (!alreadyVisited)
                {
                    dedupedSnappedPointsList.Add(thisSnappedPoint);
                }

                if (pointIndex + 1 < pathWorldPoints.Count)
                {
                    accumDistance += (nextPoint - thisPoint).magnitude;
                }
            }

            if (wasEverInside)
            {
                // find the last point of our list that is "new ground" and inside
                for (int i = snappedPointsInOrder.Count - 1; i >= 0; --i)
                {
                    if (isNewGroundList[i] && pointsInSpreadRangeList[i])
                    {
                        clipPoint   = snappedPointsInOrder[i];
                        lookAtPoint = nextPointsInOrder[i];
                        break;
                    }
                }
            }

            if ((!wasEverInside) || ((clipPoint - unit.CurrentPosition).magnitude < 1.0f))
            {
                // find the last point of our list that is "new ground"
                for (int i = snappedPointsInOrder.Count - 1; i >= 0; --i)
                {
                    if (isNewGroundList[i])
                    {
                        clipPoint   = snappedPointsInOrder[i];
                        lookAtPoint = nextPointsInOrder[i];
                        break;
                    }
                }
            }

            for (int i = snappedPointsInOrder.Count - 1; i >= 0; --i)
            {
                drawCircle(snappedPointsInOrder[i], scale * 0.2f, new Color(0.5f, 0.5f, 0.0f), 30.0f);
                if (pointsInSpreadRangeList[i])
                {
                    drawCircle(snappedPointsInOrder[i], scale * 0.25f, new Color(0.0f, 1.0f, 0.0f), 30.0f);
                }
            }

            //float lookAngle = PathingUtil.GetAngle(lookAtPoint - clipPoint);
            //Vector3 resultPos = clipPoint;
            drawCircle(clipPoint, scale * 0.4f, new Color(1.0f, 0.5f, 0.0f), 30.0f);
            return(clipPoint);
        }