/// <summary>
        /// Finds a destination this turn along a long range path towards the goal.
        /// </summary>
        /// <param name="goal"></param>
        /// <param name="movementThisTurn"></param>
        /// <param name="unit"></param>
        /// <param name="shouldSprint"></param>
        /// <param name="lookAtPoint"></param>
        /// <param name="pathIsSafe">(out) flag whether the path ends in a place out of (e.g. artillery) danger</param>
        /// <returns></returns>

        static public List <Vector3> GetPathToDestination(Vector3 goal, float movementThisTurn, AbstractActor unit, bool shouldSprint, float destinationRadius)
        {
            List <AbstractActor> lanceUnits = AIUtil.GetLanceUnits(unit.Combat, unit.LanceId);

            // can't get all the way to the destination.
            if ((unit.Combat.EncounterLayerData.inclineMeshData != null) &&
                (unit.BehaviorTree.GetBehaviorVariableValue(BehaviorVariableName.Bool_UseDynamicLongRangePathfinding).BoolVal == false))
            {
                float maxSteepnessRatio = Mathf.Tan(Mathf.Deg2Rad * AIUtil.GetMaxSteepnessForAllLance(unit));
                // TODO - make this work, if we want to maintain it
                Vector3 lookAtPoint;
                Vector3 dest = unit.Combat.EncounterLayerData.inclineMeshData.GetDestination(
                    goal,
                    movementThisTurn,
                    maxSteepnessRatio,
                    unit,
                    shouldSprint,
                    lanceUnits,
                    unit.Pathing.CurrentGrid,
                    out lookAtPoint);

                List <Vector3> path = new List <Vector3>();
                path.Add(dest);
                return(path);
            }
            else
            {
                return(GetDynamicPathToDestination(
                           goal,
                           movementThisTurn,
                           unit,
                           shouldSprint,
                           lanceUnits,
                           unit.Pathing.CurrentGrid,
                           destinationRadius));
            }
        }
    Vector3 getReachablePointOnRoute(Vector3 startPosition, RouteGameLogic patrolRoute, PatrolRouteWaypoints waypointIterator, List <PathNode> validDestinations, out bool isComplete, out int nextWaypointIndex, out bool nextPointGoesForward, out Vector3 successorPoint)
    {
        nextWaypointIndex    = -1;
        nextPointGoesForward = false;

        float destinationRadius = unit.BehaviorTree.GetBehaviorVariableValue(BehaviorVariableName.Float_RouteWaypointRadius).FloatVal;
        //Vector3 foundDest;
        Vector3 lastReachablePoint;
        Vector3 firstUnreachableWaypoint;

        List <AbstractActor> lanceUnits = AIUtil.GetLanceUnits(unit.Combat, unit.LanceId);

        bool completedRoute;

        findLastReachablePatrolWaypoint(startPosition, patrolRoute, validDestinations, waypointIterator, destinationRadius, out lastReachablePoint, out firstUnreachableWaypoint, out nextWaypointIndex, out completedRoute);
        successorPoint = startPosition;

        if (completedRoute)
        {
            isComplete           = true;
            nextWaypointIndex    = patrolRoute.routePointList.Length - 1;
            nextPointGoesForward = true;
            // look away from the foundDest in the direction that we moved
            // TODO(dlecompte) try to look away from the previous waypoint? Or maybe the pathnode's previous parent?
            successorPoint = lastReachablePoint + (lastReachablePoint - startPosition);
            // TODO(dlecompte) this ignores maxDist, probably could prune smarter.
            return(lastReachablePoint);
        }

        Vector3 nextWaypoint = patrolRoute.routePointList[nextWaypointIndex].Position;

        nextPointGoesForward = waypointIterator.goingForward;
        successorPoint       = nextWaypoint;
        isComplete           = false;

        return(lastReachablePoint);
    }
Beispiel #3
0
        protected override BehaviorTreeResults Tick()
        {
            if (unit.HasMovedThisRound)
            {
                return(new BehaviorTreeResults(BehaviorNodeState.Failure));
            }

            BehaviorVariableValue targetLanceGuidValue = this.tree.GetCustomBehaviorVariableValue(FOLLOW_LANCE_TARGET_GUID_KEY);

            if (targetLanceGuidValue == null)
            {
                return(new BehaviorTreeResults(BehaviorNodeState.Failure));
            }

            string targetLanceGuid = targetLanceGuidValue.StringVal;
            Lance  targetLance     = DestinationUtil.FindLanceByGUID(this.tree, targetLanceGuid);

            if (targetLance == null)
            {
                return(new BehaviorTreeResults(BehaviorNodeState.Failure));
            }
            List <AbstractActor> lanceMembers = AIUtil.GetLanceUnits(this.unit.Combat, this.unit.LanceId);

            float travelDistance = Mathf.Max(this.unit.MaxSprintDistance, this.unit.MaxWalkDistance);

            if (this.waitForLance)
            {
                for (int i = 0; i < lanceMembers.Count; i++)
                {
                    AbstractActor abstractActor = lanceMembers[i] as AbstractActor;

                    if (abstractActor != null)
                    {
                        float lanceMemberTravelDistance = Mathf.Max(abstractActor.MaxWalkDistance, abstractActor.MaxSprintDistance);
                        travelDistance = Mathf.Min(travelDistance, lanceMemberTravelDistance);
                    }
                }
            }

            AbstractActor targetActor   = null;
            float         targetTonnage = 0;

            for (int i = 0; i < targetLance.unitGuids.Count; i++)
            {
                ITaggedItem itemByGUID = this.unit.Combat.ItemRegistry.GetItemByGUID(targetLance.unitGuids[i]);

                if (itemByGUID != null)
                {
                    AbstractActor abstractActor = itemByGUID as AbstractActor;

                    if (abstractActor != null && !abstractActor.IsDead)
                    {
                        if (abstractActor is Mech)
                        {
                            Mech mech = (Mech)abstractActor;
                            if (mech.tonnage > targetTonnage)
                            {
                                targetActor   = mech;
                                targetTonnage = mech.tonnage;
                            }
                        }
                        else if (abstractActor is Vehicle)
                        {
                            Vehicle vehicle = (Vehicle)abstractActor;
                            if (vehicle.tonnage > targetTonnage)
                            {
                                targetActor   = vehicle;
                                targetTonnage = vehicle.tonnage;
                            }
                        }
                    }
                }
            }

            if (targetActor == null)
            {
                Main.Logger.LogError("[MoveToFollowLanceNode] Target Actor is null");
                return(new BehaviorTreeResults(BehaviorNodeState.Failure));
            }

            Main.LogDebug($"[MoveToFollowLanceNode] Target to follow is '{targetActor.DisplayName} {targetActor.VariantName}'");

            bool shouldSprint = this.tree.GetCustomBehaviorVariableValue(FOLLOW_LANCE_SHOULD_SPRINT_KEY).BoolVal;

            shouldSprint = (!this.unit.HasAnyContactWithEnemy);
            shouldSprint = (this.unit.CurrentPosition - targetActor.CurrentPosition).magnitude > 200f; // sprint if the unit is over 200 metres away

            AbstractActor closestDetectedEnemy = AiUtils.GetClosestDetectedEnemy(this.unit, targetLance);
            Vector3       lookDirection        = (closestDetectedEnemy == null) ? targetActor.CurrentPosition : closestDetectedEnemy.CurrentPosition;

            MoveType moveType = (shouldSprint) ? MoveType.Sprinting : MoveType.Walking;

            this.unit.Pathing.UpdateAIPath(targetActor.CurrentPosition, lookDirection, moveType);

            Vector3 vectorToTarget   = this.unit.Pathing.ResultDestination - this.unit.CurrentPosition;
            float   distanceToTarget = vectorToTarget.magnitude;

            if (distanceToTarget > travelDistance)
            {
                // If the target is out of range, head in the direction of that unit to the maximum possible travel distance for this turn
                vectorToTarget = vectorToTarget.normalized * travelDistance;
            }

            // Ensure the units aren't crowded
            Vector3 targetDestination = RoutingUtil.Decrowd(this.unit.CurrentPosition + vectorToTarget, this.unit);

            targetDestination = RegionUtil.MaybeClipMovementDestinationToStayInsideRegion(this.unit, targetDestination);

            float followLanceZoneRadius = this.unit.BehaviorTree.GetCustomBehaviorVariableValue(FOLLOW_LANCE_ZONE_RADIUS_KEY).FloatVal;

            if (RoutingUtils.IsUnitInsideRadiusOfPoint(this.unit, targetActor.CurrentPosition, followLanceZoneRadius))
            {
                return(new BehaviorTreeResults(BehaviorNodeState.Failure));
            }

            this.unit.Pathing.UpdateAIPath(targetDestination, lookDirection, (shouldSprint) ? MoveType.Sprinting : MoveType.Walking);
            targetDestination = this.unit.Pathing.ResultDestination;
            float        maxCost             = this.unit.Pathing.MaxCost;
            PathNodeGrid currentGrid         = this.unit.Pathing.CurrentGrid;
            Vector3      targetActorPosition = targetActor.CurrentPosition;

            if ((currentGrid.GetValidPathNodeAt(targetDestination, maxCost) == null || (targetDestination - targetActor.CurrentPosition).magnitude > 1f) && this.unit.Combat.EncounterLayerData.inclineMeshData != null)
            {
                float maxSlope = Mathf.Tan(0.0174532924f * AIUtil.GetMaxSteepnessForAllLance(this.unit));
                List <AbstractActor> lanceUnits = AIUtil.GetLanceUnits(this.unit.Combat, this.unit.LanceId);
                targetDestination = this.unit.Combat.EncounterLayerData.inclineMeshData.GetDestination(this.unit.CurrentPosition, targetDestination, maxCost, maxSlope, this.unit, shouldSprint, lanceUnits, this.unit.Pathing.CurrentGrid, out targetActorPosition);
            }

            Vector3 currentPosition = this.unit.CurrentPosition;

            AIUtil.LogAI(string.Format("issuing order from [{0} {1} {2}] to [{3} {4} {5}] looking at [{6} {7} {8}]", new object[] {
                currentPosition.x,
                currentPosition.y,
                currentPosition.z,
                targetDestination.x,
                targetDestination.y,
                targetDestination.z,
                targetActorPosition.x,
                targetActorPosition.y,
                targetActorPosition.z
            }), "AI.DecisionMaking");

            return(new BehaviorTreeResults(BehaviorNodeState.Success)
            {
                orderInfo = new MovementOrderInfo(targetDestination, targetActorPosition)
                {
                    IsSprinting = shouldSprint
                },
                debugOrderString = string.Format("{0} moving toward destination: {1} dest: {2}", this.name, targetDestination, targetActor.CurrentPosition)
            });
        }
Beispiel #4
0
    override protected BehaviorTreeResults Tick()
    {
        BattleTech.Designed.EncounterBoundaryChunkGameLogic boundaryChunk = unit.Combat.EncounterLayerData.encounterBoundaryChunk;

        if (boundaryChunk.IsInEncounterBounds(unit.CurrentPosition))
        {
            return(new BehaviorTreeResults(BehaviorNodeState.Success));
        }

        // find closest center
        float   bestDist    = float.MaxValue;
        Vector3 destination = Vector3.zero;

        if (boundaryChunk.encounterBoundaryRectList.Count == 0)
        {
            return(new BehaviorTreeResults(BehaviorNodeState.Failure));
        }

        for (int i = 0; i < boundaryChunk.encounterBoundaryRectList.Count; ++i)
        {
            RectHolder rh   = boundaryChunk.encounterBoundaryRectList[i];
            Vector3    c    = rh.rect.center;
            float      dist = (unit.CurrentPosition - c).magnitude;

            if (dist < bestDist)
            {
                bestDist    = dist;
                destination = c;
            }
        }

        if ((destination - unit.CurrentPosition).magnitude < 1)
        {
            // already close (should probably have been caught, above)
            return(new BehaviorTreeResults(BehaviorNodeState.Success));
        }

        unit.Pathing.UpdateAIPath(destination, destination, MoveType.Sprinting);
        Vector3 destinationThisTurn = unit.Pathing.ResultDestination;

        float        movementBudget = unit.Pathing.MaxCost;
        PathNodeGrid grid           = unit.Pathing.CurrentGrid;
        Vector3      successorPoint = destination;

        if ((grid.GetValidPathNodeAt(destinationThisTurn, movementBudget) == null) ||
            ((destinationThisTurn - destination).magnitude > 1.0f))
        {
            // can't get all the way to the destination.
            if (unit.Combat.EncounterLayerData.inclineMeshData != null)
            {
                List <AbstractActor> lanceUnits = AIUtil.GetLanceUnits(unit.Combat, unit.LanceId);
                List <Vector3>       path       = DynamicLongRangePathfinder.GetDynamicPathToDestination(destinationThisTurn, movementBudget, unit, true, lanceUnits, unit.Pathing.CurrentGrid, 100.0f);

                if ((path != null) && (path.Count > 0))
                {
                    destinationThisTurn = path[path.Count - 1];
                }
            }
        }

        Vector3 cur = unit.CurrentPosition;

        AIUtil.LogAI(string.Format("issuing order from [{0} {1} {2}] to [{3} {4} {5}] looking at [{6} {7} {8}]",
                                   cur.x, cur.y, cur.z,
                                   destinationThisTurn.x, destinationThisTurn.y, destinationThisTurn.z,
                                   successorPoint.x, successorPoint.y, successorPoint.z
                                   ));

        BehaviorTreeResults results      = new BehaviorTreeResults(BehaviorNodeState.Success);
        MovementOrderInfo   mvtOrderInfo = new MovementOrderInfo(destinationThisTurn, successorPoint);

        mvtOrderInfo.IsSprinting = true;
        results.orderInfo        = mvtOrderInfo;
        results.debugOrderString = string.Format("{0}: dest:{1} sprint:{2}", this.name, destination, mvtOrderInfo.IsSprinting);
        return(results);
    }
Beispiel #5
0
    override protected BehaviorTreeResults Tick()
    {
        string regionGUID = RegionUtil.GetStayInsideRegionGUID(unit);

        if (regionGUID == null)
        {
            return(new BehaviorTreeResults(BehaviorNodeState.Failure));
        }

        if (unit.IsInRegion(regionGUID))
        {
            return(new BehaviorTreeResults(BehaviorNodeState.Success));
        }

        ITaggedItem item = unit.Combat.ItemRegistry.GetItemByGUID(regionGUID);

        if (item == null)
        {
            Debug.Log("no item with GUID: " + regionGUID);
            return(new BehaviorTreeResults(BehaviorNodeState.Failure));
        }
        RegionGameLogic region = item as RegionGameLogic;

        if (region == null)
        {
            Debug.Log("item is not region: " + regionGUID);
            return(new BehaviorTreeResults(BehaviorNodeState.Failure));
        }

        // TODO: find a point inside the region, for now using the average of all vertices.
        int numPoints = region.regionPointList.Length;

        Vector3 destination = new Vector3();

        for (int pointIndex = 0; pointIndex < numPoints; ++pointIndex)
        {
            destination += region.regionPointList[pointIndex].Position;
        }
        if (numPoints == 0)
        {
            Debug.Log("no points in region: " + regionGUID);
            return(new BehaviorTreeResults(BehaviorNodeState.Failure));
        }

        destination = RoutingUtil.Decrowd(destination * 1.0f / numPoints, unit);
        destination = RegionUtil.MaybeClipMovementDestinationToStayInsideRegion(unit, destination);

        var cell = unit.Combat.MapMetaData.GetCellAt(destination);

        destination.y = cell.cachedHeight;

        if ((destination - unit.CurrentPosition).magnitude < 1)
        {
            // already close (should probably have been caught, above)
            return(new BehaviorTreeResults(BehaviorNodeState.Success));
        }

        bool shouldSprint = unit.CanSprint;

        //float sprintRange = Mathf.Max(unit.MaxSprintDistance, unit.MaxWalkDistance);
        float moveRange = unit.MaxWalkDistance;

        if ((destination - unit.CurrentPosition).magnitude < moveRange)
        {
            shouldSprint = false;
        }

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

        unit.Pathing.UpdateAIPath(destination, destination, shouldSprint ? MoveType.Sprinting : MoveType.Walking);
        Vector3 destinationThisTurn = unit.Pathing.ResultDestination;

        float        movementBudget = unit.Pathing.MaxCost;
        PathNodeGrid grid           = unit.Pathing.CurrentGrid;
        Vector3      successorPoint = destination;

        var longRangeToShorRangeDistanceThreshold = unit.BehaviorTree.GetBehaviorVariableValue(BehaviorVariableName.Float_LongRangeToShortRangeDistanceThreshold).FloatVal;

        if (grid.GetValidPathNodeAt(destinationThisTurn, movementBudget) == null || (destinationThisTurn - destination).magnitude > longRangeToShorRangeDistanceThreshold)
        {
            List <AbstractActor> lanceUnits = AIUtil.GetLanceUnits(unit.Combat, unit.LanceId);
            List <Vector3>       path       = DynamicLongRangePathfinder.GetDynamicPathToDestination(destination, movementBudget, unit, shouldSprint, lanceUnits, grid, 0);
            if (path == null || path.Count == 0)
            {
                return(new BehaviorTreeResults(BehaviorNodeState.Failure));
            }

            destinationThisTurn = path[path.Count - 1];

            Vector2 flatDestination = new Vector2(destination.x, destination.z);

            float   currentClosestPointInRegionDistance = float.MaxValue;
            Vector3?closestPoint = null;

            for (int i = 0; i < path.Count; ++i)
            {
                Vector3 pointOnPath = path[i];

                if (RegionUtil.PointInRegion(unit.Combat, pointOnPath, regionGUID))
                {
                    var distance = (flatDestination - new Vector2(pointOnPath.x, pointOnPath.z)).sqrMagnitude;

                    if (distance < currentClosestPointInRegionDistance)
                    {
                        currentClosestPointInRegionDistance = distance;
                        closestPoint = pointOnPath;
                    }
                }
            }

            if (closestPoint != null)
            {
                destinationThisTurn = closestPoint.Value;
            }
        }

        Vector3 cur = unit.CurrentPosition;

        AIUtil.LogAI(string.Format("issuing order from [{0} {1} {2}] to [{3} {4} {5}] looking at [{6} {7} {8}]",
                                   cur.x, cur.y, cur.z,
                                   destinationThisTurn.x, destinationThisTurn.y, destinationThisTurn.z,
                                   successorPoint.x, successorPoint.y, successorPoint.z
                                   ));

        BehaviorTreeResults results      = new BehaviorTreeResults(BehaviorNodeState.Success);
        MovementOrderInfo   mvtOrderInfo = new MovementOrderInfo(destinationThisTurn, successorPoint);

        mvtOrderInfo.IsSprinting = shouldSprint;
        results.orderInfo        = mvtOrderInfo;
        results.debugOrderString = string.Format("{0}: dest:{1} sprint:{2}", this.name, destination, mvtOrderInfo.IsSprinting);
        return(results);
    }
Beispiel #6
0
        protected override BehaviorTreeResults Tick()
        {
            if (unit.HasMovedThisRound)
            {
                return(new BehaviorTreeResults(BehaviorNodeState.Failure));
            }

            BehaviorVariableValue targetLanceGuidValue = this.tree.GetCustomBehaviorVariableValue(FOLLOW_LANCE_TARGET_GUID_KEY);

            if (targetLanceGuidValue == null)
            {
                return(new BehaviorTreeResults(BehaviorNodeState.Failure));
            }

            string targetLanceGuid = targetLanceGuidValue.StringVal;
            Lance  targetLance     = DestinationUtil.FindLanceByGUID(this.tree, targetLanceGuid);

            if (targetLance == null)
            {
                return(new BehaviorTreeResults(BehaviorNodeState.Failure));
            }

            AbstractActor closestEnemy = null;

            if (Main.Settings.AiSettings.FollowAiSettings.StopWhen == "OnEnemyVisible")
            {
                Main.LogDebug($"[MoveToFollowLanceNode] Looking for closest visible enemy.");
                closestEnemy = AiUtils.GetClosestVisibleEnemy(this.unit, targetLance);
            }
            else // OnEnemyDetected
            {
                Main.LogDebug($"[MoveToFollowLanceNode] Looking for closest detected enemy.");
                closestEnemy = AiUtils.GetClosestDetectedEnemy(this.unit, targetLance);
            }

            if (closestEnemy != null)
            {
                if (Main.Settings.AiSettings.FollowAiSettings.StopWhen != "WhenNotNeeded")
                {
                    Main.LogDebug($"[MoveToFollowLanceNode] Detected enemy. No longer following player mech.");
                    return(new BehaviorTreeResults(BehaviorNodeState.Failure));
                }
                else
                {
                    Main.LogDebug($"[MoveToFollowLanceNode] Enemies detected but keeping tight formation still. Following player mech.");
                }
            }
            else
            {
                Main.LogDebug($"[MoveToFollowLanceNode] No enemies detected. Following player mech.");
            }


            List <AbstractActor> lanceMembers = AIUtil.GetLanceUnits(this.unit.Combat, this.unit.LanceId);
            float travelDistance = Mathf.Max(this.unit.MaxSprintDistance, this.unit.MaxWalkDistance);

            if (this.waitForLance)
            {
                for (int i = 0; i < lanceMembers.Count; i++)
                {
                    AbstractActor abstractActor = lanceMembers[i] as AbstractActor;

                    if (abstractActor != null)
                    {
                        float lanceMemberTravelDistance = Mathf.Max(abstractActor.MaxWalkDistance, abstractActor.MaxSprintDistance);
                        travelDistance = Mathf.Min(travelDistance, lanceMemberTravelDistance);
                    }
                }
            }

            AbstractActor targetActor = GetMechToFollow(targetLance);

            if (targetActor == null)
            {
                Main.Logger.LogError("[MoveToFollowLanceNode] Target Actor is null");
                return(new BehaviorTreeResults(BehaviorNodeState.Failure));
            }

            Main.LogDebug($"[MoveToFollowLanceNode] Target to follow is '{targetActor.DisplayName} {targetActor.VariantName}'");

            bool shouldSprint = this.tree.GetCustomBehaviorVariableValue(FOLLOW_LANCE_SHOULD_SPRINT_KEY).BoolVal;

            Main.LogDebug($"[MoveToFollowLanceNode] Should sprint by behaviour value being set? '{shouldSprint}'");

            shouldSprint = (!this.unit.HasAnyContactWithEnemy);

            Main.LogDebug($"[MoveToFollowLanceNode] Should sprint by contact with enemy? '{shouldSprint}'");

            shouldSprint = (this.unit.CurrentPosition - targetActor.CurrentPosition).magnitude > Main.Settings.AiSettings.FollowAiSettings.MaxDistanceFromTargetBeforeSprinting; // sprint if the unit is over 200 metres away
            Main.LogDebug($"[MoveToFollowLanceNode] Is the follow target further than 200m? Should sprint? '{shouldSprint}'");

            Vector3  lookDirection = (closestEnemy == null) ? targetActor.CurrentPosition : closestEnemy.CurrentPosition;
            MoveType moveType      = (shouldSprint) ? MoveType.Sprinting : MoveType.Walking;

            this.unit.Pathing.UpdateAIPath(targetActor.CurrentPosition, lookDirection, moveType);

            Vector3 vectorToTarget   = this.unit.Pathing.ResultDestination - this.unit.CurrentPosition;
            float   distanceToTarget = vectorToTarget.magnitude;

            if (distanceToTarget > travelDistance)
            {
                Main.LogDebug($"[MoveToFollowLanceNode] Can't reach follow target in one go so will go as far as I can");
                // If the target is out of range, head in the direction of that unit to the maximum possible travel distance for this turn
                vectorToTarget = vectorToTarget.normalized * travelDistance;
            }

            // Ensure the units aren't crowded
            Vector3 targetDestination = RoutingUtil.Decrowd(this.unit.CurrentPosition + vectorToTarget, this.unit);

            targetDestination = RegionUtil.MaybeClipMovementDestinationToStayInsideRegion(this.unit, targetDestination);

            float followLanceZoneRadius = this.unit.BehaviorTree.GetCustomBehaviorVariableValue(FOLLOW_LANCE_ZONE_RADIUS_KEY).FloatVal;

            Main.LogDebug($"[MoveToFollowLanceNode] My follow zone radius is '{followLanceZoneRadius}'");
            if (RoutingUtils.IsUnitInsideRadiusOfPoint(this.unit, targetActor.CurrentPosition, followLanceZoneRadius))
            {
                Main.LogDebug($"[MoveToFollowLanceNode] ...and I am inside that zone.");
                return(new BehaviorTreeResults(BehaviorNodeState.Failure));
            }
            else
            {
                Main.LogDebug($"[MoveToFollowLanceNode] ...and I am NOT inside that zone.");
            }

            this.unit.Pathing.UpdateAIPath(targetDestination, lookDirection, moveType);
            targetDestination = this.unit.Pathing.ResultDestination;
            float        maxCost             = this.unit.Pathing.MaxCost;
            PathNodeGrid currentGrid         = this.unit.Pathing.CurrentGrid;
            Vector3      targetActorPosition = targetActor.CurrentPosition;

            // This method seems to get called all the time - this is meant to be a last resort method I think. I wonder why the other AI pathfinding methods don't work?
            if ((currentGrid.GetValidPathNodeAt(targetDestination, maxCost) == null || (targetDestination - targetActor.CurrentPosition).magnitude > 1f) && this.unit.Combat.EncounterLayerData.inclineMeshData != null)
            {
                float maxSlope = Mathf.Tan(0.0174532924f * AIUtil.GetMaxSteepnessForAllLance(this.unit));
                List <AbstractActor> lanceUnits = AIUtil.GetLanceUnits(this.unit.Combat, this.unit.LanceId);
                targetDestination = this.unit.Combat.EncounterLayerData.inclineMeshData.GetDestination(this.unit.CurrentPosition, targetDestination, maxCost, maxSlope, this.unit, shouldSprint, lanceUnits, this.unit.Pathing.CurrentGrid, out targetActorPosition);
            }

            Vector3 currentPosition = this.unit.CurrentPosition;

            AIUtil.LogAI(string.Format("issuing order from [{0} {1} {2}] to [{3} {4} {5}] looking at [{6} {7} {8}]", new object[] {
                currentPosition.x,
                currentPosition.y,
                currentPosition.z,
                targetDestination.x,
                targetDestination.y,
                targetDestination.z,
                targetActorPosition.x,
                targetActorPosition.y,
                targetActorPosition.z
            }), "AI.DecisionMaking");

            // TODO: Factor in jump mechs
            return(new BehaviorTreeResults(BehaviorNodeState.Success)
            {
                orderInfo = new MovementOrderInfo(targetDestination, targetActorPosition)
                {
                    IsSprinting = shouldSprint
                },
                debugOrderString = string.Format("{0} moving toward destination: {1} dest: {2}", this.name, targetDestination, targetActor.CurrentPosition)
            });
        }
    override protected BehaviorTreeResults Tick()
    {
        BehaviorVariableValue variableValue = tree.GetBehaviorVariableValue(destinationBVarName);

        if (variableValue == null)
        {
            return(new BehaviorTreeResults(BehaviorNodeState.Failure));
        }

        string destinationGUID = variableValue.StringVal;

        RoutePointGameLogic destination = DestinationUtil.FindDestinationByGUID(tree, destinationGUID);

        if (destination == null)
        {
            return(new BehaviorTreeResults(BehaviorNodeState.Failure));
        }

        float sprintDistance = Mathf.Max(unit.MaxSprintDistance, unit.MaxWalkDistance);

        if (waitForLance)
        {
            for (int lanceMemberIndex = 0; lanceMemberIndex < unit.lance.unitGuids.Count; ++lanceMemberIndex)
            {
                ITaggedItem item = unit.Combat.ItemRegistry.GetItemByGUID(unit.lance.unitGuids[lanceMemberIndex]);
                if (item == null)
                {
                    continue;
                }
                AbstractActor lanceUnit = item as AbstractActor;
                if (lanceUnit == null)
                {
                    continue;
                }
                float unitMoveDistance = Mathf.Max(lanceUnit.MaxWalkDistance, lanceUnit.MaxSprintDistance);
                sprintDistance = Mathf.Min(sprintDistance, unitMoveDistance);
            }
        }

        MoveType moveType = tree.GetBehaviorVariableValue(BehaviorVariableName.Bool_RouteShouldSprint).BoolVal ?
                            MoveType.Sprinting : MoveType.Walking;

        unit.Pathing.UpdateAIPath(destination.Position, destination.Position, moveType);

        Vector3 offset = unit.Pathing.ResultDestination - unit.CurrentPosition;

        if (offset.magnitude > sprintDistance)
        {
            offset = offset.normalized * sprintDistance;
        }

        Vector3 destinationThisTurn = RoutingUtil.Decrowd(unit.CurrentPosition + offset, unit);

        destinationThisTurn = RegionUtil.MaybeClipMovementDestinationToStayInsideRegion(unit, destinationThisTurn);

        float destinationRadius = unit.BehaviorTree.GetBehaviorVariableValue(BehaviorVariableName.Float_RouteWaypointRadius).FloatVal;

        List <AbstractActor> unitsToWaitFor = new List <AbstractActor>();

        if (waitForLance)
        {
            if (unit.lance != null)
            {
                for (int lanceGUIDIndex = 0; lanceGUIDIndex < unit.lance.unitGuids.Count; ++lanceGUIDIndex)
                {
                    string      guid = unit.lance.unitGuids[lanceGUIDIndex];
                    ITaggedItem item = unit.Combat.ItemRegistry.GetItemByGUID(guid);
                    if (item != null)
                    {
                        AbstractActor lanceUnit = item as AbstractActor;
                        if (lanceUnit != null)
                        {
                            unitsToWaitFor.Add(lanceUnit);
                        }
                    }
                }
            }
            else
            {
                unitsToWaitFor.Add(unit);
            }
        }
        if (RoutingUtil.AllUnitsInsideRadiusOfPoint(unitsToWaitFor, destination.Position, destinationRadius))
        {
            tree.RemoveBehaviorVariableValue(destinationBVarName);
        }

        bool isSprinting = tree.GetBehaviorVariableValue(BehaviorVariableName.Bool_RouteShouldSprint).BoolVal;

        unit.Pathing.UpdateAIPath(destinationThisTurn, destination.Position, isSprinting ? MoveType.Sprinting : MoveType.Walking);
        destinationThisTurn = unit.Pathing.ResultDestination;

        float        movementBudget = unit.Pathing.MaxCost;
        PathNodeGrid grid           = unit.Pathing.CurrentGrid;
        Vector3      successorPoint = destination.Position;

        if ((grid.GetValidPathNodeAt(destinationThisTurn, movementBudget) == null) ||
            ((destinationThisTurn - destination.Position).magnitude > 1.0f))
        {
            // can't get all the way to the destination.
            if (unit.Combat.EncounterLayerData.inclineMeshData != null)
            {
                float maxSteepnessRatio         = Mathf.Tan(Mathf.Deg2Rad * AIUtil.GetMaxSteepnessForAllLance(unit));
                List <AbstractActor> lanceUnits = AIUtil.GetLanceUnits(unit.Combat, unit.LanceId);
                destinationThisTurn = unit.Combat.EncounterLayerData.inclineMeshData.GetDestination(
                    unit.CurrentPosition,
                    destinationThisTurn,
                    movementBudget,
                    maxSteepnessRatio,
                    unit,
                    isSprinting,
                    lanceUnits,
                    unit.Pathing.CurrentGrid,
                    out successorPoint);
            }
        }

        Vector3 cur = unit.CurrentPosition;

        AIUtil.LogAI(string.Format("issuing order from [{0} {1} {2}] to [{3} {4} {5}] looking at [{6} {7} {8}]",
                                   cur.x, cur.y, cur.z,
                                   destinationThisTurn.x, destinationThisTurn.y, destinationThisTurn.z,
                                   successorPoint.x, successorPoint.y, successorPoint.z
                                   ));

        BehaviorTreeResults results      = new BehaviorTreeResults(BehaviorNodeState.Success);
        MovementOrderInfo   mvtOrderInfo = new MovementOrderInfo(destinationThisTurn, successorPoint);

        mvtOrderInfo.IsSprinting = isSprinting;
        results.orderInfo        = mvtOrderInfo;
        results.debugOrderString = string.Format("{0} moving toward destination: {1} dest: {2}", this.name, destinationThisTurn, destination.Position);
        return(results);
    }