public BehaviorVariableValue getBehaviourVariable(AbstractActor actor, BehaviorVariableName name) { try { List <BehaviorVariableScope> tags = getActorTags(actor); if (tags != null) { foreach (BehaviorVariableScope roleScope in tags) { BehaviorVariableValue roleValue = roleScope.GetVariableWithMood(name, actor.BehaviorTree.mood); if (roleValue != null) { if (Main.settings.debug) { Main.modLog.DebugMessage($"Hit for Var: {name.ToString()}, {actor.uid}({actor.Description.Id})"); } return(roleValue); } } if (Main.settings.debug) { Main.modLog.DebugMessage($"Miss for Var: {name.ToString()}, {actor.uid}({actor.Description.Id})"); } } } catch (Exception ex) { Main.modLog.LogException(ex); } return((BehaviorVariableValue)null); }
public static string StayInsideRegionGUID(AbstractActor unit) { BehaviorVariableValue variableValue = unit.BehaviorTree.GetBehaviorVariableValue(BehaviorVariableName.String_StayInsideRegionGUID); if ((variableValue == null) || (variableValue.StringVal.Length == 0)) { return(string.Empty); } return(variableValue.StringVal); }
static void logBVsForScope(string scopeName, BehaviorVariableScope scope) { logger.Log(string.Format(" ** Behavior variables on the {0} **", scopeName)); List <BehaviorVariableName> variables = scope.VariableNames; for (int bvIndex = 0; bvIndex < variables.Count; ++bvIndex) { BehaviorVariableValue bvv = scope.GetVariable(variables[bvIndex]); logger.Log(bvToString(variables[bvIndex], bvv)); } }
override protected BehaviorTreeResults Tick() { BehaviorVariableValue variableValue = tree.GetBehaviorVariableValue(BehaviorVariableName.String_RouteGUID); if (variableValue == null) { Debug.Log("No behavior variable for patrol route GUID found"); return(new BehaviorTreeResults(BehaviorNodeState.Failure)); } string patrolRouteGUID = variableValue.StringVal; RouteGameLogic route = RoutingUtil.FindRouteByGUID(tree, patrolRouteGUID); if (route == null) { Debug.Log("No route matching GUID found"); return(new BehaviorTreeResults(BehaviorNodeState.Failure)); } if (unit.lance == null) { Debug.Log("No lance for this unit found"); return(new BehaviorTreeResults(BehaviorNodeState.Failure)); } BehaviorVariableValue closestPointBV = tree.GetBehaviorVariableValue(BehaviorVariableName.Bool_RouteStartAtClosestPoint); BehaviorVariableValue forwardBV = tree.GetBehaviorVariableValue(BehaviorVariableName.Bool_RouteFollowingForward); int routeTargetPoint = 0; if (closestPointBV.BoolVal) { routeTargetPoint = closestPointOnRouteToLance(route, unit.lance); } else { if (forwardBV.BoolVal) { routeTargetPoint = 0; } else { routeTargetPoint = route.routePointList.Length - 1; } } unit.lance.BehaviorVariables.SetVariable(BehaviorVariableName.Int_RouteTargetPoint, new BehaviorVariableValue(routeTargetPoint)); unit.lance.BehaviorVariables.SetVariable(BehaviorVariableName.Bool_RouteStarted, new BehaviorVariableValue(true)); unit.lance.BehaviorVariables.SetVariable(BehaviorVariableName.Bool_RouteCompleted, new BehaviorVariableValue(false)); unit.lance.BehaviorVariables.SetVariable(BehaviorVariableName.Bool_RouteFollowingForward, new BehaviorVariableValue(forwardBV.BoolVal)); return(new BehaviorTreeResults(BehaviorNodeState.Success)); }
RouteGameLogic getRoute() { BehaviorVariableValue variableValue = tree.GetBehaviorVariableValue(BehaviorVariableName.String_RouteGUID); if (variableValue == null) { // no route AIUtil.LogAI("No behavior variable for route GUID found"); return(null); } string routeGUID = variableValue.StringVal; return(RoutingUtil.FindRouteByGUID(tree, routeGUID)); }
// TODO: Support global scopes [pilot personality, unit role, ai skill] on a later release // NOTE: Only supporting unit and lance to start with public BehaviorVariableValue GetBehaviourVariableValue(string type, string ownerGuid, string key) { if (this.customBehaviourVariables.ContainsKey(type)) { Dictionary <string, CustomBehaviorVariableScope> typeScopes = this.customBehaviourVariables[type]; if (typeScopes.ContainsKey(ownerGuid)) { CustomBehaviorVariableScope customScope = typeScopes[ownerGuid]; BehaviorVariableValue behaviourVariableValue = customScope.GetVariable(key); if (behaviourVariableValue != null) { return(behaviourVariableValue); } } } return(null); }
public BehaviorVariableValue GetVariableWithMood(string key, AIMood mood) { if (this.ScopesByMood.ContainsKey(mood)) { BehaviorVariableValue variable = this.ScopesByMood[mood].GetVariable(key); if (variable != null) { return(variable); } } if (this.behaviorVariables.ContainsKey(key)) { return(this.behaviorVariables[key]); } return(null); }
public static bool HasFollowLanceTarget(AbstractActor unit) { if (unit == null) { return(false); } BehaviorVariableValue value = AiManager.Instance.GetBehaviourVariableValue(unit, MoveToFollowLanceNode.FOLLOW_LANCE_TARGET_GUID_KEY); if (value != null) { string lanceGuid = value.StringVal; Lance lance = UnityGameInstance.BattleTechGame.Combat.ItemRegistry.GetItemByGUID(lanceGuid) as Lance; Main.Logger.Log($"[HasFollowLanceTarget] Unit '{unit.DisplayName}' has a follow target lance of guid '{lanceGuid}' and name '{lance.DisplayName}'"); return(true); } return(false); }
override protected BehaviorTreeResults Tick() { if (unit.lance == null) { Debug.Log("No lance for this unit found"); return(new BehaviorTreeResults(BehaviorNodeState.Failure)); } BehaviorVariableValue startedBV = unit.lance.BehaviorVariables.GetVariable(BehaviorVariableName.Bool_RouteStarted); if ((startedBV != null) && (startedBV.BoolVal)) { return(new BehaviorTreeResults(BehaviorNodeState.Success)); } else { return(new BehaviorTreeResults(BehaviorNodeState.Failure)); } }
public static void Postfix(BehaviorTree __instance, ref BehaviorVariableValue __result, BehaviorVariableName name) { try { if (name == BehaviorVariableName.Bool_ReserveEnabled) { Logger.Debug("[BehaviorTree_GetBehaviorVariableValue_POSTFIX] Overriding BehaviorVariableName.Bool_ReserveEnabled: true"); __result.BoolVal = true; } else if (name == BehaviorVariableName.Float_ReserveBasePercentage) { Logger.Debug("[BehaviorTree_GetBehaviorVariableValue_POSTFIX] Overriding BehaviorVariableName.Float_ReserveBasePercentage: " + LittleThings.Settings.EnableAIReserveBasePercentage); __result.FloatVal = LittleThings.Settings.EnableAIReserveBasePercentage; } } catch (Exception e) { Logger.Error(e); } }
override protected BehaviorTreeResults Tick() { BehaviorVariableValue variableValue = tree.GetBehaviorVariableValue(BehaviorVariableName.String_LancePostAttackDestinationGUID); 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)); } return(new BehaviorTreeResults(BehaviorNodeState.Success)); }
override protected BehaviorTreeResults Tick() { BehaviorVariableValue variableValue = tree.GetBehaviorVariableValue(BehaviorVariableName.String_RouteGUID); if (variableValue == null) { // No route, this is normal. return(new BehaviorTreeResults(BehaviorNodeState.Failure)); } string patrolRouteGUID = variableValue.StringVal; RouteGameLogic route = RoutingUtil.FindRouteByGUID(tree, patrolRouteGUID); if (route == null) { return(new BehaviorTreeResults(BehaviorNodeState.Failure)); } return(new BehaviorTreeResults(BehaviorNodeState.Success)); }
// TODO: Need to cover vanilla deserialisation for `CustomBehaviorVariableScope` type before this will work // TODO: Patch a call into this for the unit to save the data into the save file, or output as a companion save file public void Hydrate(SerializableReferenceContainer references) { Dictionary <string, BehaviorVariableValue> itemDictionary = references.GetItemDictionary <string, BehaviorVariableValue>(this, "serializableCustomBehaviorVariables"); Dictionary <int, CustomBehaviorVariableScope> itemDictionary2 = references.GetItemDictionary <int, CustomBehaviorVariableScope>(this, "serializableCustomScopesByMood"); this.behaviorVariables = new Dictionary <string, BehaviorVariableValue>(itemDictionary.Count); foreach (KeyValuePair <string, BehaviorVariableValue> keyValuePair in itemDictionary) { string key = (string)keyValuePair.Key; BehaviorVariableValue value = keyValuePair.Value; this.behaviorVariables.Add(key, value); } this.ScopesByMood = new Dictionary <AIMood, CustomBehaviorVariableScope>(itemDictionary2.Count); foreach (KeyValuePair <int, CustomBehaviorVariableScope> keyValuePair2 in itemDictionary2) { AIMood key2 = (AIMood)keyValuePair2.Key; CustomBehaviorVariableScope value2 = keyValuePair2.Value; this.ScopesByMood.Add(key2, value2); } }
// TODO: Need to cover vanilla serialisation for `CustomBehaviorVariableScope` type before this will work // TODO: Patch a call into this for the unit to load the data from the save file, or output as a companion save file public void Dehydrate(SerializableReferenceContainer references) { Dictionary <string, BehaviorVariableValue> dictionary = new Dictionary <string, BehaviorVariableValue>(this.behaviorVariables.Count); Dictionary <int, CustomBehaviorVariableScope> dictionary2 = new Dictionary <int, CustomBehaviorVariableScope>(this.ScopesByMood.Count); foreach (KeyValuePair <string, BehaviorVariableValue> keyValuePair in this.behaviorVariables) { string key = (string)keyValuePair.Key; BehaviorVariableValue value = keyValuePair.Value; dictionary.Add(key, value); } foreach (KeyValuePair <AIMood, CustomBehaviorVariableScope> keyValuePair2 in this.ScopesByMood) { int key2 = (int)keyValuePair2.Key; CustomBehaviorVariableScope value2 = keyValuePair2.Value; dictionary2.Add(key2, value2); } references.AddItemDictionary <string, BehaviorVariableValue>(this, "serializableCustomBehaviorVariables", dictionary); references.AddItemDictionary <int, CustomBehaviorVariableScope>(this, "serializableCustomScopesByMood", dictionary2); }
public BehaviorVariableValue GetBehaviourVariableValue(AbstractActor unit, string key) { BehaviorVariableValue behaviourVariableValue = GetBehaviourVariableValue("UNIT", unit.GUID, key); if (behaviourVariableValue != null) { return(behaviourVariableValue); } behaviourVariableValue = GetBehaviourVariableValue("LANCE", unit.lance.GUID, key); if (behaviourVariableValue != null) { return(behaviourVariableValue); } behaviourVariableValue = GetBehaviourVariableValue("TEAM", unit.team.GUID, key); if (behaviourVariableValue != null) { return(behaviourVariableValue); } return(null); }
static string bvToString(BehaviorVariableName name, BehaviorVariableValue bvv) { string nameString = name.ToString(); switch (bvv.type) { case BehaviorVariableValue.BehaviorVariableType.Bool: return(string.Format("{0}: {1}", nameString, bvv.BoolVal)); case BehaviorVariableValue.BehaviorVariableType.String: return(string.Format("{0}: {1}", nameString, bvv.StringVal)); case BehaviorVariableValue.BehaviorVariableType.Int: return(string.Format("{0}: {1}", nameString, bvv.IntVal)); case BehaviorVariableValue.BehaviorVariableType.Float: return(string.Format("{0}: {1}", nameString, bvv.FloatVal)); default: Debug.LogAssertion("unknown behavior variable type" + bvv.type); return("???"); } }
// TODO: EVERYTHING SHOULD CONVERT TO CACHED CALL IF POSSIBLE public static BehaviorVariableValue GetBehaviorVariableValue(BehaviorTree bTree, BehaviorVariableName name) { BehaviorVariableValue bhVarVal = null; if (ModState.RolePlayerBehaviorVarManager != null && ModState.RolePlayerGetBehaviorVar != null) { // Ask RolePlayer for the variable //getBehaviourVariable(AbstractActor actor, BehaviorVariableName name) Mod.Log.Trace?.Write($"Pulling BehaviorVariableValue from RolePlayer for unit: {bTree.unit.DistinctId()}."); bhVarVal = (BehaviorVariableValue)ModState.RolePlayerGetBehaviorVar.Invoke(ModState.RolePlayerBehaviorVarManager, new object[] { bTree.unit, name }); } if (bhVarVal == null) { // RolePlayer does not return the vanilla value if there's no configuration for the actor. We need to check that we're null here to trap that edge case. // Also, if RolePlayer isn't configured we need to read the value. Mod.Log.Trace?.Write($"Pulling BehaviorVariableValue from Vanilla for unit: {bTree.unit.DistinctId()}."); bhVarVal = GetBehaviorVariableValueDirectly(bTree, name); } Mod.Log.Trace?.Write($" Value is: {bhVarVal}"); return(bhVarVal); }
public static Vector3 Decrowd(Vector3 target, AbstractActor unit) { float myRadius = unit.BehaviorTree.GetBehaviorVariableValue(BehaviorVariableName.Float_PersonalSpaceRadius).FloatVal; List <ITaggedItem> units = unit.BehaviorTree.battleTechGame.Combat.ItemRegistry.WithType(TaggedObjectType.Unit).Search(); float testOffsetRadius = 0.0f; float testOffsetAngle = 0.0f; while (testOffsetRadius < 10.0f * myRadius) { Vector3 candidatePoint = new Vector3(target.x + Mathf.Cos(testOffsetAngle) * testOffsetRadius, target.y, target.z + Mathf.Sin(testOffsetAngle) * testOffsetRadius); bool anyViolatedConstraints = false; for (int unitIndex = 0; unitIndex < units.Count; ++unitIndex) { AbstractActor otherUnit = units[unitIndex] as AbstractActor; if (otherUnit == unit) { continue; } float otherRadius = 0.0f; if (otherUnit.BehaviorTree != null) { BehaviorVariableValue otherRadiusValue = otherUnit.BehaviorTree.GetBehaviorVariableValue(BehaviorVariableName.Float_PersonalSpaceRadius); if (otherRadiusValue != null) { otherRadius = otherRadiusValue.FloatVal; } } float desiredRadius = myRadius + otherRadius; Vector3 constraintVector = candidatePoint - otherUnit.CurrentPosition; float constraintVectorMag = constraintVector.magnitude; if (constraintVectorMag < desiredRadius) { anyViolatedConstraints = true; break; } } if (!anyViolatedConstraints) { return(candidatePoint); } float newWrap = testOffsetAngle * testOffsetRadius + myRadius; if (newWrap > Mathf.PI * 2 * testOffsetRadius) { testOffsetRadius += myRadius; } if (testOffsetRadius > 0.0f) { testOffsetAngle = newWrap / testOffsetRadius; testOffsetAngle = testOffsetAngle % (Mathf.PI * 2); } } // TODO: do something fancier here. return(target); }
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() { 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); }
public void SetVariable(string key, BehaviorVariableValue value) { this.behaviorVariables[key] = value; }
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); }
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) }); }
private static BehaviorVariableValue GetBehaviorVariableValueDirectly(BehaviorTree bTree, BehaviorVariableName name) { BehaviorVariableValue behaviorVariableValue = bTree.unitBehaviorVariables.GetVariable(name); if (behaviorVariableValue != null) { return(behaviorVariableValue); } Pilot pilot = bTree.unit.GetPilot(); if (pilot != null) { BehaviorVariableScope scopeForAIPersonality = bTree.unit.Combat.BattleTechGame.BehaviorVariableScopeManager.GetScopeForAIPersonality(pilot.pilotDef.AIPersonality); if (scopeForAIPersonality != null) { behaviorVariableValue = scopeForAIPersonality.GetVariableWithMood(name, bTree.unit.BehaviorTree.mood); if (behaviorVariableValue != null) { return(behaviorVariableValue); } } } if (bTree.unit.lance != null) { behaviorVariableValue = bTree.unit.lance.BehaviorVariables.GetVariable(name); if (behaviorVariableValue != null) { return(behaviorVariableValue); } } if (bTree.unit.team != null) { Traverse bvT = Traverse.Create(bTree.unit.team).Field("BehaviorVariables"); BehaviorVariableScope bvs = bvT.GetValue <BehaviorVariableScope>(); behaviorVariableValue = bvs.GetVariable(name); if (behaviorVariableValue != null) { return(behaviorVariableValue); } } UnitRole unitRole = bTree.unit.DynamicUnitRole; if (unitRole == UnitRole.Undefined) { unitRole = bTree.unit.StaticUnitRole; } BehaviorVariableScope scopeForRole = bTree.unit.Combat.BattleTechGame.BehaviorVariableScopeManager.GetScopeForRole(unitRole); if (scopeForRole != null) { behaviorVariableValue = scopeForRole.GetVariableWithMood(name, bTree.unit.BehaviorTree.mood); if (behaviorVariableValue != null) { return(behaviorVariableValue); } } if (bTree.unit.CanMoveAfterShooting) { BehaviorVariableScope scopeForAISkill = bTree.unit.Combat.BattleTechGame.BehaviorVariableScopeManager.GetScopeForAISkill(AISkillID.Reckless); if (scopeForAISkill != null) { behaviorVariableValue = scopeForAISkill.GetVariableWithMood(name, bTree.unit.BehaviorTree.mood); if (behaviorVariableValue != null) { return(behaviorVariableValue); } } } behaviorVariableValue = bTree.unit.Combat.BattleTechGame.BehaviorVariableScopeManager.GetGlobalScope().GetVariableWithMood(name, bTree.unit.BehaviorTree.mood); if (behaviorVariableValue != null) { return(behaviorVariableValue); } return(DefaultBehaviorVariableValue.GetSingleton()); }
public static bool Prefix(BehaviorTree __instance, BehaviorVariableName name, ref BehaviorVariableValue __result) { var value = BehaviorVariableManager.Instance.getBehaviourVariable(__instance.unit, name); if (value == null) { return(true); } __result = value; return(false); }
public static bool Prefix(BehaviorTree __instance, BehaviorVariableName name, ref BehaviorVariableValue __result) { var value = BehaviorVariableOverride.TryOverrideValue(__instance, name); if (value == null) { return(true); } __result = value; return(false); }