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() { 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); }
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); }
override protected BehaviorTreeResults Tick() { BehaviorTreeResults results; if (unit.BehaviorTree.GetBehaviorVariableValue(BehaviorVariableName.Bool_RouteCompleted).BoolVal) { results = new BehaviorTreeResults(BehaviorNodeState.Success); results.orderInfo = new OrderInfo(OrderType.Brace); results.debugOrderString = string.Format("{0}: bracing for end of patrol route", this.name); return(results); } bool isSprinting = unit.BehaviorTree.GetBehaviorVariableValue(BehaviorVariableName.Bool_RouteShouldSprint).BoolVal; if (isSprinting && unit.CanSprint) { unit.Pathing.SetSprinting(); } else { unit.Pathing.SetWalking(); } PathNodeGrid grid = unit.Pathing.CurrentGrid; if (grid.UpdateBuild(25) > 0) { // have to wait for the grid to build. results = new BehaviorTreeResults(BehaviorNodeState.Running); return(results); } if (!unit.Pathing.ArePathGridsComplete) { // have to wait for the grid to build. results = new BehaviorTreeResults(BehaviorNodeState.Running); return(results); } float destinationRadius = unit.BehaviorTree.GetBehaviorVariableValue(BehaviorVariableName.Float_RouteWaypointRadius).FloatVal; RouteGameLogic myPatrolRoute = getRoute(); if (myPatrolRoute == null) { AIUtil.LogAI("Move Along Route failing because no route found", unit); return(new BehaviorTreeResults(BehaviorNodeState.Failure)); } BehaviorVariableValue nrpiVal = unit.BehaviorTree.GetBehaviorVariableValue(BehaviorVariableName.Int_RouteTargetPoint); int nextRoutePointIndex = (nrpiVal != null) ? nrpiVal.IntVal : 0; BehaviorVariableValue pfVal = unit.BehaviorTree.GetBehaviorVariableValue(BehaviorVariableName.Bool_RouteFollowingForward); bool patrollingForward = (pfVal != null) ? pfVal.BoolVal : true; PatrolRouteWaypoints routeWaypointIterator = null; switch (myPatrolRoute.routeTransitType) { case RouteTransitType.Circuit: routeWaypointIterator = new CircuitRouteWaypoints(nextRoutePointIndex, patrollingForward, myPatrolRoute.routePointList.Length); break; case RouteTransitType.OneWay: routeWaypointIterator = new OneWayRouteWaypoints(nextRoutePointIndex, patrollingForward, myPatrolRoute.routePointList.Length); break; case RouteTransitType.PingPong: routeWaypointIterator = new PingPongRouteWaypoints(nextRoutePointIndex, patrollingForward, myPatrolRoute.routePointList.Length); break; default: Debug.LogError("Invalid route transit type: " + myPatrolRoute.routeTransitType); AIUtil.LogAI("Move Along Route failing because patrol route was set to an invalid transit type: " + myPatrolRoute.routeTransitType, unit); return(new BehaviorTreeResults(BehaviorNodeState.Failure)); } float movementAvailable = unit.Pathing.MaxCost * unit.BehaviorTree.GetBehaviorVariableValue(BehaviorVariableName.Float_PatrolRouteThrottlePercentage).FloatVal / 100.0f; bool isComplete = false; int nextWaypoint = -1; bool nextPointGoesForward = false; Vector3 successorPoint; List <PathNode> availablePathNodes = unit.Pathing.CurrentGrid.GetSampledPathNodes(); // prune for region string regionGUID = RegionUtil.StayInsideRegionGUID(unit); if (!string.IsNullOrEmpty(regionGUID)) { availablePathNodes = availablePathNodes.FindAll(node => RegionUtil.PointInRegion(unit.Combat, node.Position, regionGUID)); } string guardGUID = unit.BehaviorTree.GetBehaviorVariableValue(BehaviorVariableName.String_GuardLanceGUID).StringVal; Lance guardLance = guardGUID != null?unit.Combat.ItemRegistry.GetItemByGUID <Lance>(guardGUID) : null; // if guarding units, adjust movement available to account for their speed if (guardLance != null) { movementAvailable = adjustMovementAvailableForGuardLance(unit, movementAvailable, guardLance); } // prune for distance from start point availablePathNodes = availablePathNodes.FindAll(node => node.CostToThisNode <= movementAvailable); // if there is a guarding lance, make sure that we're not moving out of the lance tether if (guardLance != null) { availablePathNodes = filterAvailablePathNodesForGuardTether(unit, availablePathNodes, guardLance); } Vector3 patrolPoint = getReachablePointOnRoute(unit.CurrentPosition, myPatrolRoute, routeWaypointIterator, availablePathNodes, out isComplete, out nextWaypoint, out nextPointGoesForward, out successorPoint); unit.BehaviorTree.unitBehaviorVariables.SetVariable(BehaviorVariableName.Bool_RouteFollowingForward, new BehaviorVariableValue(nextPointGoesForward)); unit.BehaviorTree.unitBehaviorVariables.SetVariable(BehaviorVariableName.Int_RouteTargetPoint, new BehaviorVariableValue(nextWaypoint)); unit.BehaviorTree.unitBehaviorVariables.SetVariable(BehaviorVariableName.Bool_RouteCompleted, new BehaviorVariableValue(isComplete)); //Vector3 destination = RegionUtil.MaybeClipMovementDestinationToStayInsideRegion(unit, patrolPoint); Vector3 destination = patrolPoint; if (!isComplete) { List <PathNode> path = constructPath(unit.Combat.HexGrid, destination, availablePathNodes); if ((path.Count == 0) || ((path.Count == 1) && (AIUtil.Get2DDistanceBetweenVector3s(path[0].Position, unit.CurrentPosition) < 1))) { // can't actually make progress - fail here, and presumably pass later on. AIUtil.LogAI("Move Along Route failing because no nodes in path.", unit); DialogueGameLogic proximityDialogue = unit.Combat.ItemRegistry.GetItemByGUID <DialogueGameLogic>(unit.Combat.Constants.CaptureEscortProximityDialogID); if (proximityDialogue != null) { TriggerDialog triggerDialogueMessage = new TriggerDialog(unit.GUID, unit.Combat.Constants.CaptureEscortProximityDialogID, async: false); unit.Combat.MessageCenter.PublishMessage(triggerDialogueMessage); } else { Debug.LogError("Could not find CaptureEscortProximityDialog. This is only a real error message if this is a Capture Escort (Normal Escort) mission. For other missions (Story, Ambush Convoy, etc) you can safely ignore this error message."); } return(new BehaviorTreeResults(BehaviorNodeState.Failure)); } destination = path[path.Count - 1].Position; } Vector3 cur = unit.CurrentPosition; if ((destination - cur).magnitude < 1) { // can't actually make progress - fail here, and presumably pass later on. AIUtil.LogAI("Move Along Route failing because destination too close to unit start.", unit); return(new BehaviorTreeResults(BehaviorNodeState.Failure)); } 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, destination.x, destination.y, destination.z, successorPoint.x, successorPoint.y, successorPoint.z ), unit); results = new BehaviorTreeResults(BehaviorNodeState.Success); MovementOrderInfo mvtOrderInfo = new MovementOrderInfo(destination, successorPoint); mvtOrderInfo.IsSprinting = isSprinting; results.orderInfo = mvtOrderInfo; results.debugOrderString = string.Format("{0}: dest:{1} sprint:{2}", this.name, destination, mvtOrderInfo.IsSprinting); return(results); }