/// <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); }
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) }); }
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); }
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); }
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); }